@compass-labs/widgets 0.1.39 → 0.1.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ var apiSdk = require('@compass-labs/api-sdk');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var chains = require('viem/chains');
8
8
  var lucideReact = require('lucide-react');
9
+ var viem = require('viem');
9
10
 
10
11
  // src/provider/CompassProvider.tsx
11
12
  var ApiContext = react.createContext(null);
@@ -92,7 +93,10 @@ var disconnectedWallet = {
92
93
  },
93
94
  switchChain: null,
94
95
  login: null,
95
- logout: null
96
+ logout: null,
97
+ fundWallet: null,
98
+ hasExternalWallet: true,
99
+ sendTransaction: null
96
100
  };
97
101
  function WalletProvider({ children, wallet }) {
98
102
  const value = wallet ? {
@@ -102,7 +106,10 @@ function WalletProvider({ children, wallet }) {
102
106
  signTypedData: wallet.signTypedData,
103
107
  switchChain: wallet.switchChain ?? null,
104
108
  login: wallet.login ?? null,
105
- logout: wallet.logout ?? null
109
+ logout: wallet.logout ?? null,
110
+ fundWallet: wallet.fundWallet ?? null,
111
+ hasExternalWallet: wallet.hasExternalWallet ?? true,
112
+ sendTransaction: wallet.sendTransaction ?? null
106
113
  } : disconnectedWallet;
107
114
  return /* @__PURE__ */ jsxRuntime.jsx(WalletContext.Provider, { value, children });
108
115
  }
@@ -1271,25 +1278,29 @@ function WalletStatus({
1271
1278
  );
1272
1279
  }
1273
1280
  function ActionModal({ isOpen, onClose, title, children }) {
1281
+ const modalRef = react.useRef(null);
1274
1282
  react.useEffect(() => {
1275
1283
  const handleEscape = (e) => {
1276
1284
  if (e.key === "Escape") onClose();
1277
1285
  };
1278
1286
  if (isOpen) {
1279
1287
  document.addEventListener("keydown", handleEscape);
1280
- document.body.style.overflow = "hidden";
1281
1288
  }
1282
1289
  return () => {
1283
1290
  document.removeEventListener("keydown", handleEscape);
1284
- document.body.style.overflow = "";
1285
1291
  };
1286
1292
  }, [isOpen, onClose]);
1293
+ const handleOverlayWheel = react.useCallback((e) => {
1294
+ if (modalRef.current && !modalRef.current.contains(e.target)) {
1295
+ window.scrollBy(0, e.deltaY);
1296
+ }
1297
+ }, []);
1287
1298
  if (!isOpen) return null;
1288
1299
  return /* @__PURE__ */ jsxRuntime.jsxs(
1289
1300
  "div",
1290
1301
  {
1291
1302
  className: "fixed inset-0 z-50 flex items-center justify-center p-4",
1292
- style: { overflowY: "auto", scrollbarWidth: "none" },
1303
+ onWheel: handleOverlayWheel,
1293
1304
  children: [
1294
1305
  /* @__PURE__ */ jsxRuntime.jsx(
1295
1306
  "div",
@@ -1302,21 +1313,27 @@ function ActionModal({ isOpen, onClose, title, children }) {
1302
1313
  /* @__PURE__ */ jsxRuntime.jsxs(
1303
1314
  "div",
1304
1315
  {
1316
+ ref: modalRef,
1305
1317
  className: "relative w-full max-w-md",
1306
1318
  style: {
1307
1319
  backgroundColor: "var(--compass-color-surface)",
1308
1320
  boxShadow: "var(--compass-shadow-lg)",
1309
1321
  borderRadius: "var(--compass-border-radius-xl)",
1310
- fontFamily: "var(--compass-font-family)"
1322
+ fontFamily: "var(--compass-font-family)",
1323
+ maxHeight: "85vh",
1324
+ overflowY: "auto",
1325
+ overscrollBehavior: "contain",
1326
+ scrollbarWidth: "none"
1311
1327
  },
1312
1328
  children: [
1313
1329
  /* @__PURE__ */ jsxRuntime.jsxs(
1314
1330
  "div",
1315
1331
  {
1316
- className: "flex items-center justify-between border-b",
1332
+ className: "flex items-center justify-between border-b sticky top-0 z-10",
1317
1333
  style: {
1318
1334
  borderColor: "var(--compass-color-border)",
1319
- padding: "calc(var(--compass-spacing-card) * 0.75) var(--compass-spacing-card)"
1335
+ padding: "calc(var(--compass-spacing-card) * 0.75) var(--compass-spacing-card)",
1336
+ backgroundColor: "var(--compass-color-surface)"
1320
1337
  },
1321
1338
  children: [
1322
1339
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2573,114 +2590,731 @@ function CreditAccountGuard({
2573
2590
  }
2574
2591
  );
2575
2592
  }
2576
- function AccountBalancesModal({
2577
- isOpen,
2578
- onClose,
2579
- balances,
2580
- totalUsdValue,
2581
- isLoading = false,
2582
- earnAccountAddress,
2583
- walletAddress
2584
- }) {
2585
- return /* @__PURE__ */ jsxRuntime.jsx(ActionModal, { isOpen, onClose, title: "Balance Breakdown", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
2586
- (walletAddress || earnAccountAddress) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
2587
- walletAddress && /* @__PURE__ */ jsxRuntime.jsx(CopyableAddress, { address: walletAddress, label: "Wallet" }),
2588
- earnAccountAddress && /* @__PURE__ */ jsxRuntime.jsx(CopyableAddress, { address: earnAccountAddress, label: "Product Account" })
2589
- ] }),
2590
- isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-4", children: /* @__PURE__ */ jsxRuntime.jsx(
2591
- lucideReact.Loader2,
2592
- {
2593
- size: 24,
2594
- className: "animate-spin",
2595
- style: { color: "var(--compass-color-primary)" }
2596
- }
2597
- ) }) : balances.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
2598
- "div",
2593
+ var EVM_CHAIN_IDS = {
2594
+ ethereum: 1,
2595
+ base: 8453,
2596
+ arbitrum: 42161
2597
+ };
2598
+ function BuyForm({ targetAddress, defaultAsset = "USDC", onComplete }) {
2599
+ const { fundWallet } = useEmbeddableWallet();
2600
+ const { chainId } = useChain();
2601
+ const [amount, setAmount] = react.useState("");
2602
+ const [state, setState] = react.useState("idle");
2603
+ const [error, setError] = react.useState(null);
2604
+ const numericChainId = EVM_CHAIN_IDS[chainId] || 8453;
2605
+ const handleBuy = react.useCallback(async () => {
2606
+ if (!fundWallet || !amount || parseFloat(amount) <= 0) {
2607
+ return;
2608
+ }
2609
+ setState("buying");
2610
+ setError(null);
2611
+ try {
2612
+ await fundWallet({
2613
+ address: targetAddress,
2614
+ chainId: numericChainId,
2615
+ asset: defaultAsset,
2616
+ amount
2617
+ });
2618
+ setState("success");
2619
+ onComplete?.();
2620
+ setTimeout(() => setState("idle"), 3e3);
2621
+ } catch (err) {
2622
+ setState("error");
2623
+ setError(err instanceof Error ? err.message : "Purchase failed. Please try again.");
2624
+ }
2625
+ }, [fundWallet, amount, targetAddress, numericChainId, defaultAsset, onComplete]);
2626
+ const chainName = chainId.charAt(0).toUpperCase() + chainId.slice(1);
2627
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
2628
+ /* @__PURE__ */ jsxRuntime.jsxs(
2629
+ "p",
2599
2630
  {
2600
- className: "text-center py-4",
2601
- style: { color: "var(--compass-color-text-tertiary)" },
2602
- children: "No tokens in account"
2631
+ className: "text-sm font-medium",
2632
+ style: { color: "var(--compass-color-text)", fontFamily: "var(--compass-font-family)" },
2633
+ children: [
2634
+ "Buy ",
2635
+ defaultAsset,
2636
+ " on ",
2637
+ chainName
2638
+ ]
2603
2639
  }
2604
- ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2605
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
2640
+ ),
2641
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2642
+ /* @__PURE__ */ jsxRuntime.jsx(
2643
+ "label",
2644
+ {
2645
+ className: "block text-sm mb-1.5",
2646
+ style: { color: "var(--compass-color-text-secondary)", fontFamily: "var(--compass-font-family)" },
2647
+ children: "Amount (USD)"
2648
+ }
2649
+ ),
2650
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2606
2651
  /* @__PURE__ */ jsxRuntime.jsx(
2607
2652
  "span",
2608
2653
  {
2609
- className: "text-xs font-medium uppercase tracking-wide",
2654
+ className: "absolute left-3 top-1/2 -translate-y-1/2 text-sm",
2610
2655
  style: { color: "var(--compass-color-text-tertiary)" },
2611
- children: "Available in Account"
2656
+ children: "$"
2612
2657
  }
2613
2658
  ),
2614
2659
  /* @__PURE__ */ jsxRuntime.jsx(
2615
- "div",
2660
+ "input",
2616
2661
  {
2617
- className: "flex flex-col gap-2",
2618
- style: {
2619
- maxHeight: "50vh",
2620
- overflowY: "auto",
2621
- scrollbarWidth: "none"
2662
+ type: "number",
2663
+ value: amount,
2664
+ onChange: (e) => {
2665
+ setAmount(e.target.value);
2666
+ if (state === "error") {
2667
+ setState("idle");
2668
+ setError(null);
2669
+ }
2622
2670
  },
2623
- children: balances.filter((token) => parseFloat(token.balance) >= 5e-3).map((token) => /* @__PURE__ */ jsxRuntime.jsxs(
2624
- "div",
2671
+ disabled: state === "buying",
2672
+ placeholder: "0.00",
2673
+ className: "w-full border px-3 py-2.5 pl-7 text-sm focus:outline-none disabled:opacity-50",
2674
+ style: {
2675
+ backgroundColor: "var(--compass-color-surface)",
2676
+ borderColor: "var(--compass-color-border)",
2677
+ borderRadius: "var(--compass-border-radius-lg)",
2678
+ color: "var(--compass-color-text)",
2679
+ fontFamily: "var(--compass-font-family)"
2680
+ }
2681
+ }
2682
+ )
2683
+ ] })
2684
+ ] }),
2685
+ /* @__PURE__ */ jsxRuntime.jsx(
2686
+ "p",
2687
+ {
2688
+ className: "text-xs",
2689
+ style: { color: "var(--compass-color-text-tertiary)", fontFamily: "var(--compass-font-family)" },
2690
+ children: "Buy crypto with card or bank transfer. Funds are sent directly to your account."
2691
+ }
2692
+ ),
2693
+ state === "success" && /* @__PURE__ */ jsxRuntime.jsxs(
2694
+ "div",
2695
+ {
2696
+ className: "flex items-center gap-2 p-3 text-sm",
2697
+ style: {
2698
+ backgroundColor: "var(--compass-color-success-muted, rgba(34,197,94,0.1))",
2699
+ color: "var(--compass-color-success)",
2700
+ borderRadius: "var(--compass-border-radius-lg)",
2701
+ fontFamily: "var(--compass-font-family)"
2702
+ },
2703
+ children: [
2704
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { size: 16 }),
2705
+ "Purchase initiated. Funds may take a few minutes to arrive."
2706
+ ]
2707
+ }
2708
+ ),
2709
+ state === "error" && error && /* @__PURE__ */ jsxRuntime.jsxs(
2710
+ "div",
2711
+ {
2712
+ className: "flex items-center gap-2 p-3 text-sm",
2713
+ style: {
2714
+ backgroundColor: "var(--compass-color-error-muted, rgba(239,68,68,0.1))",
2715
+ color: "var(--compass-color-error)",
2716
+ borderRadius: "var(--compass-border-radius-lg)",
2717
+ fontFamily: "var(--compass-font-family)"
2718
+ },
2719
+ children: [
2720
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { size: 16 }),
2721
+ error
2722
+ ]
2723
+ }
2724
+ ),
2725
+ /* @__PURE__ */ jsxRuntime.jsx(
2726
+ "button",
2727
+ {
2728
+ onClick: handleBuy,
2729
+ disabled: state === "buying" || !amount || parseFloat(amount) <= 0,
2730
+ className: "w-full py-3 px-4 text-sm font-medium flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
2731
+ style: {
2732
+ backgroundColor: "var(--compass-color-primary)",
2733
+ color: "white",
2734
+ borderRadius: "var(--compass-border-radius-xl)",
2735
+ fontFamily: "var(--compass-font-family)",
2736
+ transition: "var(--compass-transition-normal)"
2737
+ },
2738
+ children: state === "buying" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2739
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
2740
+ "Processing..."
2741
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2742
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CreditCard, { className: "h-4 w-4" }),
2743
+ "Buy with card / bank"
2744
+ ] })
2745
+ }
2746
+ )
2747
+ ] });
2748
+ }
2749
+ var BLOCK_EXPLORERS = {
2750
+ ethereum: "https://etherscan.io",
2751
+ base: "https://basescan.org",
2752
+ arbitrum: "https://arbiscan.io"
2753
+ };
2754
+ function TxStatus({ state }) {
2755
+ const { chainId } = useChain();
2756
+ if (state.status === "idle") return null;
2757
+ const explorer = BLOCK_EXPLORERS[chainId] || BLOCK_EXPLORERS.ethereum;
2758
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2759
+ "div",
2760
+ {
2761
+ className: "mt-4 p-4 border",
2762
+ style: {
2763
+ borderColor: "var(--compass-color-border)",
2764
+ borderRadius: "var(--compass-border-radius-xl)",
2765
+ fontFamily: "var(--compass-font-family)"
2766
+ },
2767
+ children: [
2768
+ state.status === "preparing" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
2769
+ /* @__PURE__ */ jsxRuntime.jsx(
2770
+ lucideReact.Loader2,
2771
+ {
2772
+ className: "h-4 w-4 animate-spin",
2773
+ style: { color: "var(--compass-color-primary)" }
2774
+ }
2775
+ ),
2776
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-secondary)" }, children: "Preparing transaction..." })
2777
+ ] }),
2778
+ state.status === "signing" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
2779
+ /* @__PURE__ */ jsxRuntime.jsx(
2780
+ lucideReact.Loader2,
2781
+ {
2782
+ className: "h-4 w-4 animate-spin",
2783
+ style: { color: "var(--compass-color-warning)" }
2784
+ }
2785
+ ),
2786
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-secondary)" }, children: "Waiting for signature..." })
2787
+ ] }),
2788
+ state.status === "broadcasting" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
2789
+ /* @__PURE__ */ jsxRuntime.jsx(
2790
+ lucideReact.Loader2,
2791
+ {
2792
+ className: "h-4 w-4 animate-spin",
2793
+ style: { color: "var(--compass-color-primary)" }
2794
+ }
2795
+ ),
2796
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-secondary)" }, children: "Broadcasting transaction..." })
2797
+ ] }),
2798
+ state.status === "submitted" && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2799
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2800
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
2801
+ /* @__PURE__ */ jsxRuntime.jsx(
2802
+ lucideReact.Loader2,
2803
+ {
2804
+ className: "h-4 w-4 animate-spin",
2805
+ style: { color: "var(--compass-color-primary)" }
2806
+ }
2807
+ ),
2808
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text)" }, children: "Transaction submitted" })
2809
+ ] }),
2810
+ /* @__PURE__ */ jsxRuntime.jsxs(
2811
+ "a",
2625
2812
  {
2626
- className: "flex items-center justify-between p-3 rounded-lg",
2627
- style: {
2628
- backgroundColor: "var(--compass-color-surface)",
2629
- border: "1px solid var(--compass-color-border)",
2630
- flexShrink: 0
2631
- },
2813
+ href: `${explorer}/tx/${state.txHash}`,
2814
+ target: "_blank",
2815
+ rel: "noopener noreferrer",
2816
+ className: "flex items-center gap-1.5 text-xs hover:opacity-70 transition-opacity",
2817
+ style: { color: "var(--compass-color-text-secondary)" },
2632
2818
  children: [
2633
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", style: { color: "var(--compass-color-text)" }, children: token.symbol }),
2634
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-end", children: [
2635
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono font-medium", style: { color: "var(--compass-color-text)" }, children: formatAmount(token.balance) }),
2636
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: formatUSD(token.usdValue) })
2637
- ] })
2819
+ "View on explorer",
2820
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3" })
2638
2821
  ]
2639
- },
2640
- token.symbol
2641
- ))
2642
- }
2643
- )
2644
- ] }),
2645
- /* @__PURE__ */ jsxRuntime.jsxs(
2646
- "div",
2647
- {
2648
- className: "flex items-center justify-between pt-3 mt-2",
2649
- style: { borderTop: "2px solid var(--compass-color-border)" },
2650
- children: [
2651
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", style: { color: "var(--compass-color-text)" }, children: "Total" }),
2822
+ }
2823
+ )
2824
+ ] }),
2825
+ /* @__PURE__ */ jsxRuntime.jsx(
2826
+ "p",
2827
+ {
2828
+ className: "text-xs mt-2",
2829
+ style: { color: "var(--compass-color-text-tertiary)" },
2830
+ children: "Waiting for on-chain confirmation. Balances will update automatically."
2831
+ }
2832
+ )
2833
+ ] }),
2834
+ state.status === "confirmed" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2835
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
2652
2836
  /* @__PURE__ */ jsxRuntime.jsx(
2653
- "span",
2837
+ lucideReact.CheckCircle,
2654
2838
  {
2655
- className: "font-bold text-xl",
2656
- style: { color: "var(--compass-color-text)" },
2657
- children: formatUSD(totalUsdValue)
2839
+ className: "h-4 w-4",
2840
+ style: { color: "var(--compass-color-success)" }
2658
2841
  }
2659
- )
2660
- ]
2661
- }
2662
- )
2663
- ] })
2664
- ] }) });
2665
- }
2666
- var TRANSFER_TOKEN = "USDC";
2667
- var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2668
- compact = false,
2669
- hideVisual = false,
2842
+ ),
2843
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-success)" }, children: "Transaction confirmed" })
2844
+ ] }),
2845
+ /* @__PURE__ */ jsxRuntime.jsxs(
2846
+ "a",
2847
+ {
2848
+ href: `${explorer}/tx/${state.txHash}`,
2849
+ target: "_blank",
2850
+ rel: "noopener noreferrer",
2851
+ className: "flex items-center gap-1.5 text-xs hover:opacity-70 transition-opacity",
2852
+ style: { color: "var(--compass-color-text-secondary)" },
2853
+ children: [
2854
+ "View on explorer",
2855
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3" })
2856
+ ]
2857
+ }
2858
+ )
2859
+ ] }),
2860
+ state.status === "failed" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 text-sm", children: [
2861
+ /* @__PURE__ */ jsxRuntime.jsx(
2862
+ lucideReact.XCircle,
2863
+ {
2864
+ className: "h-4 w-4 mt-0.5 shrink-0",
2865
+ style: { color: "var(--compass-color-error)" }
2866
+ }
2867
+ ),
2868
+ /* @__PURE__ */ jsxRuntime.jsx(
2869
+ "span",
2870
+ {
2871
+ className: "break-all",
2872
+ style: { color: "var(--compass-color-error)" },
2873
+ children: state.error
2874
+ }
2875
+ )
2876
+ ] })
2877
+ ]
2878
+ }
2879
+ );
2880
+ }
2881
+ function useTxPolling(options = {}) {
2882
+ const { chainId } = useChain();
2883
+ const queryClient = reactQuery.useQueryClient();
2884
+ const pollIntervalRef = react.useRef(null);
2885
+ const clearPolling = react.useCallback(() => {
2886
+ if (pollIntervalRef.current) {
2887
+ clearInterval(pollIntervalRef.current);
2888
+ pollIntervalRef.current = null;
2889
+ }
2890
+ }, []);
2891
+ react.useEffect(() => {
2892
+ return () => clearPolling();
2893
+ }, [clearPolling]);
2894
+ const startPolling = react.useCallback(
2895
+ (txHash, setTxState) => {
2896
+ clearPolling();
2897
+ let polls = 0;
2898
+ pollIntervalRef.current = setInterval(async () => {
2899
+ polls++;
2900
+ for (const key of options.queryKeysToInvalidate ?? []) {
2901
+ queryClient.invalidateQueries({ queryKey: key });
2902
+ }
2903
+ try {
2904
+ const res = await fetch(
2905
+ `/api/compass/tx/receipt?hash=${txHash}&chain=${chainId}`
2906
+ );
2907
+ const data = await res.json();
2908
+ if (data.status === "success") {
2909
+ clearPolling();
2910
+ setTxState({ status: "confirmed", txHash });
2911
+ return;
2912
+ } else if (data.status === "reverted") {
2913
+ clearPolling();
2914
+ setTxState({ status: "failed", error: "Transaction reverted" });
2915
+ return;
2916
+ }
2917
+ } catch (err) {
2918
+ if (err instanceof TypeError && err.message.includes("fetch")) ; else {
2919
+ console.warn("[useTxPolling] Unexpected error polling tx receipt:", err);
2920
+ }
2921
+ }
2922
+ if (polls >= 24) {
2923
+ clearPolling();
2924
+ setTxState({ status: "failed", error: "Unable to confirm transaction. Please check a block explorer to verify." });
2925
+ }
2926
+ }, 5e3);
2927
+ },
2928
+ [chainId, clearPolling, queryClient, options.queryKeysToInvalidate]
2929
+ );
2930
+ return { startPolling, clearPolling };
2931
+ }
2932
+ var USDC_ADDRESSES = {
2933
+ ethereum: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
2934
+ base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
2935
+ arbitrum: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"
2936
+ };
2937
+ var ERC20_TRANSFER_ABI = [
2938
+ {
2939
+ type: "function",
2940
+ name: "transfer",
2941
+ inputs: [
2942
+ { name: "to", type: "address" },
2943
+ { name: "amount", type: "uint256" }
2944
+ ],
2945
+ outputs: [{ name: "", type: "bool" }],
2946
+ stateMutability: "nonpayable"
2947
+ }
2948
+ ];
2949
+ async function waitForReceipt(txHash, chainId) {
2950
+ const maxPolls = 60;
2951
+ for (let i = 0; i < maxPolls; i++) {
2952
+ await new Promise((resolve) => setTimeout(resolve, 3e3));
2953
+ try {
2954
+ const res = await fetch(`/api/compass/tx/receipt?hash=${txHash}&chain=${chainId}`);
2955
+ const data = await res.json();
2956
+ if (data.status === "success") return;
2957
+ if (data.status === "reverted") throw new Error("Withdrawal transaction reverted");
2958
+ } catch (err) {
2959
+ if (err instanceof Error && err.message.includes("reverted")) throw err;
2960
+ }
2961
+ }
2962
+ throw new Error("Unable to confirm withdrawal. Please check a block explorer to verify.");
2963
+ }
2964
+ function SendForm({ onComplete, product = "earn", productAccountAddress }) {
2965
+ const { sendTransaction, signTypedData, address } = useEmbeddableWallet();
2966
+ const { chainId } = useChain();
2967
+ const { earnAccountAddress, isDeployed } = useEarnAccount();
2968
+ const [recipient, setRecipient] = react.useState("");
2969
+ const [amount, setAmount] = react.useState("");
2970
+ const [txState, setTxState] = react.useState({ status: "idle" });
2971
+ const { startPolling } = useTxPolling({
2972
+ queryKeysToInvalidate: product === "credit" ? [["creditBalances"], ["creditPositions"], ["eoaBalances"]] : [["earnAccountBalances"], ["eoaBalances"]]
2973
+ });
2974
+ const { data: earnBalanceData } = reactQuery.useQuery({
2975
+ queryKey: ["earnAccountBalances", chainId, address],
2976
+ queryFn: async () => {
2977
+ if (!address) return null;
2978
+ const response = await fetch(
2979
+ `/api/compass/earn-account/balances?owner=${address}&chain=${chainId}`
2980
+ );
2981
+ if (!response.ok) return null;
2982
+ return response.json();
2983
+ },
2984
+ enabled: !!address && isDeployed && product === "earn",
2985
+ staleTime: 30 * 1e3
2986
+ });
2987
+ const { data: creditBalanceData } = reactQuery.useQuery({
2988
+ queryKey: ["creditBalances", productAccountAddress, chainId],
2989
+ queryFn: async () => {
2990
+ if (!productAccountAddress) return null;
2991
+ const response = await fetch(
2992
+ `/api/compass/credit/balances?owner=${productAccountAddress}&chain=${chainId}`
2993
+ );
2994
+ if (!response.ok) return null;
2995
+ return response.json();
2996
+ },
2997
+ enabled: !!productAccountAddress && product === "credit",
2998
+ staleTime: 30 * 1e3
2999
+ });
3000
+ const availableBalance = product === "credit" ? creditBalanceData?.find((b) => b.tokenSymbol === "USDC")?.amount || "0" : earnBalanceData?.balances?.["USDC"]?.balance || "0";
3001
+ const tokenAddress = USDC_ADDRESSES[chainId];
3002
+ const isValidAddress = recipient.length > 0 && viem.isAddress(recipient);
3003
+ const isValidAmount = amount.length > 0 && parseFloat(amount) > 0;
3004
+ const exceedsBalance = isValidAmount && parseFloat(amount) > parseFloat(availableBalance);
3005
+ const isBusy = txState.status !== "idle" && txState.status !== "confirmed" && txState.status !== "failed";
3006
+ const canSend = isValidAddress && isValidAmount && !exceedsBalance && !!sendTransaction && !!signTypedData && !isBusy;
3007
+ const handleSend = react.useCallback(async () => {
3008
+ if (!sendTransaction || !signTypedData || !isValidAddress || !isValidAmount || !tokenAddress) return;
3009
+ try {
3010
+ setTxState({ status: "preparing" });
3011
+ const prepareRes = await fetch("/api/compass/transfer/prepare", {
3012
+ method: "POST",
3013
+ headers: { "Content-Type": "application/json" },
3014
+ body: JSON.stringify({
3015
+ owner: address,
3016
+ chain: chainId,
3017
+ token: "USDC",
3018
+ amount,
3019
+ action: "WITHDRAW",
3020
+ ...product === "credit" ? { product: "credit" } : {}
3021
+ })
3022
+ });
3023
+ if (!prepareRes.ok) {
3024
+ const errData = await prepareRes.json();
3025
+ throw new Error(errData.error || "Failed to prepare withdrawal");
3026
+ }
3027
+ const prepareData = await prepareRes.json();
3028
+ setTxState({ status: "signing" });
3029
+ const withdrawSig = await signTypedData({
3030
+ domain: prepareData.domain,
3031
+ types: prepareData.normalizedTypes,
3032
+ primaryType: prepareData.primaryType,
3033
+ message: prepareData.message
3034
+ });
3035
+ setTxState({ status: "broadcasting" });
3036
+ const executeRes = await fetch("/api/compass/transfer/execute", {
3037
+ method: "POST",
3038
+ headers: { "Content-Type": "application/json" },
3039
+ body: JSON.stringify({
3040
+ owner: address,
3041
+ chain: chainId,
3042
+ eip712: prepareData.eip712,
3043
+ signature: withdrawSig,
3044
+ ...product === "credit" ? { product: "credit" } : {}
3045
+ })
3046
+ });
3047
+ if (!executeRes.ok) {
3048
+ const errData = await executeRes.json();
3049
+ throw new Error(errData.error || "Withdrawal failed");
3050
+ }
3051
+ const { txHash: withdrawHash } = await executeRes.json();
3052
+ await waitForReceipt(withdrawHash, chainId);
3053
+ setTxState({ status: "signing" });
3054
+ const data = viem.encodeFunctionData({
3055
+ abi: ERC20_TRANSFER_ABI,
3056
+ functionName: "transfer",
3057
+ args: [recipient, viem.parseUnits(amount, 6)]
3058
+ });
3059
+ const sendHash = await sendTransaction({ to: tokenAddress, data });
3060
+ setTxState({ status: "submitted", txHash: sendHash });
3061
+ startPolling(sendHash, setTxState);
3062
+ setAmount("");
3063
+ setRecipient("");
3064
+ onComplete?.();
3065
+ } catch (err) {
3066
+ setTxState({ status: "failed", error: err instanceof Error ? err.message : "Transaction failed" });
3067
+ }
3068
+ }, [sendTransaction, signTypedData, recipient, amount, address, chainId, tokenAddress, isValidAddress, isValidAmount, onComplete, startPolling, product]);
3069
+ const chainName = chainId.charAt(0).toUpperCase() + chainId.slice(1);
3070
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
3071
+ /* @__PURE__ */ jsxRuntime.jsxs(
3072
+ "p",
3073
+ {
3074
+ className: "text-sm font-medium",
3075
+ style: { color: "var(--compass-color-text)", fontFamily: "var(--compass-font-family)" },
3076
+ children: [
3077
+ "Send USDC on ",
3078
+ chainName
3079
+ ]
3080
+ }
3081
+ ),
3082
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3083
+ /* @__PURE__ */ jsxRuntime.jsx(
3084
+ "label",
3085
+ {
3086
+ className: "block text-sm mb-1.5",
3087
+ style: { color: "var(--compass-color-text-secondary)", fontFamily: "var(--compass-font-family)" },
3088
+ children: "Recipient Address"
3089
+ }
3090
+ ),
3091
+ /* @__PURE__ */ jsxRuntime.jsx(
3092
+ "input",
3093
+ {
3094
+ type: "text",
3095
+ value: recipient,
3096
+ onChange: (e) => {
3097
+ setRecipient(e.target.value);
3098
+ if (txState.status === "failed") setTxState({ status: "idle" });
3099
+ },
3100
+ disabled: isBusy,
3101
+ placeholder: "0x...",
3102
+ className: "w-full border px-3 py-2.5 text-sm focus:outline-none disabled:opacity-50 font-mono",
3103
+ style: {
3104
+ backgroundColor: "var(--compass-color-surface)",
3105
+ borderColor: recipient && !isValidAddress ? "var(--compass-color-error)" : "var(--compass-color-border)",
3106
+ borderRadius: "var(--compass-border-radius-lg)",
3107
+ color: "var(--compass-color-text)",
3108
+ fontFamily: "var(--compass-font-family)"
3109
+ }
3110
+ }
3111
+ ),
3112
+ recipient && !isValidAddress && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs mt-1", style: { color: "var(--compass-color-error)" }, children: "Invalid Ethereum address" }),
3113
+ isValidAddress && recipient.toLowerCase() === address?.toLowerCase() && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs mt-1", style: { color: "var(--compass-color-warning, #f59e0b)" }, children: "This is your own wallet address" }),
3114
+ isValidAddress && earnAccountAddress && recipient.toLowerCase() === earnAccountAddress.toLowerCase() && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs mt-1", style: { color: "var(--compass-color-warning, #f59e0b)" }, children: "This is your savings account address" })
3115
+ ] }),
3116
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3117
+ /* @__PURE__ */ jsxRuntime.jsx(
3118
+ "label",
3119
+ {
3120
+ className: "block text-sm mb-1.5",
3121
+ style: { color: "var(--compass-color-text-secondary)", fontFamily: "var(--compass-font-family)" },
3122
+ children: "Amount"
3123
+ }
3124
+ ),
3125
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
3126
+ /* @__PURE__ */ jsxRuntime.jsx(
3127
+ "input",
3128
+ {
3129
+ type: "number",
3130
+ value: amount,
3131
+ onChange: (e) => {
3132
+ setAmount(e.target.value);
3133
+ if (txState.status === "failed") setTxState({ status: "idle" });
3134
+ },
3135
+ disabled: isBusy,
3136
+ placeholder: "0.00",
3137
+ className: "w-full border px-3 py-2.5 pr-14 text-sm focus:outline-none disabled:opacity-50",
3138
+ style: {
3139
+ backgroundColor: "var(--compass-color-surface)",
3140
+ borderColor: "var(--compass-color-border)",
3141
+ borderRadius: "var(--compass-border-radius-lg)",
3142
+ color: "var(--compass-color-text)",
3143
+ fontFamily: "var(--compass-font-family)"
3144
+ }
3145
+ }
3146
+ ),
3147
+ /* @__PURE__ */ jsxRuntime.jsx(
3148
+ "span",
3149
+ {
3150
+ className: "absolute right-3 top-1/2 -translate-y-1/2 text-xs",
3151
+ style: { color: "var(--compass-color-text-tertiary)" },
3152
+ children: "USDC"
3153
+ }
3154
+ )
3155
+ ] }),
3156
+ /* @__PURE__ */ jsxRuntime.jsxs(
3157
+ "button",
3158
+ {
3159
+ onClick: () => setAmount(availableBalance),
3160
+ className: "text-xs mt-1",
3161
+ style: { color: "var(--compass-color-primary)", cursor: "pointer", background: "none", border: "none", padding: 0 },
3162
+ children: [
3163
+ "Available: ",
3164
+ parseFloat(availableBalance).toFixed(2),
3165
+ " USDC"
3166
+ ]
3167
+ }
3168
+ ),
3169
+ exceedsBalance && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs mt-1", style: { color: "var(--compass-color-error)" }, children: "Amount exceeds available balance" })
3170
+ ] }),
3171
+ /* @__PURE__ */ jsxRuntime.jsx(
3172
+ "p",
3173
+ {
3174
+ className: "text-xs",
3175
+ style: { color: "var(--compass-color-text-tertiary)", fontFamily: "var(--compass-font-family)" },
3176
+ children: "Send USDC from your account to any address. Requires 2 signatures. Gas fees are sponsored."
3177
+ }
3178
+ ),
3179
+ /* @__PURE__ */ jsxRuntime.jsx(TxStatus, { state: txState }),
3180
+ /* @__PURE__ */ jsxRuntime.jsx(
3181
+ "button",
3182
+ {
3183
+ onClick: handleSend,
3184
+ disabled: !canSend,
3185
+ className: "w-full py-3 px-4 text-sm font-medium flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
3186
+ style: {
3187
+ backgroundColor: "var(--compass-color-primary)",
3188
+ color: "white",
3189
+ borderRadius: "var(--compass-border-radius-xl)",
3190
+ fontFamily: "var(--compass-font-family)",
3191
+ transition: "var(--compass-transition-normal)"
3192
+ },
3193
+ children: isBusy ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3194
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
3195
+ "Sending..."
3196
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3197
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-4 w-4" }),
3198
+ "Send USDC"
3199
+ ] })
3200
+ }
3201
+ )
3202
+ ] });
3203
+ }
3204
+ function AccountBalancesModal({
3205
+ isOpen,
3206
+ onClose,
3207
+ balances,
3208
+ totalUsdValue,
3209
+ isLoading = false,
3210
+ earnAccountAddress,
3211
+ walletAddress
3212
+ }) {
3213
+ return /* @__PURE__ */ jsxRuntime.jsx(ActionModal, { isOpen, onClose, title: "Balance Breakdown", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
3214
+ (walletAddress || earnAccountAddress) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
3215
+ walletAddress && /* @__PURE__ */ jsxRuntime.jsx(CopyableAddress, { address: walletAddress, label: "Wallet" }),
3216
+ earnAccountAddress && /* @__PURE__ */ jsxRuntime.jsx(CopyableAddress, { address: earnAccountAddress, label: "Product Account" })
3217
+ ] }),
3218
+ isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-4", children: /* @__PURE__ */ jsxRuntime.jsx(
3219
+ lucideReact.Loader2,
3220
+ {
3221
+ size: 24,
3222
+ className: "animate-spin",
3223
+ style: { color: "var(--compass-color-primary)" }
3224
+ }
3225
+ ) }) : balances.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
3226
+ "div",
3227
+ {
3228
+ className: "text-center py-4",
3229
+ style: { color: "var(--compass-color-text-tertiary)" },
3230
+ children: "No tokens in account"
3231
+ }
3232
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3233
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
3234
+ /* @__PURE__ */ jsxRuntime.jsx(
3235
+ "span",
3236
+ {
3237
+ className: "text-xs font-medium uppercase tracking-wide",
3238
+ style: { color: "var(--compass-color-text-tertiary)" },
3239
+ children: "Available in Account"
3240
+ }
3241
+ ),
3242
+ /* @__PURE__ */ jsxRuntime.jsx(
3243
+ "div",
3244
+ {
3245
+ className: "flex flex-col gap-2",
3246
+ style: {
3247
+ maxHeight: "50vh",
3248
+ overflowY: "auto",
3249
+ scrollbarWidth: "none"
3250
+ },
3251
+ children: balances.filter((token) => parseFloat(token.balance) >= 5e-3).map((token) => /* @__PURE__ */ jsxRuntime.jsxs(
3252
+ "div",
3253
+ {
3254
+ className: "flex items-center justify-between p-3 rounded-lg",
3255
+ style: {
3256
+ backgroundColor: "var(--compass-color-surface)",
3257
+ border: "1px solid var(--compass-color-border)",
3258
+ flexShrink: 0
3259
+ },
3260
+ children: [
3261
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", style: { color: "var(--compass-color-text)" }, children: token.symbol }),
3262
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-end", children: [
3263
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono font-medium", style: { color: "var(--compass-color-text)" }, children: formatAmount(token.balance) }),
3264
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: formatUSD(token.usdValue) })
3265
+ ] })
3266
+ ]
3267
+ },
3268
+ token.symbol
3269
+ ))
3270
+ }
3271
+ )
3272
+ ] }),
3273
+ /* @__PURE__ */ jsxRuntime.jsxs(
3274
+ "div",
3275
+ {
3276
+ className: "flex items-center justify-between pt-3 mt-2",
3277
+ style: { borderTop: "2px solid var(--compass-color-border)" },
3278
+ children: [
3279
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", style: { color: "var(--compass-color-text)" }, children: "Total" }),
3280
+ /* @__PURE__ */ jsxRuntime.jsx(
3281
+ "span",
3282
+ {
3283
+ className: "font-bold text-xl",
3284
+ style: { color: "var(--compass-color-text)" },
3285
+ children: formatUSD(totalUsdValue)
3286
+ }
3287
+ )
3288
+ ]
3289
+ }
3290
+ )
3291
+ ] })
3292
+ ] }) });
3293
+ }
3294
+ var TRANSFER_TOKEN = "USDC";
3295
+ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
3296
+ compact = false,
3297
+ hideVisual = false,
2670
3298
  onTransferComplete
2671
3299
  }, ref) {
3300
+ const { address, isConnected, signTypedData, switchChain, walletChainId, fundWallet, hasExternalWallet, sendTransaction } = useEmbeddableWallet();
3301
+ const { chainId, chain } = useChain();
3302
+ const { earnAccountAddress, isDeployed } = useEarnAccount();
3303
+ const queryClient = reactQuery.useQueryClient();
3304
+ const showBuyTab = !!fundWallet;
3305
+ const showSendTab = !!sendTransaction && !hasExternalWallet;
3306
+ const buyOnly = !hasExternalWallet && showBuyTab && !sendTransaction;
2672
3307
  const [isModalOpen, setIsModalOpen] = react.useState(false);
2673
- const [activeAction, setActiveAction] = react.useState("deposit");
3308
+ const [activeAction, setActiveAction] = react.useState(
3309
+ !hasExternalWallet && fundWallet ? "buy" : "deposit"
3310
+ );
2674
3311
  const selectedToken = TRANSFER_TOKEN;
2675
3312
  const [amount, setAmount] = react.useState("");
2676
- const [transferState, setTransferState] = react.useState("idle");
2677
- const [statusMessage, setStatusMessage] = react.useState("");
2678
- const [error, setError] = react.useState(null);
3313
+ const [txState, setTxState] = react.useState({ status: "idle" });
2679
3314
  const [isBalancesModalOpen, setIsBalancesModalOpen] = react.useState(false);
2680
- const { address, isConnected, signTypedData, switchChain, walletChainId } = useEmbeddableWallet();
2681
- const { chainId, chain } = useChain();
2682
- const { earnAccountAddress, isDeployed } = useEarnAccount();
2683
- const queryClient = reactQuery.useQueryClient();
3315
+ const { startPolling, clearPolling } = useTxPolling({
3316
+ queryKeysToInvalidate: [["earnAccountBalances"], ["eoaBalances"]]
3317
+ });
2684
3318
  const { data: balanceData, isLoading: balancesLoading } = reactQuery.useQuery({
2685
3319
  queryKey: ["earnAccountBalances", chainId, address],
2686
3320
  queryFn: async () => {
@@ -2712,7 +3346,7 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2712
3346
  }
2713
3347
  return {};
2714
3348
  },
2715
- enabled: !!address && activeAction === "deposit",
3349
+ enabled: !!address && hasExternalWallet && activeAction === "deposit",
2716
3350
  staleTime: 30 * 1e3
2717
3351
  });
2718
3352
  const earnBalances = balanceData?.balances || {};
@@ -2731,10 +3365,9 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2731
3365
  }
2732
3366
  const resetForm = react.useCallback(() => {
2733
3367
  setAmount("");
2734
- setTransferState("idle");
2735
- setStatusMessage("");
2736
- setError(null);
2737
- }, []);
3368
+ setTxState({ status: "idle" });
3369
+ clearPolling();
3370
+ }, [clearPolling]);
2738
3371
  const handleOpenModal = () => {
2739
3372
  resetForm();
2740
3373
  setIsModalOpen(true);
@@ -2749,7 +3382,8 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2749
3382
  const handleActionChange = (action) => {
2750
3383
  setActiveAction(action);
2751
3384
  setAmount("");
2752
- setError(null);
3385
+ setTxState({ status: "idle" });
3386
+ clearPolling();
2753
3387
  };
2754
3388
  const getMaxBalance = () => {
2755
3389
  if (activeAction === "deposit") {
@@ -2759,30 +3393,26 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2759
3393
  };
2760
3394
  const handleTransfer = react.useCallback(async () => {
2761
3395
  if (!address || !amount || !signTypedData) return;
2762
- setError(null);
3396
+ setTxState({ status: "preparing" });
2763
3397
  try {
2764
3398
  const targetChainId = chain.viemChain.id;
2765
3399
  if (walletChainId !== void 0 && walletChainId !== targetChainId) {
2766
3400
  if (!switchChain) {
2767
3401
  throw new Error(`Please switch your wallet to ${chain.name} (chain ID ${targetChainId}) to continue. Your wallet is currently on chain ID ${walletChainId}.`);
2768
3402
  }
2769
- setStatusMessage(`Switching network to ${chain.name}...`);
2770
3403
  try {
2771
3404
  await switchChain(targetChainId);
2772
- } catch (switchError) {
3405
+ } catch {
2773
3406
  throw new Error(`Please switch your wallet to ${chain.name} to continue. Your wallet is on chain ID ${walletChainId}, but ${chain.name} requires chain ID ${targetChainId}.`);
2774
3407
  }
2775
3408
  } else if (switchChain && walletChainId === void 0) {
2776
- setStatusMessage("Switching network...");
2777
3409
  try {
2778
3410
  await switchChain(targetChainId);
2779
- } catch (switchError) {
3411
+ } catch {
2780
3412
  throw new Error(`Please switch your wallet to ${chain.name} to continue`);
2781
3413
  }
2782
3414
  }
2783
3415
  if (activeAction === "deposit") {
2784
- setTransferState("checking_approval");
2785
- setStatusMessage("Checking token approval...");
2786
3416
  const approveResponse = await fetch("/api/compass/transfer/approve", {
2787
3417
  method: "POST",
2788
3418
  headers: { "Content-Type": "application/json" },
@@ -2801,16 +3431,14 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2801
3431
  if (approvalData.requiresTransaction) {
2802
3432
  throw new Error("This token requires a transaction-based approval. Please approve manually.");
2803
3433
  }
2804
- setTransferState("awaiting_approval_signature");
2805
- setStatusMessage("Please sign the approval...");
3434
+ setTxState({ status: "signing" });
2806
3435
  const approvalSignature = await signTypedData({
2807
3436
  domain: approvalData.domain,
2808
3437
  types: approvalData.normalizedTypes,
2809
3438
  primaryType: "Permit",
2810
3439
  message: approvalData.message
2811
3440
  });
2812
- setTransferState("approving");
2813
- setStatusMessage("Executing approval...");
3441
+ setTxState({ status: "broadcasting" });
2814
3442
  const executeApprovalResponse = await fetch("/api/compass/transfer/execute", {
2815
3443
  method: "POST",
2816
3444
  headers: { "Content-Type": "application/json" },
@@ -2827,8 +3455,7 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2827
3455
  }
2828
3456
  }
2829
3457
  }
2830
- setTransferState("awaiting_transfer_signature");
2831
- setStatusMessage("Preparing transfer...");
3458
+ setTxState({ status: "signing" });
2832
3459
  const prepareResponse = await fetch("/api/compass/transfer/prepare", {
2833
3460
  method: "POST",
2834
3461
  headers: { "Content-Type": "application/json" },
@@ -2845,15 +3472,13 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2845
3472
  throw new Error(errData.error || "Failed to prepare transfer");
2846
3473
  }
2847
3474
  const prepareData = await prepareResponse.json();
2848
- setStatusMessage("Please sign the transfer...");
2849
3475
  const transferSignature = await signTypedData({
2850
3476
  domain: prepareData.domain,
2851
3477
  types: prepareData.normalizedTypes,
2852
3478
  primaryType: prepareData.primaryType,
2853
3479
  message: prepareData.message
2854
3480
  });
2855
- setTransferState("transferring");
2856
- setStatusMessage("Executing transfer...");
3481
+ setTxState({ status: "broadcasting" });
2857
3482
  const executeResponse = await fetch("/api/compass/transfer/execute", {
2858
3483
  method: "POST",
2859
3484
  headers: { "Content-Type": "application/json" },
@@ -2869,17 +3494,12 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2869
3494
  throw new Error(errData.error || "Transfer failed");
2870
3495
  }
2871
3496
  const { txHash } = await executeResponse.json();
2872
- setTransferState("success");
2873
- setStatusMessage("Transfer successful!");
3497
+ setTxState({ status: "submitted", txHash });
3498
+ startPolling(txHash, setTxState);
2874
3499
  onTransferComplete?.(activeAction, selectedToken, amount, txHash);
2875
- queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
2876
- queryClient.invalidateQueries({ queryKey: ["eoaBalances"] });
2877
- setTimeout(() => {
2878
- resetForm();
2879
- }, 2e3);
3500
+ setAmount("");
2880
3501
  } catch (err) {
2881
- setTransferState("error");
2882
- setError(err instanceof Error ? err.message : "Transfer failed");
3502
+ setTxState({ status: "failed", error: err instanceof Error ? err.message : "Transfer failed" });
2883
3503
  }
2884
3504
  }, [
2885
3505
  address,
@@ -2891,14 +3511,13 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2891
3511
  walletChainId,
2892
3512
  signTypedData,
2893
3513
  switchChain,
2894
- queryClient,
2895
3514
  onTransferComplete,
2896
- resetForm
3515
+ startPolling
2897
3516
  ]);
2898
3517
  if (!isConnected) {
2899
3518
  return null;
2900
3519
  }
2901
- const isProcessing = transferState !== "idle" && transferState !== "success" && transferState !== "error";
3520
+ const isProcessing = txState.status !== "idle" && txState.status !== "confirmed" && txState.status !== "failed";
2902
3521
  if (!isDeployed && !hideVisual) {
2903
3522
  return /* @__PURE__ */ jsxRuntime.jsxs(
2904
3523
  "div",
@@ -2982,9 +3601,9 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2982
3601
  {
2983
3602
  isOpen: isModalOpen,
2984
3603
  onClose: handleCloseModal,
2985
- title: "Transfer Funds",
3604
+ title: showSendTab ? "Manage Funds" : buyOnly ? "Add Funds" : "Transfer Funds",
2986
3605
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
2987
- /* @__PURE__ */ jsxRuntime.jsx(
3606
+ buyOnly ? null : /* @__PURE__ */ jsxRuntime.jsx(
2988
3607
  "div",
2989
3608
  {
2990
3609
  className: "flex gap-1 p-1",
@@ -2993,7 +3612,9 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2993
3612
  borderRadius: "var(--compass-border-radius-lg)",
2994
3613
  fontFamily: "var(--compass-font-family)"
2995
3614
  },
2996
- children: ["deposit", "withdraw"].map((action) => /* @__PURE__ */ jsxRuntime.jsxs(
3615
+ children: [
3616
+ ...showSendTab ? ["buy", "send"] : ["deposit", "withdraw", ...showBuyTab ? ["buy"] : []]
3617
+ ].map((action) => /* @__PURE__ */ jsxRuntime.jsxs(
2997
3618
  "button",
2998
3619
  {
2999
3620
  onClick: () => handleActionChange(action),
@@ -3005,7 +3626,7 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
3005
3626
  borderRadius: "var(--compass-border-radius-md)"
3006
3627
  },
3007
3628
  children: [
3008
- action === "deposit" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownLeft, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpRight, { size: 14 }),
3629
+ action === "deposit" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownLeft, { size: 14 }) : action === "withdraw" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpRight, { size: 14 }) : action === "send" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { size: 14 }),
3009
3630
  action
3010
3631
  ]
3011
3632
  },
@@ -3013,120 +3634,116 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
3013
3634
  ))
3014
3635
  }
3015
3636
  ),
3016
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3017
- /* @__PURE__ */ jsxRuntime.jsx(
3018
- "label",
3019
- {
3020
- className: "text-sm font-medium mb-1 block",
3021
- style: { color: "var(--compass-color-text-secondary)" },
3022
- children: "Amount"
3637
+ activeAction === "send" ? /* @__PURE__ */ jsxRuntime.jsx(
3638
+ SendForm,
3639
+ {
3640
+ onComplete: () => {
3641
+ queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
3642
+ queryClient.invalidateQueries({ queryKey: ["eoaBalances"] });
3023
3643
  }
3024
- ),
3025
- /* @__PURE__ */ jsxRuntime.jsxs(
3026
- "div",
3644
+ }
3645
+ ) : activeAction === "buy" || buyOnly ? /* @__PURE__ */ jsxRuntime.jsx(
3646
+ BuyForm,
3647
+ {
3648
+ targetAddress: earnAccountAddress || "",
3649
+ onComplete: () => {
3650
+ queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
3651
+ queryClient.invalidateQueries({ queryKey: ["eoaBalances"] });
3652
+ }
3653
+ }
3654
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3655
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3656
+ /* @__PURE__ */ jsxRuntime.jsx(
3657
+ "label",
3658
+ {
3659
+ className: "text-sm font-medium mb-1 block",
3660
+ style: { color: "var(--compass-color-text-secondary)" },
3661
+ children: "Amount"
3662
+ }
3663
+ ),
3664
+ /* @__PURE__ */ jsxRuntime.jsxs(
3665
+ "div",
3666
+ {
3667
+ className: "flex items-center gap-2 p-3 border",
3668
+ style: {
3669
+ backgroundColor: "var(--compass-color-background)",
3670
+ borderColor: "var(--compass-color-border)",
3671
+ borderRadius: "var(--compass-border-radius-lg)"
3672
+ },
3673
+ children: [
3674
+ /* @__PURE__ */ jsxRuntime.jsx(
3675
+ "input",
3676
+ {
3677
+ type: "number",
3678
+ value: amount,
3679
+ onChange: (e) => {
3680
+ setAmount(e.target.value);
3681
+ if (txState.status === "failed") setTxState({ status: "idle" });
3682
+ },
3683
+ placeholder: "0.00",
3684
+ disabled: isProcessing,
3685
+ className: "flex-1 bg-transparent outline-none text-lg font-mono",
3686
+ style: { color: "var(--compass-color-text)" }
3687
+ }
3688
+ ),
3689
+ /* @__PURE__ */ jsxRuntime.jsx(
3690
+ "span",
3691
+ {
3692
+ className: "text-sm font-medium",
3693
+ style: { color: "var(--compass-color-text-secondary)" },
3694
+ children: "USDC"
3695
+ }
3696
+ )
3697
+ ]
3698
+ }
3699
+ ),
3700
+ /* @__PURE__ */ jsxRuntime.jsxs(
3701
+ "button",
3702
+ {
3703
+ onClick: () => setAmount(formatAmount(getMaxBalance())),
3704
+ className: "text-xs mt-1",
3705
+ style: { color: "var(--compass-color-primary)", cursor: "pointer", background: "none", border: "none", padding: 0 },
3706
+ children: [
3707
+ "Available: ",
3708
+ formatAmount(getMaxBalance()),
3709
+ " USDC"
3710
+ ]
3711
+ }
3712
+ )
3713
+ ] }),
3714
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: activeAction === "deposit" ? `Approves the token transfer, then transfers USDC from your ${!hasExternalWallet ? "embedded " : ""}wallet into your savings account. Requires 2 signatures.${!hasExternalWallet ? " Note: deposit and withdraw use your embedded wallet, not your bank account." : ""}` : `Moves USDC from your savings account back to your ${!hasExternalWallet ? "embedded " : ""}wallet. Requires 1 signature.${!hasExternalWallet ? " Gas fees are sponsored." : ""}` }),
3715
+ /* @__PURE__ */ jsxRuntime.jsx(TxStatus, { state: txState }),
3716
+ /* @__PURE__ */ jsxRuntime.jsx(
3717
+ "button",
3027
3718
  {
3028
- className: "flex items-center gap-2 p-3 border",
3719
+ onClick: handleTransfer,
3720
+ disabled: isProcessing || !amount || parseFloat(amount) <= 0,
3721
+ className: "w-full py-3 font-medium transition-colors flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
3029
3722
  style: {
3030
- backgroundColor: "var(--compass-color-background)",
3031
- borderColor: "var(--compass-color-border)",
3032
- borderRadius: "var(--compass-border-radius-lg)"
3723
+ backgroundColor: "var(--compass-color-primary)",
3724
+ color: "var(--compass-color-primary-text)",
3725
+ borderRadius: "var(--compass-border-radius-lg)",
3726
+ fontFamily: "var(--compass-font-family)"
3033
3727
  },
3034
- children: [
3035
- /* @__PURE__ */ jsxRuntime.jsx(
3036
- "input",
3037
- {
3038
- type: "number",
3039
- value: amount,
3040
- onChange: (e) => {
3041
- setAmount(e.target.value);
3042
- setError(null);
3043
- },
3044
- placeholder: "0.00",
3045
- disabled: isProcessing,
3046
- className: "flex-1 bg-transparent outline-none text-lg font-mono",
3047
- style: { color: "var(--compass-color-text)" }
3048
- }
3049
- ),
3050
- /* @__PURE__ */ jsxRuntime.jsx(
3051
- "span",
3052
- {
3053
- className: "text-sm font-medium",
3054
- style: { color: "var(--compass-color-text-secondary)" },
3055
- children: "USDC"
3056
- }
3057
- )
3058
- ]
3728
+ children: isProcessing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3729
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 18, className: "animate-spin" }),
3730
+ " Processing..."
3731
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3732
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpRight, { size: 16 }),
3733
+ " ",
3734
+ activeAction === "deposit" ? "Transfer to Savings Account" : "Withdraw to Wallet"
3735
+ ] })
3059
3736
  }
3060
- ),
3061
- /* @__PURE__ */ jsxRuntime.jsxs(
3062
- "button",
3737
+ ),
3738
+ /* @__PURE__ */ jsxRuntime.jsx(
3739
+ "p",
3063
3740
  {
3064
- onClick: () => setAmount(formatAmount(getMaxBalance())),
3065
- className: "text-xs mt-1",
3066
- style: { color: "var(--compass-color-primary)", cursor: "pointer", background: "none", border: "none", padding: 0 },
3067
- children: [
3068
- "Available: ",
3069
- formatAmount(getMaxBalance()),
3070
- " USDC"
3071
- ]
3741
+ className: "text-xs text-center",
3742
+ style: { color: "var(--compass-color-text-tertiary)" },
3743
+ children: "Gas fees are sponsored by Compass"
3072
3744
  }
3073
3745
  )
3074
- ] }),
3075
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: activeAction === "deposit" ? "Approves the token transfer, then transfers USDC from your wallet into your savings account. Requires 2 signatures." : "Withdraws USDC from your savings account back to your wallet. Requires 1 signature." }),
3076
- error && /* @__PURE__ */ jsxRuntime.jsx(
3077
- "div",
3078
- {
3079
- className: "p-3 text-sm",
3080
- style: {
3081
- backgroundColor: "var(--compass-color-error-muted)",
3082
- color: "var(--compass-color-error)",
3083
- borderRadius: "var(--compass-border-radius-lg)"
3084
- },
3085
- children: error
3086
- }
3087
- ),
3088
- statusMessage && !error && /* @__PURE__ */ jsxRuntime.jsx(
3089
- "div",
3090
- {
3091
- className: "p-3 text-sm text-center",
3092
- style: {
3093
- backgroundColor: transferState === "success" ? "var(--compass-color-success-muted)" : "var(--compass-color-primary-muted)",
3094
- color: transferState === "success" ? "var(--compass-color-success)" : "var(--compass-color-primary)",
3095
- borderRadius: "var(--compass-border-radius-lg)"
3096
- },
3097
- children: statusMessage
3098
- }
3099
- ),
3100
- /* @__PURE__ */ jsxRuntime.jsx(
3101
- "button",
3102
- {
3103
- onClick: handleTransfer,
3104
- disabled: isProcessing || !amount || parseFloat(amount) <= 0,
3105
- className: "w-full py-3 font-medium transition-colors flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
3106
- style: {
3107
- backgroundColor: "var(--compass-color-primary)",
3108
- color: "var(--compass-color-primary-text)",
3109
- borderRadius: "var(--compass-border-radius-lg)",
3110
- fontFamily: "var(--compass-font-family)"
3111
- },
3112
- children: isProcessing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3113
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 18, className: "animate-spin" }),
3114
- " Processing..."
3115
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3116
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpRight, { size: 16 }),
3117
- " ",
3118
- activeAction === "deposit" ? "Transfer to Savings Account" : "Withdraw to Wallet"
3119
- ] })
3120
- }
3121
- ),
3122
- /* @__PURE__ */ jsxRuntime.jsx(
3123
- "p",
3124
- {
3125
- className: "text-xs text-center",
3126
- style: { color: "var(--compass-color-text-tertiary)" },
3127
- children: "Gas fees are sponsored by Compass"
3128
- }
3129
- )
3746
+ ] })
3130
3747
  ] })
3131
3748
  }
3132
3749
  )
@@ -3181,7 +3798,7 @@ function truncate(value, decimals) {
3181
3798
  const factor = Math.pow(10, decimals);
3182
3799
  return (Math.floor(value * factor) / factor).toFixed(decimals);
3183
3800
  }
3184
- var BLOCK_EXPLORERS = {
3801
+ var BLOCK_EXPLORERS2 = {
3185
3802
  ethereum: "https://etherscan.io",
3186
3803
  base: "https://basescan.org",
3187
3804
  arbitrum: "https://arbiscan.io"
@@ -3652,7 +4269,7 @@ function SwapForm({
3652
4269
  /* @__PURE__ */ jsxRuntime.jsxs(
3653
4270
  "a",
3654
4271
  {
3655
- href: `${BLOCK_EXPLORERS[chainId] || BLOCK_EXPLORERS.ethereum}/tx/${lastTxHash}`,
4272
+ href: `${BLOCK_EXPLORERS2[chainId] || BLOCK_EXPLORERS2.ethereum}/tx/${lastTxHash}`,
3656
4273
  target: "_blank",
3657
4274
  rel: "noopener noreferrer",
3658
4275
  style: { display: "flex", alignItems: "center", gap: "4px", color: "var(--compass-color-text-secondary)", fontSize: "0.75rem", textDecoration: "none" },
@@ -3842,8 +4459,7 @@ function MarketSelector({
3842
4459
  },
3843
4460
  children: [
3844
4461
  getTypeLabel(selectedMarket.type),
3845
- " \xB7 ",
3846
- formatTvl(selectedMarket.tvl)
4462
+ selectedMarket.type !== "aave" ? ` \xB7 ${formatTvl(selectedMarket.tvl)}` : ""
3847
4463
  ]
3848
4464
  }
3849
4465
  )
@@ -3949,8 +4565,7 @@ function MarketSelector({
3949
4565
  },
3950
4566
  children: [
3951
4567
  getTypeLabel(market.type),
3952
- " \xB7 ",
3953
- formatTvl(market.tvl)
4568
+ market.type !== "aave" ? ` \xB7 ${formatTvl(market.tvl)}` : ""
3954
4569
  ]
3955
4570
  }
3956
4571
  )
@@ -3979,7 +4594,7 @@ function MarketSelector({
3979
4594
  )
3980
4595
  ] });
3981
4596
  }
3982
- var BLOCK_EXPLORERS2 = {
4597
+ var BLOCK_EXPLORERS3 = {
3983
4598
  ethereum: "https://etherscan.io",
3984
4599
  base: "https://basescan.org",
3985
4600
  arbitrum: "https://arbiscan.io"
@@ -4141,7 +4756,7 @@ function PositionCard({ position, chainId }) {
4141
4756
  tx.txHash && /* @__PURE__ */ jsxRuntime.jsx(
4142
4757
  "a",
4143
4758
  {
4144
- href: `${BLOCK_EXPLORERS2[chainId || "ethereum"] || BLOCK_EXPLORERS2.ethereum}/tx/${tx.txHash}`,
4759
+ href: `${BLOCK_EXPLORERS3[chainId || "ethereum"] || BLOCK_EXPLORERS3.ethereum}/tx/${tx.txHash}`,
4145
4760
  target: "_blank",
4146
4761
  rel: "noopener noreferrer",
4147
4762
  style: { color: "var(--compass-color-text-tertiary)", lineHeight: 0 },
@@ -4216,220 +4831,88 @@ function EarningsModal({ isOpen, onClose, positions, totalEarned, isLoading, cha
4216
4831
  padding: "6px",
4217
4832
  color: "var(--compass-color-text-secondary)",
4218
4833
  borderRadius: "8px",
4219
- display: "flex",
4220
- alignItems: "center",
4221
- justifyContent: "center"
4222
- },
4223
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 16 })
4224
- }
4225
- )
4226
- ] }),
4227
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
4228
- padding: "16px 20px",
4229
- margin: "0 16px",
4230
- backgroundColor: "var(--compass-color-surface)",
4231
- borderRadius: "14px"
4232
- }, children: [
4233
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
4234
- display: "flex",
4235
- alignItems: "center",
4236
- gap: "8px",
4237
- marginBottom: "8px"
4238
- }, children: [
4239
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
4240
- width: "28px",
4241
- height: "28px",
4242
- borderRadius: "50%",
4243
- backgroundColor: totalEarned >= 0 ? "var(--compass-color-success-muted)" : "var(--compass-color-error-muted)",
4244
- display: "flex",
4245
- alignItems: "center",
4246
- justifyContent: "center"
4247
- }, children: totalEarned >= 0 ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { size: 14, style: { color: "var(--compass-color-success)" } }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingDown, { size: 14, style: { color: "var(--compass-color-error)" } }) }),
4248
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4249
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "0.6875rem", color: "var(--compass-color-text-tertiary)" }, children: "Total P&L" }),
4250
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
4251
- fontSize: "1.25rem",
4252
- fontWeight: 700,
4253
- color: totalEarned >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)",
4254
- letterSpacing: "-0.02em"
4255
- }, children: [
4256
- totalEarned >= 0 ? "+" : "",
4257
- formatCurrency(totalEarned)
4258
- ] })
4259
- ] })
4260
- ] }),
4261
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "16px" }, children: [
4262
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4263
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.6875rem", color: "var(--compass-color-text-tertiary)" }, children: "Unrealised " }),
4264
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: {
4265
- fontSize: "0.8125rem",
4266
- fontWeight: 600,
4267
- color: totalUnrealized >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)"
4268
- }, children: [
4269
- totalUnrealized >= 0 ? "+" : "",
4270
- formatCurrency(totalUnrealized)
4271
- ] })
4272
- ] }),
4273
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4274
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.6875rem", color: "var(--compass-color-text-tertiary)" }, children: "Realised " }),
4275
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: {
4276
- fontSize: "0.8125rem",
4277
- fontWeight: 600,
4278
- color: totalRealized >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)"
4279
- }, children: [
4280
- totalRealized >= 0 ? "+" : "",
4281
- formatCurrency(totalRealized)
4282
- ] })
4283
- ] })
4284
- ] })
4285
- ] }),
4286
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
4287
- flex: 1,
4288
- overflowY: "auto",
4289
- scrollbarWidth: "none",
4290
- padding: "16px",
4291
- display: "flex",
4292
- flexDirection: "column",
4293
- gap: "10px"
4294
- }, children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { textAlign: "center", padding: "32px 0", color: "var(--compass-color-text-tertiary)", fontSize: "0.875rem" }, children: "Loading positions..." }) : positions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center", padding: "32px 0" }, children: [
4295
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--compass-color-text-secondary)", fontSize: "0.9375rem", margin: "0 0 4px" }, children: "No active positions yet" }),
4296
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--compass-color-text-tertiary)", fontSize: "0.8125rem", margin: 0 }, children: "Deposit into a market to start earning" })
4297
- ] }) : positions.map((position) => /* @__PURE__ */ jsxRuntime.jsx(PositionCard, { position, chainId }, position.id)) })
4298
- ]
4299
- }
4300
- )
4301
- }
4302
- );
4303
- }
4304
- var BLOCK_EXPLORERS3 = {
4305
- ethereum: "https://etherscan.io",
4306
- base: "https://basescan.org",
4307
- arbitrum: "https://arbiscan.io"
4308
- };
4309
- function TxStatus({ state }) {
4310
- const { chainId } = useChain();
4311
- if (state.status === "idle") return null;
4312
- const explorer = BLOCK_EXPLORERS3[chainId] || BLOCK_EXPLORERS3.ethereum;
4313
- return /* @__PURE__ */ jsxRuntime.jsxs(
4314
- "div",
4315
- {
4316
- className: "mt-4 p-4 border",
4317
- style: {
4318
- borderColor: "var(--compass-color-border)",
4319
- borderRadius: "var(--compass-border-radius-xl)",
4320
- fontFamily: "var(--compass-font-family)"
4321
- },
4322
- children: [
4323
- state.status === "preparing" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
4324
- /* @__PURE__ */ jsxRuntime.jsx(
4325
- lucideReact.Loader2,
4326
- {
4327
- className: "h-4 w-4 animate-spin",
4328
- style: { color: "var(--compass-color-primary)" }
4329
- }
4330
- ),
4331
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-secondary)" }, children: "Preparing transaction..." })
4332
- ] }),
4333
- state.status === "signing" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
4334
- /* @__PURE__ */ jsxRuntime.jsx(
4335
- lucideReact.Loader2,
4336
- {
4337
- className: "h-4 w-4 animate-spin",
4338
- style: { color: "var(--compass-color-warning)" }
4339
- }
4340
- ),
4341
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-secondary)" }, children: "Waiting for signature..." })
4342
- ] }),
4343
- state.status === "broadcasting" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
4344
- /* @__PURE__ */ jsxRuntime.jsx(
4345
- lucideReact.Loader2,
4346
- {
4347
- className: "h-4 w-4 animate-spin",
4348
- style: { color: "var(--compass-color-primary)" }
4349
- }
4350
- ),
4351
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-secondary)" }, children: "Broadcasting transaction..." })
4352
- ] }),
4353
- state.status === "submitted" && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4354
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
4355
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
4356
- /* @__PURE__ */ jsxRuntime.jsx(
4357
- lucideReact.Loader2,
4358
- {
4359
- className: "h-4 w-4 animate-spin",
4360
- style: { color: "var(--compass-color-primary)" }
4361
- }
4362
- ),
4363
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text)" }, children: "Transaction submitted" })
4364
- ] }),
4365
- /* @__PURE__ */ jsxRuntime.jsxs(
4366
- "a",
4367
- {
4368
- href: `${explorer}/tx/${state.txHash}`,
4369
- target: "_blank",
4370
- rel: "noopener noreferrer",
4371
- className: "flex items-center gap-1.5 text-xs hover:opacity-70 transition-opacity",
4372
- style: { color: "var(--compass-color-text-secondary)" },
4373
- children: [
4374
- "View on explorer",
4375
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3" })
4376
- ]
4377
- }
4378
- )
4379
- ] }),
4380
- /* @__PURE__ */ jsxRuntime.jsx(
4381
- "p",
4382
- {
4383
- className: "text-xs mt-2",
4384
- style: { color: "var(--compass-color-text-tertiary)" },
4385
- children: "Waiting for on-chain confirmation. Balances will update automatically."
4386
- }
4387
- )
4388
- ] }),
4389
- state.status === "confirmed" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
4390
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
4391
- /* @__PURE__ */ jsxRuntime.jsx(
4392
- lucideReact.CheckCircle,
4393
- {
4394
- className: "h-4 w-4",
4395
- style: { color: "var(--compass-color-success)" }
4396
- }
4397
- ),
4398
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-success)" }, children: "Transaction confirmed" })
4399
- ] }),
4400
- /* @__PURE__ */ jsxRuntime.jsxs(
4401
- "a",
4402
- {
4403
- href: `${explorer}/tx/${state.txHash}`,
4404
- target: "_blank",
4405
- rel: "noopener noreferrer",
4406
- className: "flex items-center gap-1.5 text-xs hover:opacity-70 transition-opacity",
4407
- style: { color: "var(--compass-color-text-secondary)" },
4408
- children: [
4409
- "View on explorer",
4410
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3" })
4411
- ]
4412
- }
4413
- )
4414
- ] }),
4415
- state.status === "failed" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 text-sm", children: [
4416
- /* @__PURE__ */ jsxRuntime.jsx(
4417
- lucideReact.XCircle,
4418
- {
4419
- className: "h-4 w-4 mt-0.5 shrink-0",
4420
- style: { color: "var(--compass-color-error)" }
4421
- }
4422
- ),
4423
- /* @__PURE__ */ jsxRuntime.jsx(
4424
- "span",
4425
- {
4426
- className: "break-all",
4427
- style: { color: "var(--compass-color-error)" },
4428
- children: state.error
4429
- }
4430
- )
4431
- ] })
4432
- ]
4834
+ display: "flex",
4835
+ alignItems: "center",
4836
+ justifyContent: "center"
4837
+ },
4838
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 16 })
4839
+ }
4840
+ )
4841
+ ] }),
4842
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
4843
+ padding: "16px 20px",
4844
+ margin: "0 16px",
4845
+ backgroundColor: "var(--compass-color-surface)",
4846
+ borderRadius: "14px"
4847
+ }, children: [
4848
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
4849
+ display: "flex",
4850
+ alignItems: "center",
4851
+ gap: "8px",
4852
+ marginBottom: "8px"
4853
+ }, children: [
4854
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
4855
+ width: "28px",
4856
+ height: "28px",
4857
+ borderRadius: "50%",
4858
+ backgroundColor: totalEarned >= 0 ? "var(--compass-color-success-muted)" : "var(--compass-color-error-muted)",
4859
+ display: "flex",
4860
+ alignItems: "center",
4861
+ justifyContent: "center"
4862
+ }, children: totalEarned >= 0 ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { size: 14, style: { color: "var(--compass-color-success)" } }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingDown, { size: 14, style: { color: "var(--compass-color-error)" } }) }),
4863
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4864
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "0.6875rem", color: "var(--compass-color-text-tertiary)" }, children: "Total P&L" }),
4865
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
4866
+ fontSize: "1.25rem",
4867
+ fontWeight: 700,
4868
+ color: totalEarned >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)",
4869
+ letterSpacing: "-0.02em"
4870
+ }, children: [
4871
+ totalEarned >= 0 ? "+" : "",
4872
+ formatCurrency(totalEarned)
4873
+ ] })
4874
+ ] })
4875
+ ] }),
4876
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "16px" }, children: [
4877
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4878
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.6875rem", color: "var(--compass-color-text-tertiary)" }, children: "Unrealised " }),
4879
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: {
4880
+ fontSize: "0.8125rem",
4881
+ fontWeight: 600,
4882
+ color: totalUnrealized >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)"
4883
+ }, children: [
4884
+ totalUnrealized >= 0 ? "+" : "",
4885
+ formatCurrency(totalUnrealized)
4886
+ ] })
4887
+ ] }),
4888
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4889
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.6875rem", color: "var(--compass-color-text-tertiary)" }, children: "Realised " }),
4890
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: {
4891
+ fontSize: "0.8125rem",
4892
+ fontWeight: 600,
4893
+ color: totalRealized >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)"
4894
+ }, children: [
4895
+ totalRealized >= 0 ? "+" : "",
4896
+ formatCurrency(totalRealized)
4897
+ ] })
4898
+ ] })
4899
+ ] })
4900
+ ] }),
4901
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
4902
+ flex: 1,
4903
+ overflowY: "auto",
4904
+ scrollbarWidth: "none",
4905
+ padding: "16px",
4906
+ display: "flex",
4907
+ flexDirection: "column",
4908
+ gap: "10px"
4909
+ }, children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { textAlign: "center", padding: "32px 0", color: "var(--compass-color-text-tertiary)", fontSize: "0.875rem" }, children: "Loading positions..." }) : positions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center", padding: "32px 0" }, children: [
4910
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--compass-color-text-secondary)", fontSize: "0.9375rem", margin: "0 0 4px" }, children: "No active positions yet" }),
4911
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--compass-color-text-tertiary)", fontSize: "0.8125rem", margin: 0 }, children: "Deposit into a market to start earning" })
4912
+ ] }) : positions.map((position) => /* @__PURE__ */ jsxRuntime.jsx(PositionCard, { position, chainId }, position.id)) })
4913
+ ]
4914
+ }
4915
+ )
4433
4916
  }
4434
4917
  );
4435
4918
  }
@@ -4437,7 +4920,7 @@ function truncate2(value, decimals) {
4437
4920
  const factor = Math.pow(10, decimals);
4438
4921
  return (Math.floor(value * factor) / factor).toFixed(decimals);
4439
4922
  }
4440
- var EVM_CHAIN_IDS = {
4923
+ var EVM_CHAIN_IDS2 = {
4441
4924
  ethereum: 1,
4442
4925
  base: 8453,
4443
4926
  arbitrum: 42161
@@ -4478,8 +4961,8 @@ function EarnAccount({
4478
4961
  chain: chainProp,
4479
4962
  height = "600px"
4480
4963
  }) {
4481
- const { address, isConnected, login, logout, signTypedData, switchChain, walletChainId } = useEmbeddableWallet();
4482
- const { isDeployed } = useEarnAccount();
4964
+ const { address, isConnected, login, logout, signTypedData, switchChain, walletChainId, fundWallet, hasExternalWallet, sendTransaction } = useEmbeddableWallet();
4965
+ const { isDeployed, earnAccountAddress } = useEarnAccount();
4483
4966
  const { chainId: contextChainId } = useChain();
4484
4967
  const CHAIN_ID = chainProp || contextChainId;
4485
4968
  const queryClient = reactQuery.useQueryClient();
@@ -4493,10 +4976,15 @@ function EarnAccount({
4493
4976
  const [isTokenDropdownOpen, setIsTokenDropdownOpen] = react.useState(false);
4494
4977
  const [isFundModalOpen, setIsFundModalOpen] = react.useState(false);
4495
4978
  const [fundAmount, setFundAmount] = react.useState("");
4496
- const [fundAction, setFundAction] = react.useState("deposit");
4979
+ const [fundAction, setFundAction] = react.useState(
4980
+ !hasExternalWallet && fundWallet ? "buy" : "deposit"
4981
+ );
4497
4982
  const [fundStep, setFundStep] = react.useState("idle");
4498
4983
  const [fundTxState, setFundTxState] = react.useState({ status: "idle" });
4499
4984
  const fundPollRef = react.useRef(null);
4985
+ const showBuyTab = !!fundWallet;
4986
+ const showSendTab = !!sendTransaction && !hasExternalWallet;
4987
+ const fundBuyOnly = !hasExternalWallet && showBuyTab && !sendTransaction;
4500
4988
  const actionPollRef = react.useRef(null);
4501
4989
  const actionTimeoutRef = react.useRef(null);
4502
4990
  const [isBalancesModalOpen, setIsBalancesModalOpen] = react.useState(false);
@@ -4536,7 +5024,7 @@ function EarnAccount({
4536
5024
  const fundIsValid = Number(fundAmount) > 0 && !!address && isDeployed;
4537
5025
  const fundPhase = getFundPhase(fundStep);
4538
5026
  const ensureCorrectChain = react.useCallback(async () => {
4539
- const targetChainId = EVM_CHAIN_IDS[CHAIN_ID];
5027
+ const targetChainId = EVM_CHAIN_IDS2[CHAIN_ID];
4540
5028
  if (!targetChainId) return;
4541
5029
  if (walletChainId !== void 0 && walletChainId !== targetChainId) {
4542
5030
  if (!switchChain) {
@@ -4560,7 +5048,7 @@ function EarnAccount({
4560
5048
  return null;
4561
5049
  }
4562
5050
  },
4563
- enabled: !!address && isFundModalOpen && fundAction === "deposit",
5051
+ enabled: !!address && hasExternalWallet && isFundModalOpen && fundAction === "deposit",
4564
5052
  staleTime: 15 * 1e3
4565
5053
  });
4566
5054
  const walletBalance = walletBalanceQuery.data?.balance || "0";
@@ -4844,7 +5332,10 @@ function EarnAccount({
4844
5332
  setFundTxState({ status: "failed", error: "Transaction reverted" });
4845
5333
  return;
4846
5334
  }
4847
- } catch {
5335
+ } catch (err) {
5336
+ if (err instanceof TypeError && err.message.includes("fetch")) ; else {
5337
+ console.warn("[EarnAccount] Unexpected error polling tx receipt:", err);
5338
+ }
4848
5339
  }
4849
5340
  if (polls >= 24) {
4850
5341
  clearFundPolling();
@@ -5735,13 +6226,13 @@ function EarnAccount({
5735
6226
  onClose: () => {
5736
6227
  setIsFundModalOpen(false);
5737
6228
  setFundAmount("");
5738
- setFundAction("deposit");
6229
+ setFundAction(fundBuyOnly ? "buy" : showSendTab ? "buy" : "deposit");
5739
6230
  setFundStep("idle");
5740
6231
  setFundTxState({ status: "idle" });
5741
6232
  },
5742
- title: "Transfer Funds",
6233
+ title: showSendTab ? "Manage Funds" : fundBuyOnly ? "Add Funds" : "Transfer Funds",
5743
6234
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
5744
- /* @__PURE__ */ jsxRuntime.jsx(
6235
+ fundBuyOnly ? null : /* @__PURE__ */ jsxRuntime.jsx(
5745
6236
  "div",
5746
6237
  {
5747
6238
  className: "flex gap-1 p-1",
@@ -5750,7 +6241,9 @@ function EarnAccount({
5750
6241
  borderRadius: "var(--compass-border-radius-lg)",
5751
6242
  fontFamily: "var(--compass-font-family)"
5752
6243
  },
5753
- children: ["deposit", "withdraw"].map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
6244
+ children: [
6245
+ ...showSendTab ? ["buy", "send"] : ["deposit", "withdraw", ...showBuyTab ? ["buy"] : []]
6246
+ ].map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
5754
6247
  "button",
5755
6248
  {
5756
6249
  onClick: () => handleFundActionChange(tab),
@@ -5763,7 +6256,7 @@ function EarnAccount({
5763
6256
  border: "none"
5764
6257
  },
5765
6258
  children: [
5766
- tab === "deposit" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownLeft, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpRight, { size: 14 }),
6259
+ tab === "deposit" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownLeft, { size: 14 }) : tab === "withdraw" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpRight, { size: 14 }) : tab === "send" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { size: 14 }),
5767
6260
  tab
5768
6261
  ]
5769
6262
  },
@@ -5771,137 +6264,158 @@ function EarnAccount({
5771
6264
  ))
5772
6265
  }
5773
6266
  ),
5774
- (fundIsBusy || fundStep === "confirmed") && fundAction === "deposit" && /* @__PURE__ */ jsxRuntime.jsxs(
5775
- "div",
6267
+ fundAction === "send" ? /* @__PURE__ */ jsxRuntime.jsx(
6268
+ SendForm,
5776
6269
  {
5777
- className: "flex gap-3 p-3 text-xs",
5778
- style: {
5779
- backgroundColor: "var(--compass-color-surface)",
5780
- borderRadius: "var(--compass-border-radius-lg)",
5781
- fontFamily: "var(--compass-font-family)"
5782
- },
5783
- children: [
5784
- /* @__PURE__ */ jsxRuntime.jsx(
5785
- FundStepIndicator,
5786
- {
5787
- number: 1,
5788
- label: "Approve",
5789
- state: fundPhase > 1 ? "done" : fundPhase === 1 ? "active" : "pending"
5790
- }
5791
- ),
5792
- /* @__PURE__ */ jsxRuntime.jsx(
5793
- FundStepIndicator,
5794
- {
5795
- number: 2,
5796
- label: "Transfer",
5797
- state: fundStep === "confirmed" ? "done" : fundPhase > 1 ? "active" : "pending"
5798
- }
5799
- )
5800
- ]
6270
+ onComplete: () => {
6271
+ queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
6272
+ queryClient.invalidateQueries({ queryKey: ["walletBalance"] });
6273
+ queryClient.invalidateQueries({ queryKey: ["walletTokenBalance"] });
6274
+ }
5801
6275
  }
5802
- ),
5803
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
5804
- /* @__PURE__ */ jsxRuntime.jsx(
5805
- "label",
6276
+ ) : fundAction === "buy" || fundBuyOnly ? /* @__PURE__ */ jsxRuntime.jsx(
6277
+ BuyForm,
6278
+ {
6279
+ targetAddress: earnAccountAddress || "",
6280
+ onComplete: () => {
6281
+ queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
6282
+ queryClient.invalidateQueries({ queryKey: ["walletBalance"] });
6283
+ queryClient.invalidateQueries({ queryKey: ["walletTokenBalance"] });
6284
+ }
6285
+ }
6286
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6287
+ (fundIsBusy || fundStep === "confirmed") && fundAction === "deposit" && /* @__PURE__ */ jsxRuntime.jsxs(
6288
+ "div",
5806
6289
  {
5807
- className: "block text-sm mb-1.5",
6290
+ className: "flex gap-3 p-3 text-xs",
5808
6291
  style: {
5809
- color: "var(--compass-color-text-secondary)",
6292
+ backgroundColor: "var(--compass-color-surface)",
6293
+ borderRadius: "var(--compass-border-radius-lg)",
5810
6294
  fontFamily: "var(--compass-font-family)"
5811
6295
  },
5812
- children: "Amount"
6296
+ children: [
6297
+ /* @__PURE__ */ jsxRuntime.jsx(
6298
+ FundStepIndicator,
6299
+ {
6300
+ number: 1,
6301
+ label: "Approve",
6302
+ state: fundPhase > 1 ? "done" : fundPhase === 1 ? "active" : "pending"
6303
+ }
6304
+ ),
6305
+ /* @__PURE__ */ jsxRuntime.jsx(
6306
+ FundStepIndicator,
6307
+ {
6308
+ number: 2,
6309
+ label: "Transfer",
6310
+ state: fundStep === "confirmed" ? "done" : fundPhase > 1 ? "active" : "pending"
6311
+ }
6312
+ )
6313
+ ]
5813
6314
  }
5814
6315
  ),
5815
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
6316
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
5816
6317
  /* @__PURE__ */ jsxRuntime.jsx(
5817
- "input",
6318
+ "label",
5818
6319
  {
5819
- type: "number",
5820
- value: fundAmount,
5821
- onChange: (e) => {
5822
- setFundAmount(e.target.value);
5823
- setFundTxState({ status: "idle" });
5824
- },
5825
- disabled: fundIsBusy,
5826
- placeholder: "0.00",
5827
- className: "w-full border px-3 py-2.5 pr-14 text-sm focus:outline-none disabled:opacity-50",
6320
+ className: "block text-sm mb-1.5",
5828
6321
  style: {
5829
- backgroundColor: "var(--compass-color-surface)",
5830
- borderColor: "var(--compass-color-border)",
5831
- borderRadius: "var(--compass-border-radius-lg)",
5832
- color: "var(--compass-color-text)",
6322
+ color: "var(--compass-color-text-secondary)",
5833
6323
  fontFamily: "var(--compass-font-family)"
5834
- }
6324
+ },
6325
+ children: "Amount"
5835
6326
  }
5836
6327
  ),
5837
- /* @__PURE__ */ jsxRuntime.jsx(
5838
- "span",
6328
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
6329
+ /* @__PURE__ */ jsxRuntime.jsx(
6330
+ "input",
6331
+ {
6332
+ type: "number",
6333
+ value: fundAmount,
6334
+ onChange: (e) => {
6335
+ setFundAmount(e.target.value);
6336
+ setFundTxState({ status: "idle" });
6337
+ },
6338
+ disabled: fundIsBusy,
6339
+ placeholder: "0.00",
6340
+ className: "w-full border px-3 py-2.5 pr-14 text-sm focus:outline-none disabled:opacity-50",
6341
+ style: {
6342
+ backgroundColor: "var(--compass-color-surface)",
6343
+ borderColor: "var(--compass-color-border)",
6344
+ borderRadius: "var(--compass-border-radius-lg)",
6345
+ color: "var(--compass-color-text)",
6346
+ fontFamily: "var(--compass-font-family)"
6347
+ }
6348
+ }
6349
+ ),
6350
+ /* @__PURE__ */ jsxRuntime.jsx(
6351
+ "span",
6352
+ {
6353
+ className: "absolute right-3 top-1/2 -translate-y-1/2 text-xs",
6354
+ style: { color: "var(--compass-color-text-tertiary)" },
6355
+ children: "USDC"
6356
+ }
6357
+ )
6358
+ ] }),
6359
+ fundMaxBalance !== "0" && /* @__PURE__ */ jsxRuntime.jsxs(
6360
+ "p",
5839
6361
  {
5840
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-xs",
6362
+ className: "text-xs mt-1",
5841
6363
  style: { color: "var(--compass-color-text-tertiary)" },
5842
- children: "USDC"
6364
+ children: [
6365
+ "Available:",
6366
+ " ",
6367
+ /* @__PURE__ */ jsxRuntime.jsxs(
6368
+ "button",
6369
+ {
6370
+ onClick: () => setFundAmount(truncate2(parseFloat(fundMaxBalance), 2)),
6371
+ disabled: fundIsBusy,
6372
+ style: { color: "var(--compass-color-primary)", background: "none", border: "none", padding: 0, cursor: "pointer", fontSize: "inherit" },
6373
+ children: [
6374
+ truncate2(parseFloat(fundMaxBalance), 2),
6375
+ " USDC"
6376
+ ]
6377
+ }
6378
+ )
6379
+ ]
5843
6380
  }
5844
6381
  )
5845
6382
  ] }),
5846
- fundMaxBalance !== "0" && /* @__PURE__ */ jsxRuntime.jsxs(
6383
+ !fundIsBusy && fundStep !== "confirmed" && fundStep !== "failed" && fundTxState.status === "idle" && /* @__PURE__ */ jsxRuntime.jsx(
5847
6384
  "p",
5848
6385
  {
5849
- className: "text-xs mt-1",
5850
- style: { color: "var(--compass-color-text-tertiary)" },
5851
- children: [
5852
- "Available:",
5853
- " ",
5854
- /* @__PURE__ */ jsxRuntime.jsxs(
5855
- "button",
5856
- {
5857
- onClick: () => setFundAmount(truncate2(parseFloat(fundMaxBalance), 2)),
5858
- disabled: fundIsBusy,
5859
- style: { color: "var(--compass-color-primary)", background: "none", border: "none", padding: 0, cursor: "pointer", fontSize: "inherit" },
5860
- children: [
5861
- truncate2(parseFloat(fundMaxBalance), 2),
5862
- " USDC"
5863
- ]
5864
- }
5865
- )
5866
- ]
6386
+ className: "text-xs",
6387
+ style: {
6388
+ color: "var(--compass-color-text-tertiary)",
6389
+ fontFamily: "var(--compass-font-family)"
6390
+ },
6391
+ children: fundAction === "deposit" ? `Approves the token transfer, then transfers USDC from your ${!hasExternalWallet ? "embedded " : ""}wallet into your savings account. Requires 2 signatures.${!hasExternalWallet ? " Note: deposit and withdraw use your embedded wallet, not your bank account." : ""}` : `Moves USDC from your savings account back to your ${!hasExternalWallet ? "embedded " : ""}wallet. Requires 1 signature.${!hasExternalWallet ? " Gas fees are sponsored." : ""}`
5867
6392
  }
5868
- )
5869
- ] }),
5870
- !fundIsBusy && fundStep !== "confirmed" && fundStep !== "failed" && fundTxState.status === "idle" && /* @__PURE__ */ jsxRuntime.jsx(
5871
- "p",
5872
- {
5873
- className: "text-xs",
5874
- style: {
5875
- color: "var(--compass-color-text-tertiary)",
5876
- fontFamily: "var(--compass-font-family)"
5877
- },
5878
- children: fundAction === "deposit" ? "Approves the token transfer, then transfers USDC from your wallet into your savings account. Requires 2 signatures." : "Transfers USDC from your savings account back to your wallet. Requires 1 signature."
5879
- }
5880
- ),
5881
- /* @__PURE__ */ jsxRuntime.jsx(
5882
- "button",
5883
- {
5884
- onClick: handleFundTransfer,
5885
- disabled: !fundIsValid || fundIsBusy,
5886
- className: "w-full py-3 px-4 text-sm font-medium flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
5887
- style: {
5888
- backgroundColor: "var(--compass-color-primary)",
5889
- color: "var(--compass-color-primary-text)",
5890
- borderRadius: "var(--compass-border-radius-xl)",
5891
- fontFamily: "var(--compass-font-family)",
5892
- transition: "all 200ms ease",
5893
- border: "none"
5894
- },
5895
- children: fundIsBusy ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5896
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
5897
- fundButtonLabel()
5898
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5899
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { className: "h-4 w-4" }),
5900
- fundAction === "deposit" ? "Transfer to Savings Account" : "Withdraw to Wallet"
5901
- ] })
5902
- }
5903
- ),
5904
- /* @__PURE__ */ jsxRuntime.jsx(TxStatus, { state: fundTxState })
6393
+ ),
6394
+ /* @__PURE__ */ jsxRuntime.jsx(
6395
+ "button",
6396
+ {
6397
+ onClick: handleFundTransfer,
6398
+ disabled: !fundIsValid || fundIsBusy,
6399
+ className: "w-full py-3 px-4 text-sm font-medium flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
6400
+ style: {
6401
+ backgroundColor: "var(--compass-color-primary)",
6402
+ color: "var(--compass-color-primary-text)",
6403
+ borderRadius: "var(--compass-border-radius-xl)",
6404
+ fontFamily: "var(--compass-font-family)",
6405
+ transition: "all 200ms ease",
6406
+ border: "none"
6407
+ },
6408
+ children: fundIsBusy ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6409
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
6410
+ fundButtonLabel()
6411
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6412
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { className: "h-4 w-4" }),
6413
+ fundAction === "deposit" ? "Transfer to Savings Account" : "Withdraw to Wallet"
6414
+ ] })
6415
+ }
6416
+ ),
6417
+ /* @__PURE__ */ jsxRuntime.jsx(TxStatus, { state: fundTxState })
6418
+ ] })
5905
6419
  ] })
5906
6420
  }
5907
6421
  ),
@@ -7708,19 +8222,24 @@ function getPhase(step) {
7708
8222
  if (step === "approving" || step === "signing-approval") return 1;
7709
8223
  return 2;
7710
8224
  }
7711
- var EVM_CHAIN_IDS2 = {
8225
+ var EVM_CHAIN_IDS3 = {
7712
8226
  ethereum: 1,
7713
8227
  base: 8453,
7714
8228
  arbitrum: 42161
7715
8229
  };
7716
8230
  function TopUpModal({ isOpen, onClose }) {
7717
- const { address, signTypedData, switchChain, walletChainId } = useEmbeddableWallet();
8231
+ const { address, signTypedData, switchChain, walletChainId, fundWallet, hasExternalWallet, sendTransaction } = useEmbeddableWallet();
7718
8232
  const { chainId } = useChain();
7719
- const { isDeployed } = useCreditAccount();
8233
+ const { isDeployed, creditAccountAddress } = useCreditAccount();
7720
8234
  const queryClient = reactQuery.useQueryClient();
7721
8235
  const prices = useTokenPrices();
7722
8236
  const token = "USDC";
7723
- const [action, setAction] = react.useState("deposit");
8237
+ const [action, setAction] = react.useState(
8238
+ !hasExternalWallet && fundWallet ? "buy" : "deposit"
8239
+ );
8240
+ const showBuyTab = !!fundWallet;
8241
+ const showSendTab = !!sendTransaction && !hasExternalWallet;
8242
+ const buyOnly = !hasExternalWallet && showBuyTab && !sendTransaction;
7724
8243
  const [amount, setAmount] = react.useState("");
7725
8244
  const [step, setStep] = react.useState("idle");
7726
8245
  const [txState, setTxState] = react.useState({ status: "idle" });
@@ -7769,7 +8288,7 @@ function TopUpModal({ isOpen, onClose }) {
7769
8288
  const isValid = Number(amount) > 0 && !!address && isDeployed;
7770
8289
  const isBusy = step === "approving" || step === "signing-approval" || step === "preparing-transfer" || step === "signing-transfer" || step === "executing";
7771
8290
  const ensureCorrectChain = react.useCallback(async () => {
7772
- const targetChainId = EVM_CHAIN_IDS2[CHAIN_ID];
8291
+ const targetChainId = EVM_CHAIN_IDS3[CHAIN_ID];
7773
8292
  if (!targetChainId) return;
7774
8293
  if (walletChainId !== void 0 && walletChainId !== targetChainId) {
7775
8294
  if (!switchChain) {
@@ -7971,7 +8490,10 @@ function TopUpModal({ isOpen, onClose }) {
7971
8490
  setTxState({ status: "failed", error: "Transaction reverted" });
7972
8491
  return;
7973
8492
  }
7974
- } catch {
8493
+ } catch (err) {
8494
+ if (err instanceof TypeError && err.message.includes("fetch")) ; else {
8495
+ console.warn("[TopUpModal] Unexpected error polling tx receipt:", err);
8496
+ }
7975
8497
  }
7976
8498
  if (polls >= 24) {
7977
8499
  clearPolling();
@@ -8007,8 +8529,8 @@ function TopUpModal({ isOpen, onClose }) {
8007
8529
  color: "var(--compass-color-text-secondary)",
8008
8530
  fontFamily: "var(--compass-font-family)"
8009
8531
  };
8010
- return /* @__PURE__ */ jsxRuntime.jsx(ActionModal, { isOpen, onClose, title: "Transfer Funds", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
8011
- /* @__PURE__ */ jsxRuntime.jsx(
8532
+ return /* @__PURE__ */ jsxRuntime.jsx(ActionModal, { isOpen, onClose, title: showSendTab ? "Manage Funds" : buyOnly ? "Add Funds" : "Transfer Funds", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
8533
+ !buyOnly && /* @__PURE__ */ jsxRuntime.jsx(
8012
8534
  "div",
8013
8535
  {
8014
8536
  className: "flex gap-1 p-1",
@@ -8017,7 +8539,9 @@ function TopUpModal({ isOpen, onClose }) {
8017
8539
  borderRadius: "var(--compass-border-radius-lg)",
8018
8540
  fontFamily: "var(--compass-font-family)"
8019
8541
  },
8020
- children: ["deposit", "withdraw"].map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
8542
+ children: [
8543
+ ...showSendTab ? ["buy", "send"] : showBuyTab ? ["deposit", "withdraw", "buy"] : ["deposit", "withdraw"]
8544
+ ].map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
8021
8545
  "button",
8022
8546
  {
8023
8547
  onClick: () => handleActionChange(tab),
@@ -8029,7 +8553,7 @@ function TopUpModal({ isOpen, onClose }) {
8029
8553
  borderRadius: "var(--compass-border-radius-md)"
8030
8554
  },
8031
8555
  children: [
8032
- tab === "deposit" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownLeft, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpRight, { size: 14 }),
8556
+ tab === "deposit" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownLeft, { size: 14 }) : tab === "withdraw" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpRight, { size: 14 }) : tab === "send" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { size: 14 }),
8033
8557
  tab
8034
8558
  ]
8035
8559
  },
@@ -8037,122 +8561,143 @@ function TopUpModal({ isOpen, onClose }) {
8037
8561
  ))
8038
8562
  }
8039
8563
  ),
8040
- (isBusy || step === "confirmed") && action === "deposit" && /* @__PURE__ */ jsxRuntime.jsxs(
8041
- "div",
8564
+ action === "send" ? /* @__PURE__ */ jsxRuntime.jsx(
8565
+ SendForm,
8042
8566
  {
8043
- className: "flex gap-3 p-3 text-xs",
8044
- style: {
8045
- backgroundColor: "var(--compass-color-surface)",
8046
- borderRadius: "var(--compass-border-radius-lg)",
8047
- fontFamily: "var(--compass-font-family)"
8048
- },
8049
- children: [
8567
+ product: "credit",
8568
+ productAccountAddress: creditAccountAddress || void 0,
8569
+ onComplete: () => {
8570
+ queryClient.invalidateQueries({ queryKey: ["creditBalances"] });
8571
+ queryClient.invalidateQueries({ queryKey: ["walletTokenBalance"] });
8572
+ }
8573
+ }
8574
+ ) : action === "buy" || buyOnly ? /* @__PURE__ */ jsxRuntime.jsx(
8575
+ BuyForm,
8576
+ {
8577
+ targetAddress: creditAccountAddress || "",
8578
+ onComplete: () => {
8579
+ queryClient.invalidateQueries({ queryKey: ["creditBalances"] });
8580
+ queryClient.invalidateQueries({ queryKey: ["walletTokenBalance"] });
8581
+ }
8582
+ }
8583
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8584
+ (isBusy || step === "confirmed") && action === "deposit" && /* @__PURE__ */ jsxRuntime.jsxs(
8585
+ "div",
8586
+ {
8587
+ className: "flex gap-3 p-3 text-xs",
8588
+ style: {
8589
+ backgroundColor: "var(--compass-color-surface)",
8590
+ borderRadius: "var(--compass-border-radius-lg)",
8591
+ fontFamily: "var(--compass-font-family)"
8592
+ },
8593
+ children: [
8594
+ /* @__PURE__ */ jsxRuntime.jsx(
8595
+ StepIndicator,
8596
+ {
8597
+ number: 1,
8598
+ label: "Approve",
8599
+ state: phase > 1 ? "done" : phase === 1 ? "active" : "pending"
8600
+ }
8601
+ ),
8602
+ /* @__PURE__ */ jsxRuntime.jsx(
8603
+ StepIndicator,
8604
+ {
8605
+ number: 2,
8606
+ label: "Transfer",
8607
+ state: step === "confirmed" ? "done" : phase > 1 ? "active" : "pending"
8608
+ }
8609
+ )
8610
+ ]
8611
+ }
8612
+ ),
8613
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8614
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm mb-1.5", style: labelStyle, children: "Amount" }),
8615
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
8050
8616
  /* @__PURE__ */ jsxRuntime.jsx(
8051
- StepIndicator,
8617
+ "input",
8052
8618
  {
8053
- number: 1,
8054
- label: "Approve",
8055
- state: phase > 1 ? "done" : phase === 1 ? "active" : "pending"
8619
+ type: "number",
8620
+ value: amount,
8621
+ onChange: (e) => {
8622
+ setAmount(e.target.value);
8623
+ setTxState({ status: "idle" });
8624
+ },
8625
+ disabled: isBusy,
8626
+ placeholder: "0.00",
8627
+ className: "w-full border px-3 py-2.5 pr-14 text-sm focus:outline-none disabled:opacity-50",
8628
+ style: inputStyle
8056
8629
  }
8057
8630
  ),
8058
8631
  /* @__PURE__ */ jsxRuntime.jsx(
8059
- StepIndicator,
8632
+ "span",
8060
8633
  {
8061
- number: 2,
8062
- label: "Transfer",
8063
- state: step === "confirmed" ? "done" : phase > 1 ? "active" : "pending"
8634
+ className: "absolute right-3 top-1/2 -translate-y-1/2 text-xs",
8635
+ style: { color: "var(--compass-color-text-tertiary)" },
8636
+ children: token
8064
8637
  }
8065
8638
  )
8066
- ]
8067
- }
8068
- ),
8069
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8070
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm mb-1.5", style: labelStyle, children: "Amount" }),
8071
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
8072
- /* @__PURE__ */ jsxRuntime.jsx(
8073
- "input",
8639
+ ] }),
8640
+ formatUsdEstimate(amount, token, prices) && /* @__PURE__ */ jsxRuntime.jsxs(
8641
+ "p",
8074
8642
  {
8075
- type: "number",
8076
- value: amount,
8077
- onChange: (e) => {
8078
- setAmount(e.target.value);
8079
- setTxState({ status: "idle" });
8080
- },
8081
- disabled: isBusy,
8082
- placeholder: "0.00",
8083
- className: "w-full border px-3 py-2.5 pr-14 text-sm focus:outline-none disabled:opacity-50",
8084
- style: inputStyle
8643
+ className: "text-xs mt-1",
8644
+ style: { color: "var(--compass-color-text-tertiary)" },
8645
+ children: [
8646
+ "\u2248",
8647
+ " ",
8648
+ formatUsdEstimate(amount, token, prices)
8649
+ ]
8085
8650
  }
8086
8651
  ),
8087
- /* @__PURE__ */ jsxRuntime.jsx(
8088
- "span",
8652
+ maxBalance && maxBalance !== "0" && /* @__PURE__ */ jsxRuntime.jsxs(
8653
+ "p",
8089
8654
  {
8090
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-xs",
8655
+ className: "text-xs mt-1",
8091
8656
  style: { color: "var(--compass-color-text-tertiary)" },
8092
- children: token
8657
+ children: [
8658
+ "Available: ",
8659
+ truncate6(parseFloat(maxBalance), 2),
8660
+ " ",
8661
+ token
8662
+ ]
8093
8663
  }
8094
8664
  )
8095
8665
  ] }),
8096
- formatUsdEstimate(amount, token, prices) && /* @__PURE__ */ jsxRuntime.jsxs(
8666
+ !isBusy && step !== "confirmed" && step !== "failed" && txState.status === "idle" && /* @__PURE__ */ jsxRuntime.jsx(
8097
8667
  "p",
8098
8668
  {
8099
- className: "text-xs mt-1",
8100
- style: { color: "var(--compass-color-text-tertiary)" },
8101
- children: [
8102
- "\u2248",
8103
- " ",
8104
- formatUsdEstimate(amount, token, prices)
8105
- ]
8669
+ className: "text-xs",
8670
+ style: {
8671
+ color: "var(--compass-color-text-tertiary)",
8672
+ fontFamily: "var(--compass-font-family)"
8673
+ },
8674
+ children: action === "deposit" ? `Approves the token transfer, then transfers tokens from your ${!hasExternalWallet ? "embedded " : ""}wallet into your credit account. Requires 2 signatures.${!hasExternalWallet ? " Note: deposit and withdraw use your embedded wallet, not your bank account." : ""}` : `Moves tokens from your credit account back to your ${!hasExternalWallet ? "embedded " : ""}wallet. Requires 1 signature.${!hasExternalWallet ? " Gas fees are sponsored." : ""}`
8106
8675
  }
8107
8676
  ),
8108
- maxBalance && maxBalance !== "0" && /* @__PURE__ */ jsxRuntime.jsxs(
8109
- "p",
8677
+ /* @__PURE__ */ jsxRuntime.jsx(
8678
+ "button",
8110
8679
  {
8111
- className: "text-xs mt-1",
8112
- style: { color: "var(--compass-color-text-tertiary)" },
8113
- children: [
8114
- "Available: ",
8115
- truncate6(parseFloat(maxBalance), 2),
8116
- " ",
8117
- token
8118
- ]
8680
+ onClick: handleTransfer,
8681
+ disabled: !isValid || isBusy,
8682
+ className: "w-full py-3 px-4 text-sm font-medium flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
8683
+ style: {
8684
+ backgroundColor: "var(--compass-color-primary)",
8685
+ color: "white",
8686
+ borderRadius: "var(--compass-border-radius-xl)",
8687
+ fontFamily: "var(--compass-font-family)",
8688
+ transition: "var(--compass-transition-normal)"
8689
+ },
8690
+ children: isBusy ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8691
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
8692
+ buttonLabel()
8693
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8694
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { className: "h-4 w-4" }),
8695
+ action === "deposit" ? "Transfer to Credit Account" : "Withdraw to Wallet"
8696
+ ] })
8119
8697
  }
8120
- )
8121
- ] }),
8122
- !isBusy && step !== "confirmed" && step !== "failed" && txState.status === "idle" && /* @__PURE__ */ jsxRuntime.jsx(
8123
- "p",
8124
- {
8125
- className: "text-xs",
8126
- style: {
8127
- color: "var(--compass-color-text-tertiary)",
8128
- fontFamily: "var(--compass-font-family)"
8129
- },
8130
- children: action === "deposit" ? "Approves the token transfer, then transfers tokens from your wallet into your credit account. Requires 2 signatures." : "Transfers tokens from your credit account back to your wallet. Requires 1 signature."
8131
- }
8132
- ),
8133
- /* @__PURE__ */ jsxRuntime.jsx(
8134
- "button",
8135
- {
8136
- onClick: handleTransfer,
8137
- disabled: !isValid || isBusy,
8138
- className: "w-full py-3 px-4 text-sm font-medium flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
8139
- style: {
8140
- backgroundColor: "var(--compass-color-primary)",
8141
- color: "white",
8142
- borderRadius: "var(--compass-border-radius-xl)",
8143
- fontFamily: "var(--compass-font-family)",
8144
- transition: "var(--compass-transition-normal)"
8145
- },
8146
- children: isBusy ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8147
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
8148
- buttonLabel()
8149
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8150
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { className: "h-4 w-4" }),
8151
- action === "deposit" ? "Transfer to Credit Account" : "Withdraw to Wallet"
8152
- ] })
8153
- }
8154
- ),
8155
- /* @__PURE__ */ jsxRuntime.jsx(TxStatus, { state: txState })
8698
+ ),
8699
+ /* @__PURE__ */ jsxRuntime.jsx(TxStatus, { state: txState })
8700
+ ] })
8156
8701
  ] }) });
8157
8702
  }
8158
8703
  function StepIndicator({
@@ -8180,9 +8725,10 @@ function CreditSwapModal({ isOpen, onClose }) {
8180
8725
  const prices = useTokenPrices();
8181
8726
  const { txState, executeBundle, resetState } = useCreditBundle();
8182
8727
  const tokens = CREDIT_TOKENS;
8183
- const fromToken = "USDC";
8184
- const [toToken, setToToken] = react.useState(tokens.find((t) => t !== fromToken) || tokens[0] || "WETH");
8728
+ const [fromToken, setFromToken] = react.useState("USDC");
8729
+ const [toToken, setToToken] = react.useState(tokens.find((t) => t !== "USDC") || tokens[0] || "WETH");
8185
8730
  const [fromAmount, setFromAmount] = react.useState("");
8731
+ const [isFromOpen, setIsFromOpen] = react.useState(false);
8186
8732
  const [isToOpen, setIsToOpen] = react.useState(false);
8187
8733
  react.useEffect(() => {
8188
8734
  if (isOpen) {
@@ -8190,6 +8736,12 @@ function CreditSwapModal({ isOpen, onClose }) {
8190
8736
  resetState();
8191
8737
  }
8192
8738
  }, [isOpen]);
8739
+ react.useEffect(() => {
8740
+ if (fromToken === toToken) {
8741
+ const alt = tokens.find((t) => t !== fromToken);
8742
+ if (alt) setToToken(alt);
8743
+ }
8744
+ }, [fromToken, toToken, tokens]);
8193
8745
  const fromBalance = balances?.find((b) => b.tokenSymbol === fromToken);
8194
8746
  const fromBalanceAmount = fromBalance ? parseFloat(fromBalance.amount) : 0;
8195
8747
  const estimatedOutput = (() => {
@@ -8268,24 +8820,76 @@ function CreditSwapModal({ isOpen, onClose }) {
8268
8820
  }
8269
8821
  }
8270
8822
  ),
8271
- /* @__PURE__ */ jsxRuntime.jsx(
8272
- "div",
8273
- {
8274
- style: {
8275
- display: "flex",
8276
- alignItems: "center",
8277
- fontWeight: 500,
8278
- backgroundColor: "var(--compass-color-background)",
8279
- border: "1.5px solid var(--compass-color-border)",
8280
- color: "var(--compass-color-text)",
8281
- borderRadius: "var(--compass-border-radius-md)",
8282
- padding: "4px 8px",
8283
- fontSize: "0.8125rem",
8284
- flexShrink: 0
8285
- },
8286
- children: fromToken
8287
- }
8288
- )
8823
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", flexShrink: 0 }, children: [
8824
+ /* @__PURE__ */ jsxRuntime.jsxs(
8825
+ "button",
8826
+ {
8827
+ onClick: () => setIsFromOpen(!isFromOpen),
8828
+ disabled: isBusy,
8829
+ style: {
8830
+ display: "flex",
8831
+ alignItems: "center",
8832
+ fontWeight: 500,
8833
+ backgroundColor: "var(--compass-color-background)",
8834
+ border: "1.5px solid var(--compass-color-border)",
8835
+ color: "var(--compass-color-text)",
8836
+ borderRadius: "var(--compass-border-radius-md)",
8837
+ padding: "4px 8px",
8838
+ gap: "4px",
8839
+ fontSize: "0.8125rem",
8840
+ cursor: isBusy ? "not-allowed" : "pointer"
8841
+ },
8842
+ children: [
8843
+ fromToken,
8844
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 12, style: { color: "var(--compass-color-text-tertiary)" } })
8845
+ ]
8846
+ }
8847
+ ),
8848
+ isFromOpen && /* @__PURE__ */ jsxRuntime.jsx(
8849
+ "div",
8850
+ {
8851
+ style: {
8852
+ position: "absolute",
8853
+ right: 0,
8854
+ top: "100%",
8855
+ marginTop: "4px",
8856
+ zIndex: 10,
8857
+ backgroundColor: "var(--compass-color-surface)",
8858
+ border: "1.5px solid var(--compass-color-border)",
8859
+ borderRadius: "var(--compass-border-radius-lg)",
8860
+ boxShadow: "var(--compass-shadow-md)",
8861
+ minWidth: "120px",
8862
+ maxHeight: "200px",
8863
+ overflowY: "auto",
8864
+ scrollbarWidth: "none"
8865
+ },
8866
+ children: tokens.filter((t) => t !== toToken).map((token) => /* @__PURE__ */ jsxRuntime.jsx(
8867
+ "button",
8868
+ {
8869
+ onClick: () => {
8870
+ setFromToken(token);
8871
+ setFromAmount("");
8872
+ setIsFromOpen(false);
8873
+ },
8874
+ style: {
8875
+ display: "block",
8876
+ width: "100%",
8877
+ textAlign: "left",
8878
+ padding: "6px 10px",
8879
+ fontSize: "0.8125rem",
8880
+ fontWeight: 500,
8881
+ color: token === fromToken ? "var(--compass-color-primary)" : "var(--compass-color-text)",
8882
+ backgroundColor: token === fromToken ? "var(--compass-color-primary-muted)" : "transparent",
8883
+ border: "none",
8884
+ cursor: "pointer"
8885
+ },
8886
+ children: token
8887
+ },
8888
+ token
8889
+ ))
8890
+ }
8891
+ )
8892
+ ] })
8289
8893
  ] })
8290
8894
  ]
8291
8895
  }
@@ -8492,7 +9096,7 @@ function CreditAccount({
8492
9096
  onBorrow: _onBorrow,
8493
9097
  onRepay: _onRepay
8494
9098
  }) {
8495
- const { address, isConnected, login, logout } = useEmbeddableWallet();
9099
+ const { address, isConnected, login, logout, hasExternalWallet } = useEmbeddableWallet();
8496
9100
  const { isDeployed, creditAccountAddress } = useCreditAccount();
8497
9101
  const { chainId } = useChain();
8498
9102
  const { data: positions, isLoading: positionsLoading } = useCreditPositions();
@@ -8518,6 +9122,25 @@ function CreditAccount({
8518
9122
  return { ...b, usdValue };
8519
9123
  }).filter((b) => b.usdValue >= 0.01).sort((a, b) => a.tokenSymbol.localeCompare(b.tokenSymbol));
8520
9124
  const totalIdleUsd = balancesWithUsd.reduce((sum, b) => sum + b.usdValue, 0);
9125
+ const { data: walletBalance } = reactQuery.useQuery({
9126
+ queryKey: ["embeddedWalletBalance", chainId, address, "USDC"],
9127
+ queryFn: async () => {
9128
+ if (!address) return "0";
9129
+ try {
9130
+ const response = await fetch(
9131
+ `/api/compass/token/balance?chain=${chainId}&token=USDC&address=${address}`
9132
+ );
9133
+ if (response.ok) {
9134
+ const data = await response.json();
9135
+ return data.balance || "0";
9136
+ }
9137
+ } catch {
9138
+ }
9139
+ return "0";
9140
+ },
9141
+ enabled: !!address && hasExternalWallet,
9142
+ staleTime: 30 * 1e3
9143
+ });
8521
9144
  const totalInterestEarnedUsd = (() => {
8522
9145
  if (!positions) return 0;
8523
9146
  let total = 0;
@@ -9518,6 +10141,7 @@ function PositionDetailModal({ position, onClose }) {
9518
10141
  const isPendle = position.venue === "pendle";
9519
10142
  const duration = formatDuration(position.entryTimestamp);
9520
10143
  const expiryDate = formatExpiryUTC(position.expiry);
10144
+ const modalRef = react.useRef(null);
9521
10145
  react.useEffect(() => {
9522
10146
  const handleKeyDown = (e) => {
9523
10147
  if (e.key === "Escape") onClose();
@@ -9525,296 +10149,312 @@ function PositionDetailModal({ position, onClose }) {
9525
10149
  document.addEventListener("keydown", handleKeyDown);
9526
10150
  return () => document.removeEventListener("keydown", handleKeyDown);
9527
10151
  }, [onClose]);
10152
+ const handleOverlayWheel = react.useCallback((e) => {
10153
+ if (modalRef.current && !modalRef.current.contains(e.target)) {
10154
+ window.scrollBy(0, e.deltaY);
10155
+ }
10156
+ }, []);
9528
10157
  const hasStats = position.apy || isPendle && (duration || expiryDate) || position.pnl;
9529
- return /* @__PURE__ */ jsxRuntime.jsx(
10158
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9530
10159
  "div",
9531
10160
  {
9532
10161
  className: "fixed inset-0 z-50 flex items-center justify-center p-4",
9533
- style: { backgroundColor: "rgba(0, 0, 0, 0.7)" },
10162
+ onWheel: handleOverlayWheel,
9534
10163
  onClick: onClose,
9535
- children: /* @__PURE__ */ jsxRuntime.jsxs(
9536
- "div",
9537
- {
9538
- className: "relative w-full max-w-md rounded-xl border overflow-hidden max-h-[90vh] overflow-y-auto",
9539
- style: {
9540
- backgroundColor: "var(--compass-color-background)",
9541
- borderColor: "var(--compass-color-border)",
9542
- scrollbarWidth: "none"
9543
- },
9544
- onClick: (e) => e.stopPropagation(),
9545
- children: [
9546
- /* @__PURE__ */ jsxRuntime.jsxs(
9547
- "div",
9548
- {
9549
- className: "sticky top-0 flex items-center justify-between p-4 border-b",
9550
- style: {
9551
- backgroundColor: "var(--compass-color-surface)",
9552
- borderColor: "var(--compass-color-border)"
9553
- },
9554
- children: [
9555
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
9556
- /* @__PURE__ */ jsxRuntime.jsx(
9557
- "h2",
9558
- {
9559
- className: "font-semibold text-lg",
9560
- style: { color: "var(--compass-color-text)" },
9561
- children: position.name
9562
- }
9563
- ),
9564
- /* @__PURE__ */ jsxRuntime.jsx(
9565
- "p",
9566
- {
9567
- className: "text-sm",
9568
- style: { color: "var(--compass-color-text-secondary)" },
9569
- children: position.venueName
9570
- }
9571
- )
9572
- ] }),
9573
- /* @__PURE__ */ jsxRuntime.jsx(
9574
- "button",
9575
- {
9576
- onClick: onClose,
9577
- className: "p-2 rounded-lg hover:opacity-80 transition-opacity",
9578
- style: { backgroundColor: "var(--compass-color-background)" },
9579
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 20, style: { color: "var(--compass-color-text-secondary)" } })
9580
- }
9581
- )
9582
- ]
9583
- }
9584
- ),
9585
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 border-b", style: { borderColor: "var(--compass-color-border)" }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
9586
- /* @__PURE__ */ jsxRuntime.jsx(
9587
- "p",
9588
- {
9589
- className: "text-sm mb-1",
9590
- style: { color: "var(--compass-color-text-secondary)" },
9591
- children: "Current Balance"
9592
- }
9593
- ),
9594
- /* @__PURE__ */ jsxRuntime.jsx(
9595
- "p",
9596
- {
9597
- className: "text-3xl font-bold",
9598
- style: { color: "var(--compass-color-text)" },
9599
- children: formatUSD(position.balanceUsd)
9600
- }
9601
- ),
10164
+ children: [
10165
+ /* @__PURE__ */ jsxRuntime.jsx(
10166
+ "div",
10167
+ {
10168
+ className: "absolute inset-0",
10169
+ style: { backgroundColor: "rgba(0, 0, 0, 0.7)" }
10170
+ }
10171
+ ),
10172
+ /* @__PURE__ */ jsxRuntime.jsxs(
10173
+ "div",
10174
+ {
10175
+ ref: modalRef,
10176
+ className: "relative w-full max-w-md rounded-xl border max-h-[85vh] overflow-y-auto",
10177
+ style: {
10178
+ backgroundColor: "var(--compass-color-background)",
10179
+ borderColor: "var(--compass-color-border)",
10180
+ scrollbarWidth: "none",
10181
+ overscrollBehavior: "contain"
10182
+ },
10183
+ onClick: (e) => e.stopPropagation(),
10184
+ children: [
9602
10185
  /* @__PURE__ */ jsxRuntime.jsxs(
9603
- "p",
9604
- {
9605
- className: "text-sm font-mono mt-1",
9606
- style: { color: "var(--compass-color-text-secondary)" },
9607
- children: [
9608
- formatAmount(position.balance),
9609
- " ",
9610
- position.assetSymbol
9611
- ]
9612
- }
9613
- )
9614
- ] }) }),
9615
- hasStats && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3 p-4 border-b", style: { borderColor: "var(--compass-color-border)" }, children: [
9616
- position.apy && /* @__PURE__ */ jsxRuntime.jsxs(
9617
- "div",
9618
- {
9619
- className: "p-3 rounded-lg",
9620
- style: { backgroundColor: "var(--compass-color-surface)" },
9621
- children: [
9622
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
9623
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Percent, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
9624
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "APY" })
9625
- ] }),
9626
- /* @__PURE__ */ jsxRuntime.jsxs(
9627
- "p",
9628
- {
9629
- className: "font-semibold",
9630
- style: { color: "var(--compass-color-success)" },
9631
- children: [
9632
- parseFloat(position.apy).toFixed(2),
9633
- "%"
9634
- ]
9635
- }
9636
- )
9637
- ]
9638
- }
9639
- ),
9640
- isPendle && duration && /* @__PURE__ */ jsxRuntime.jsxs(
9641
- "div",
9642
- {
9643
- className: "p-3 rounded-lg",
9644
- style: { backgroundColor: "var(--compass-color-surface)" },
9645
- children: [
9646
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
9647
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
9648
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Duration" })
9649
- ] }),
9650
- /* @__PURE__ */ jsxRuntime.jsx(
9651
- "p",
9652
- {
9653
- className: "font-semibold",
9654
- style: { color: "var(--compass-color-text)" },
9655
- children: duration
9656
- }
9657
- )
9658
- ]
9659
- }
9660
- ),
9661
- isPendle && expiryDate && /* @__PURE__ */ jsxRuntime.jsxs(
9662
10186
  "div",
9663
10187
  {
9664
- className: "p-3 rounded-lg",
9665
- style: { backgroundColor: "var(--compass-color-surface)" },
10188
+ className: "sticky top-0 flex items-center justify-between p-4 border-b",
10189
+ style: {
10190
+ backgroundColor: "var(--compass-color-surface)",
10191
+ borderColor: "var(--compass-color-border)"
10192
+ },
9666
10193
  children: [
9667
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
9668
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Calendar, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
9669
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Expiry (UTC)" })
10194
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
10195
+ /* @__PURE__ */ jsxRuntime.jsx(
10196
+ "h2",
10197
+ {
10198
+ className: "font-semibold text-lg",
10199
+ style: { color: "var(--compass-color-text)" },
10200
+ children: position.name
10201
+ }
10202
+ ),
10203
+ /* @__PURE__ */ jsxRuntime.jsx(
10204
+ "p",
10205
+ {
10206
+ className: "text-sm",
10207
+ style: { color: "var(--compass-color-text-secondary)" },
10208
+ children: position.venueName
10209
+ }
10210
+ )
9670
10211
  ] }),
9671
10212
  /* @__PURE__ */ jsxRuntime.jsx(
9672
- "p",
10213
+ "button",
9673
10214
  {
9674
- className: "font-semibold",
9675
- style: { color: "var(--compass-color-text)" },
9676
- children: expiryDate
10215
+ onClick: onClose,
10216
+ className: "p-2 rounded-lg hover:opacity-80 transition-opacity",
10217
+ style: { backgroundColor: "var(--compass-color-background)" },
10218
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 20, style: { color: "var(--compass-color-text-secondary)" } })
9677
10219
  }
9678
10220
  )
9679
10221
  ]
9680
10222
  }
9681
10223
  ),
9682
- position.pnl && /* @__PURE__ */ jsxRuntime.jsxs(
9683
- "div",
9684
- {
9685
- className: "p-3 rounded-lg",
9686
- style: { backgroundColor: "var(--compass-color-surface)" },
9687
- children: [
9688
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
9689
- isPnlPositive ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { size: 14, style: { color: "var(--compass-color-success)" } }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingDown, { size: 14, style: { color: "var(--compass-color-error)" } }),
9690
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Total P&L" })
9691
- ] }),
9692
- /* @__PURE__ */ jsxRuntime.jsxs(
9693
- "p",
10224
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 border-b", style: { borderColor: "var(--compass-color-border)" }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
10225
+ /* @__PURE__ */ jsxRuntime.jsx(
10226
+ "p",
10227
+ {
10228
+ className: "text-sm mb-1",
10229
+ style: { color: "var(--compass-color-text-secondary)" },
10230
+ children: "Current Balance"
10231
+ }
10232
+ ),
10233
+ /* @__PURE__ */ jsxRuntime.jsx(
10234
+ "p",
10235
+ {
10236
+ className: "text-3xl font-bold",
10237
+ style: { color: "var(--compass-color-text)" },
10238
+ children: formatUSD(position.balanceUsd)
10239
+ }
10240
+ ),
10241
+ /* @__PURE__ */ jsxRuntime.jsxs(
10242
+ "p",
10243
+ {
10244
+ className: "text-sm font-mono mt-1",
10245
+ style: { color: "var(--compass-color-text-secondary)" },
10246
+ children: [
10247
+ formatAmount(position.balance),
10248
+ " ",
10249
+ position.assetSymbol
10250
+ ]
10251
+ }
10252
+ )
10253
+ ] }) }),
10254
+ hasStats && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3 p-4 border-b", style: { borderColor: "var(--compass-color-border)" }, children: [
10255
+ position.apy && /* @__PURE__ */ jsxRuntime.jsxs(
10256
+ "div",
10257
+ {
10258
+ className: "p-3 rounded-lg",
10259
+ style: { backgroundColor: "var(--compass-color-surface)" },
10260
+ children: [
10261
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
10262
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Percent, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
10263
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "APY" })
10264
+ ] }),
10265
+ /* @__PURE__ */ jsxRuntime.jsxs(
10266
+ "p",
10267
+ {
10268
+ className: "font-semibold",
10269
+ style: { color: "var(--compass-color-success)" },
10270
+ children: [
10271
+ parseFloat(position.apy).toFixed(2),
10272
+ "%"
10273
+ ]
10274
+ }
10275
+ )
10276
+ ]
10277
+ }
10278
+ ),
10279
+ isPendle && duration && /* @__PURE__ */ jsxRuntime.jsxs(
10280
+ "div",
10281
+ {
10282
+ className: "p-3 rounded-lg",
10283
+ style: { backgroundColor: "var(--compass-color-surface)" },
10284
+ children: [
10285
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
10286
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
10287
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Duration" })
10288
+ ] }),
10289
+ /* @__PURE__ */ jsxRuntime.jsx(
10290
+ "p",
10291
+ {
10292
+ className: "font-semibold",
10293
+ style: { color: "var(--compass-color-text)" },
10294
+ children: duration
10295
+ }
10296
+ )
10297
+ ]
10298
+ }
10299
+ ),
10300
+ isPendle && expiryDate && /* @__PURE__ */ jsxRuntime.jsxs(
10301
+ "div",
10302
+ {
10303
+ className: "p-3 rounded-lg",
10304
+ style: { backgroundColor: "var(--compass-color-surface)" },
10305
+ children: [
10306
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
10307
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Calendar, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
10308
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Expiry (UTC)" })
10309
+ ] }),
10310
+ /* @__PURE__ */ jsxRuntime.jsx(
10311
+ "p",
10312
+ {
10313
+ className: "font-semibold",
10314
+ style: { color: "var(--compass-color-text)" },
10315
+ children: expiryDate
10316
+ }
10317
+ )
10318
+ ]
10319
+ }
10320
+ ),
10321
+ position.pnl && /* @__PURE__ */ jsxRuntime.jsxs(
10322
+ "div",
10323
+ {
10324
+ className: "p-3 rounded-lg",
10325
+ style: { backgroundColor: "var(--compass-color-surface)" },
10326
+ children: [
10327
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
10328
+ isPnlPositive ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { size: 14, style: { color: "var(--compass-color-success)" } }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingDown, { size: 14, style: { color: "var(--compass-color-error)" } }),
10329
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Total P&L" })
10330
+ ] }),
10331
+ /* @__PURE__ */ jsxRuntime.jsxs(
10332
+ "p",
10333
+ {
10334
+ className: "font-semibold",
10335
+ style: {
10336
+ color: isPnlPositive ? "var(--compass-color-success)" : "var(--compass-color-error)"
10337
+ },
10338
+ children: [
10339
+ isPnlPositive ? "+" : "",
10340
+ formatUSD(position.pnl.totalPnl)
10341
+ ]
10342
+ }
10343
+ )
10344
+ ]
10345
+ }
10346
+ )
10347
+ ] }),
10348
+ (position.deposits.length > 0 || position.withdrawals.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
10349
+ /* @__PURE__ */ jsxRuntime.jsx(
10350
+ "h3",
10351
+ {
10352
+ className: "font-semibold mb-3",
10353
+ style: { color: "var(--compass-color-text)" },
10354
+ children: "Transaction History"
10355
+ }
10356
+ ),
10357
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
10358
+ position.deposits.map((tx, i) => {
10359
+ const date = formatDate(tx.timestamp);
10360
+ return /* @__PURE__ */ jsxRuntime.jsxs(
10361
+ "div",
9694
10362
  {
9695
- className: "font-semibold",
9696
- style: {
9697
- color: isPnlPositive ? "var(--compass-color-success)" : "var(--compass-color-error)"
9698
- },
10363
+ className: "flex items-center justify-between p-3 rounded-lg",
10364
+ style: { backgroundColor: "var(--compass-color-surface)" },
9699
10365
  children: [
9700
- isPnlPositive ? "+" : "",
9701
- formatUSD(position.pnl.totalPnl)
9702
- ]
9703
- }
9704
- )
9705
- ]
9706
- }
9707
- )
9708
- ] }),
9709
- (position.deposits.length > 0 || position.withdrawals.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
9710
- /* @__PURE__ */ jsxRuntime.jsx(
9711
- "h3",
9712
- {
9713
- className: "font-semibold mb-3",
9714
- style: { color: "var(--compass-color-text)" },
9715
- children: "Transaction History"
9716
- }
9717
- ),
9718
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
9719
- position.deposits.map((tx, i) => {
9720
- const date = formatDate(tx.timestamp);
9721
- return /* @__PURE__ */ jsxRuntime.jsxs(
9722
- "div",
9723
- {
9724
- className: "flex items-center justify-between p-3 rounded-lg",
9725
- style: { backgroundColor: "var(--compass-color-surface)" },
9726
- children: [
9727
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
9728
- /* @__PURE__ */ jsxRuntime.jsxs(
9729
- "p",
9730
- {
9731
- className: "font-medium",
9732
- style: { color: "var(--compass-color-success)" },
9733
- children: [
9734
- "+",
9735
- formatAmount(tx.amount),
9736
- " ",
9737
- position.assetSymbol
9738
- ]
9739
- }
9740
- ),
9741
- /* @__PURE__ */ jsxRuntime.jsx(
9742
- "p",
10366
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
10367
+ /* @__PURE__ */ jsxRuntime.jsxs(
10368
+ "p",
10369
+ {
10370
+ className: "font-medium",
10371
+ style: { color: "var(--compass-color-success)" },
10372
+ children: [
10373
+ "+",
10374
+ formatAmount(tx.amount),
10375
+ " ",
10376
+ position.assetSymbol
10377
+ ]
10378
+ }
10379
+ ),
10380
+ /* @__PURE__ */ jsxRuntime.jsx(
10381
+ "p",
10382
+ {
10383
+ className: "text-xs",
10384
+ style: { color: "var(--compass-color-text-tertiary)" },
10385
+ children: date ? `Deposit - ${date}` : "Deposit"
10386
+ }
10387
+ )
10388
+ ] }),
10389
+ tx.txHash && /* @__PURE__ */ jsxRuntime.jsx(
10390
+ "a",
9743
10391
  {
9744
- className: "text-xs",
9745
- style: { color: "var(--compass-color-text-tertiary)" },
9746
- children: date ? `Deposit - ${date}` : "Deposit"
10392
+ href: `https://etherscan.io/tx/${tx.txHash}`,
10393
+ target: "_blank",
10394
+ rel: "noopener noreferrer",
10395
+ className: "p-2 rounded-lg hover:opacity-80 transition-opacity",
10396
+ style: { backgroundColor: "var(--compass-color-background)" },
10397
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
9747
10398
  }
9748
10399
  )
9749
- ] }),
9750
- tx.txHash && /* @__PURE__ */ jsxRuntime.jsx(
9751
- "a",
9752
- {
9753
- href: `https://etherscan.io/tx/${tx.txHash}`,
9754
- target: "_blank",
9755
- rel: "noopener noreferrer",
9756
- className: "p-2 rounded-lg hover:opacity-80 transition-opacity",
9757
- style: { backgroundColor: "var(--compass-color-background)" },
9758
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
9759
- }
9760
- )
9761
- ]
9762
- },
9763
- `deposit-${i}`
9764
- );
9765
- }),
9766
- position.withdrawals.map((tx, i) => {
9767
- const date = formatDate(tx.timestamp);
9768
- return /* @__PURE__ */ jsxRuntime.jsxs(
9769
- "div",
9770
- {
9771
- className: "flex items-center justify-between p-3 rounded-lg",
9772
- style: { backgroundColor: "var(--compass-color-surface)" },
9773
- children: [
9774
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
9775
- /* @__PURE__ */ jsxRuntime.jsxs(
9776
- "p",
9777
- {
9778
- className: "font-medium",
9779
- style: { color: "var(--compass-color-error)" },
9780
- children: [
9781
- "-",
9782
- formatAmount(tx.amount),
9783
- " ",
9784
- position.assetSymbol
9785
- ]
9786
- }
9787
- ),
9788
- /* @__PURE__ */ jsxRuntime.jsx(
9789
- "p",
10400
+ ]
10401
+ },
10402
+ `deposit-${i}`
10403
+ );
10404
+ }),
10405
+ position.withdrawals.map((tx, i) => {
10406
+ const date = formatDate(tx.timestamp);
10407
+ return /* @__PURE__ */ jsxRuntime.jsxs(
10408
+ "div",
10409
+ {
10410
+ className: "flex items-center justify-between p-3 rounded-lg",
10411
+ style: { backgroundColor: "var(--compass-color-surface)" },
10412
+ children: [
10413
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
10414
+ /* @__PURE__ */ jsxRuntime.jsxs(
10415
+ "p",
10416
+ {
10417
+ className: "font-medium",
10418
+ style: { color: "var(--compass-color-error)" },
10419
+ children: [
10420
+ "-",
10421
+ formatAmount(tx.amount),
10422
+ " ",
10423
+ position.assetSymbol
10424
+ ]
10425
+ }
10426
+ ),
10427
+ /* @__PURE__ */ jsxRuntime.jsx(
10428
+ "p",
10429
+ {
10430
+ className: "text-xs",
10431
+ style: { color: "var(--compass-color-text-tertiary)" },
10432
+ children: date ? `Withdrawal - ${date}` : "Withdrawal"
10433
+ }
10434
+ )
10435
+ ] }),
10436
+ tx.txHash && /* @__PURE__ */ jsxRuntime.jsx(
10437
+ "a",
9790
10438
  {
9791
- className: "text-xs",
9792
- style: { color: "var(--compass-color-text-tertiary)" },
9793
- children: date ? `Withdrawal - ${date}` : "Withdrawal"
10439
+ href: `https://etherscan.io/tx/${tx.txHash}`,
10440
+ target: "_blank",
10441
+ rel: "noopener noreferrer",
10442
+ className: "p-2 rounded-lg hover:opacity-80 transition-opacity",
10443
+ style: { backgroundColor: "var(--compass-color-background)" },
10444
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
9794
10445
  }
9795
10446
  )
9796
- ] }),
9797
- tx.txHash && /* @__PURE__ */ jsxRuntime.jsx(
9798
- "a",
9799
- {
9800
- href: `https://etherscan.io/tx/${tx.txHash}`,
9801
- target: "_blank",
9802
- rel: "noopener noreferrer",
9803
- className: "p-2 rounded-lg hover:opacity-80 transition-opacity",
9804
- style: { backgroundColor: "var(--compass-color-background)" },
9805
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
9806
- }
9807
- )
9808
- ]
9809
- },
9810
- `withdraw-${i}`
9811
- );
9812
- })
10447
+ ]
10448
+ },
10449
+ `withdraw-${i}`
10450
+ );
10451
+ })
10452
+ ] })
9813
10453
  ] })
9814
- ] })
9815
- ]
9816
- }
9817
- )
10454
+ ]
10455
+ }
10456
+ )
10457
+ ]
9818
10458
  }
9819
10459
  );
9820
10460
  }
@@ -10583,7 +11223,7 @@ function AllocationEditor({
10583
11223
  ) })
10584
11224
  ] });
10585
11225
  }
10586
- var EVM_CHAIN_IDS3 = {
11226
+ var EVM_CHAIN_IDS4 = {
10587
11227
  ethereum: 1,
10588
11228
  base: 8453,
10589
11229
  arbitrum: 42161
@@ -10617,7 +11257,7 @@ function RebalancingWidget({
10617
11257
  }) {
10618
11258
  const { chainId: contextChainId, setChainId } = useChain();
10619
11259
  const CHAIN_ID = chain || contextChainId;
10620
- const { address, signTypedData, isConnected, login, logout, switchChain, walletChainId } = useEmbeddableWallet();
11260
+ const { address, signTypedData, isConnected, login, logout, switchChain, walletChainId, fundWallet, hasExternalWallet } = useEmbeddableWallet();
10621
11261
  const { earnAccountAddress } = useEarnAccount();
10622
11262
  const queryClient = reactQuery.useQueryClient();
10623
11263
  const { portfolio, earnAccountMarkets, isMarketsLoading, isLoading, isError, error, refetch } = useRebalancingData(chain);
@@ -10666,10 +11306,11 @@ function RebalancingWidget({
10666
11306
  const [selectedToken, setSelectedToken] = react.useState("USDC");
10667
11307
  const [depositAmount, setDepositAmount] = react.useState("");
10668
11308
  const [isTokenDropdownOpen, setIsTokenDropdownOpen] = react.useState(false);
10669
- const [isProcessing, setIsProcessing] = react.useState(false);
10670
- const [depositError, setDepositError] = react.useState(null);
10671
- const [depositStatus, setDepositStatus] = react.useState("");
11309
+ const [depositTxState, setDepositTxState] = react.useState({ status: "idle" });
10672
11310
  const earnBalanceRef = react.useRef(null);
11311
+ const { startPolling: startDepositPolling, clearPolling: clearDepositPolling } = useTxPolling({
11312
+ queryKeysToInvalidate: [["earnAccountBalances"], ["rebalancing"]]
11313
+ });
10673
11314
  react.useEffect(() => {
10674
11315
  setTargets([]);
10675
11316
  setOriginalAllocations({});
@@ -10684,9 +11325,9 @@ function RebalancingWidget({
10684
11325
  setIsSwapModalOpen(false);
10685
11326
  setSelectedMarket(null);
10686
11327
  setDepositAmount("");
10687
- setDepositError(null);
10688
- setDepositStatus("");
10689
- }, [CHAIN_ID]);
11328
+ setDepositTxState({ status: "idle" });
11329
+ clearDepositPolling();
11330
+ }, [CHAIN_ID, clearDepositPolling]);
10690
11331
  react.useEffect(() => {
10691
11332
  if (portfolio && portfolio.positions.length > 0 && !hasInitializedTargets && !isLoading) {
10692
11333
  const origAllocs = {};
@@ -10767,7 +11408,7 @@ function RebalancingWidget({
10767
11408
  setTargets((prev) => prev.map((t, i) => i === index ? { ...t, targetPercent: rounded } : t));
10768
11409
  }, []);
10769
11410
  const ensureCorrectChain = react.useCallback(async () => {
10770
- const targetChainId = EVM_CHAIN_IDS3[CHAIN_ID];
11411
+ const targetChainId = EVM_CHAIN_IDS4[CHAIN_ID];
10771
11412
  if (!targetChainId) return;
10772
11413
  if (walletChainId !== void 0 && walletChainId !== targetChainId) {
10773
11414
  if (!switchChain) {
@@ -10899,13 +11540,11 @@ function RebalancingWidget({
10899
11540
  };
10900
11541
  const handleDeposit = react.useCallback(async () => {
10901
11542
  if (!selectedMarket || !depositAmount || parseFloat(depositAmount) <= 0 || !address || !signTypedData) return;
10902
- setIsProcessing(true);
10903
- setDepositError(null);
11543
+ setDepositTxState({ status: "preparing" });
10904
11544
  const underlyingToken = selectedMarket.underlyingToken;
10905
11545
  try {
10906
11546
  let resultTxHash;
10907
11547
  if (needsSwap) {
10908
- setDepositStatus("Getting swap quote...");
10909
11548
  const quoteResponse = await fetch(
10910
11549
  `/api/compass/swap/quote?owner=${address}&chain=${CHAIN_ID}&tokenIn=${selectedToken}&tokenOut=${underlyingToken}&amountIn=${depositAmount}`
10911
11550
  );
@@ -10919,7 +11558,6 @@ function RebalancingWidget({
10919
11558
  throw new Error("Invalid swap quote - no output amount");
10920
11559
  }
10921
11560
  const actualDepositAmount = (parseFloat(estimatedOutput) * 0.99999).toString();
10922
- setDepositStatus("Preparing swap and deposit...");
10923
11561
  const bundleActions = [
10924
11562
  {
10925
11563
  body: {
@@ -10953,7 +11591,7 @@ function RebalancingWidget({
10953
11591
  throw new Error(errorData.error || "Failed to prepare bundle");
10954
11592
  }
10955
11593
  const { eip712, normalizedTypes, domain, message } = await prepareResponse.json();
10956
- setDepositStatus("Please sign the transaction...");
11594
+ setDepositTxState({ status: "signing" });
10957
11595
  await ensureCorrectChain();
10958
11596
  const signature = await signTypedData({
10959
11597
  domain,
@@ -10961,7 +11599,7 @@ function RebalancingWidget({
10961
11599
  primaryType: "SafeTx",
10962
11600
  message
10963
11601
  });
10964
- setDepositStatus("Executing swap and deposit...");
11602
+ setDepositTxState({ status: "broadcasting" });
10965
11603
  const executeResponse = await fetch("/api/compass/bundle/execute", {
10966
11604
  method: "POST",
10967
11605
  headers: { "Content-Type": "application/json" },
@@ -10979,7 +11617,6 @@ function RebalancingWidget({
10979
11617
  const result = await executeResponse.json();
10980
11618
  resultTxHash = result.txHash;
10981
11619
  } else {
10982
- setDepositStatus("Preparing deposit...");
10983
11620
  const venueParams = buildVenueParamsFromMarket(selectedMarket);
10984
11621
  const prepareResponse = await fetch("/api/compass/deposit/prepare", {
10985
11622
  method: "POST",
@@ -10996,7 +11633,7 @@ function RebalancingWidget({
10996
11633
  throw new Error(errData.error || "Failed to prepare deposit");
10997
11634
  }
10998
11635
  const prepareData = await prepareResponse.json();
10999
- setDepositStatus("Please sign the transaction...");
11636
+ setDepositTxState({ status: "signing" });
11000
11637
  await ensureCorrectChain();
11001
11638
  const signature = await signTypedData({
11002
11639
  domain: prepareData.domain,
@@ -11004,7 +11641,7 @@ function RebalancingWidget({
11004
11641
  primaryType: "SafeTx",
11005
11642
  message: prepareData.message
11006
11643
  });
11007
- setDepositStatus("Executing deposit...");
11644
+ setDepositTxState({ status: "broadcasting" });
11008
11645
  const executeResponse = await fetch("/api/compass/deposit/execute", {
11009
11646
  method: "POST",
11010
11647
  headers: { "Content-Type": "application/json" },
@@ -11022,7 +11659,8 @@ function RebalancingWidget({
11022
11659
  const result = await executeResponse.json();
11023
11660
  resultTxHash = result.txHash;
11024
11661
  }
11025
- setDepositStatus("Deposit successful!");
11662
+ setDepositTxState({ status: "submitted", txHash: resultTxHash });
11663
+ startDepositPolling(resultTxHash, setDepositTxState);
11026
11664
  setDepositAmount("");
11027
11665
  queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
11028
11666
  queryClient.invalidateQueries({ queryKey: ["rebalancing"] });
@@ -11034,15 +11672,11 @@ function RebalancingWidget({
11034
11672
  refetch();
11035
11673
  }, delay);
11036
11674
  }
11037
- setTimeout(() => setDepositStatus(""), 3e3);
11038
11675
  } catch (err) {
11039
- setDepositError(err instanceof Error ? err.message : "Deposit failed");
11040
- setDepositStatus("");
11676
+ setDepositTxState({ status: "failed", error: err instanceof Error ? err.message : "Deposit failed" });
11041
11677
  onError?.(err instanceof Error ? err : new Error("Deposit failed"));
11042
- } finally {
11043
- setIsProcessing(false);
11044
11678
  }
11045
- }, [selectedMarket, depositAmount, address, signTypedData, selectedToken, needsSwap, CHAIN_ID, ensureCorrectChain, queryClient, refetch, onError]);
11679
+ }, [selectedMarket, depositAmount, address, signTypedData, selectedToken, needsSwap, CHAIN_ID, ensureCorrectChain, queryClient, refetch, onError, startDepositPolling]);
11046
11680
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col w-full", style: { gap: "0", fontFamily: "var(--compass-font-family)", height, overflow: "hidden", borderRadius: "var(--compass-border-radius-xl)" }, children: [
11047
11681
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minHeight: 0, overflowY: "auto", scrollbarWidth: "none", display: "flex", flexDirection: "column" }, children: [
11048
11682
  showChainSwitcher && !chain && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "0 4px", marginBottom: "8px" }, children: /* @__PURE__ */ jsxRuntime.jsx(ChainSwitcher, {}) }),
@@ -11071,8 +11705,19 @@ function RebalancingWidget({
11071
11705
  }
11072
11706
  )
11073
11707
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11074
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg font-semibold mb-2", style: { color: "var(--compass-color-text)" }, children: "No positions found" }),
11075
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm", style: { color: "var(--compass-color-text-secondary)" }, children: "Deposit into a vault, Aave market, or Pendle market to start rebalancing." })
11708
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg font-semibold mb-2", style: { color: "var(--compass-color-text)" }, children: fundWallet && !hasExternalWallet ? "Fund your account to start" : "No positions found" }),
11709
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mb-4", style: { color: "var(--compass-color-text-secondary)" }, children: fundWallet && !hasExternalWallet ? "Buy crypto to get started with your DeFi portfolio." : "Deposit into a vault, Aave market, or Pendle market to start rebalancing." }),
11710
+ fundWallet && earnAccountAddress && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: "100%", maxWidth: "320px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
11711
+ BuyForm,
11712
+ {
11713
+ targetAddress: earnAccountAddress,
11714
+ onComplete: () => {
11715
+ queryClient.invalidateQueries({ queryKey: ["rebalancingPortfolio"] });
11716
+ queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
11717
+ refetch();
11718
+ }
11719
+ }
11720
+ ) })
11076
11721
  ] }) }),
11077
11722
  state !== "loading" && state !== "empty" && portfolio && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11078
11723
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center text-center", style: { padding: "16px 16px 12px" }, children: [
@@ -11239,7 +11884,7 @@ function RebalancingWidget({
11239
11884
  selectedMarket,
11240
11885
  onMarketSelect: (market) => {
11241
11886
  setSelectedMarket(market);
11242
- setDepositError(null);
11887
+ if (depositTxState.status === "failed") setDepositTxState({ status: "idle" });
11243
11888
  },
11244
11889
  isLoading: isMarketsLoading
11245
11890
  }
@@ -11262,10 +11907,10 @@ function RebalancingWidget({
11262
11907
  value: depositAmount,
11263
11908
  onChange: (e) => {
11264
11909
  setDepositAmount(e.target.value);
11265
- setDepositError(null);
11910
+ if (depositTxState.status === "failed") setDepositTxState({ status: "idle" });
11266
11911
  },
11267
11912
  placeholder: "0.00",
11268
- disabled: isProcessing,
11913
+ disabled: depositTxState.status !== "idle" && depositTxState.status !== "confirmed" && depositTxState.status !== "failed",
11269
11914
  className: "flex-1 font-bold bg-transparent outline-none",
11270
11915
  style: {
11271
11916
  color: "var(--compass-color-text)",
@@ -11281,7 +11926,7 @@ function RebalancingWidget({
11281
11926
  "button",
11282
11927
  {
11283
11928
  onClick: () => setIsTokenDropdownOpen(!isTokenDropdownOpen),
11284
- disabled: isProcessing,
11929
+ disabled: depositTxState.status !== "idle" && depositTxState.status !== "confirmed" && depositTxState.status !== "failed",
11285
11930
  className: "flex items-center font-semibold",
11286
11931
  style: {
11287
11932
  backgroundColor: "var(--compass-color-surface-hover, var(--compass-color-surface))",
@@ -11318,7 +11963,7 @@ function RebalancingWidget({
11318
11963
  onClick: () => {
11319
11964
  setSelectedToken(token);
11320
11965
  setIsTokenDropdownOpen(false);
11321
- setDepositError(null);
11966
+ if (depositTxState.status === "failed") setDepositTxState({ status: "idle" });
11322
11967
  },
11323
11968
  className: "w-full text-left font-medium",
11324
11969
  style: {
@@ -11344,7 +11989,7 @@ function RebalancingWidget({
11344
11989
  "button",
11345
11990
  {
11346
11991
  onClick: () => setDepositAmount(earnBalancesQuery.toString()),
11347
- disabled: isProcessing,
11992
+ disabled: depositTxState.status !== "idle" && depositTxState.status !== "confirmed" && depositTxState.status !== "failed",
11348
11993
  className: "font-semibold",
11349
11994
  style: { color: "var(--compass-color-primary)", fontSize: "0.8125rem", background: "none", border: "none", padding: 0 },
11350
11995
  children: [
@@ -11381,26 +12026,12 @@ function RebalancingWidget({
11381
12026
  ]
11382
12027
  }
11383
12028
  ),
11384
- depositError && /* @__PURE__ */ jsxRuntime.jsx(
11385
- "div",
11386
- {
11387
- className: "flex items-center",
11388
- style: {
11389
- backgroundColor: "var(--compass-color-error-muted)",
11390
- color: "var(--compass-color-error)",
11391
- borderRadius: "var(--compass-border-radius-lg)",
11392
- padding: "12px 14px",
11393
- gap: "8px",
11394
- fontSize: "0.875rem"
11395
- },
11396
- children: depositError
11397
- }
11398
- ),
12029
+ /* @__PURE__ */ jsxRuntime.jsx(TxStatus, { state: depositTxState }),
11399
12030
  /* @__PURE__ */ jsxRuntime.jsx(
11400
12031
  "button",
11401
12032
  {
11402
12033
  onClick: !isConnected && login ? login : handleDeposit,
11403
- disabled: isConnected && (isProcessing || !selectedMarket || !depositAmount || parseFloat(depositAmount) <= 0 || parseFloat(depositAmount) > earnBalancesQuery),
12034
+ disabled: isConnected && (depositTxState.status !== "idle" && depositTxState.status !== "confirmed" && depositTxState.status !== "failed" || !selectedMarket || !depositAmount || parseFloat(depositAmount) <= 0 || parseFloat(depositAmount) > earnBalancesQuery),
11404
12035
  className: "w-full font-bold",
11405
12036
  style: {
11406
12037
  backgroundColor: "var(--compass-color-primary)",
@@ -11411,26 +12042,13 @@ function RebalancingWidget({
11411
12042
  fontSize: "1rem",
11412
12043
  transition: "all 200ms ease",
11413
12044
  border: "none",
11414
- opacity: isConnected && (isProcessing || !selectedMarket || !depositAmount || parseFloat(depositAmount) <= 0 || parseFloat(depositAmount) > earnBalancesQuery) ? 0.4 : 1
12045
+ opacity: isConnected && (depositTxState.status !== "idle" && depositTxState.status !== "confirmed" && depositTxState.status !== "failed" || !selectedMarket || !depositAmount || parseFloat(depositAmount) <= 0 || parseFloat(depositAmount) > earnBalancesQuery) ? 0.4 : 1
11415
12046
  },
11416
- children: !isConnected ? "Connect Wallet" : isProcessing ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center justify-center", style: { gap: "8px" }, children: [
12047
+ children: !isConnected ? "Connect Wallet" : depositTxState.status !== "idle" && depositTxState.status !== "confirmed" && depositTxState.status !== "failed" ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center justify-center", style: { gap: "8px" }, children: [
11417
12048
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 16, className: "animate-spin" }),
11418
12049
  "Processing..."
11419
12050
  ] }) : !selectedMarket ? "Select a market" : needsSwap ? "Swap & Deposit" : "Deposit"
11420
12051
  }
11421
- ),
11422
- depositStatus && !depositError && /* @__PURE__ */ jsxRuntime.jsx(
11423
- "div",
11424
- {
11425
- className: "text-sm text-center",
11426
- style: {
11427
- backgroundColor: depositStatus.includes("successful") ? "var(--compass-color-success-muted)" : "var(--compass-color-primary-muted, rgba(99, 102, 241, 0.1))",
11428
- color: depositStatus.includes("successful") ? "var(--compass-color-success)" : "var(--compass-color-primary)",
11429
- borderRadius: "var(--compass-border-radius-lg)",
11430
- padding: "10px 12px"
11431
- },
11432
- children: depositStatus
11433
- }
11434
12052
  )
11435
12053
  ] }),
11436
12054
  clientPreview && clientPreview.actions.length > 0 && state === "editing" && /* @__PURE__ */ jsxRuntime.jsxs(