@compass-labs/widgets 0.1.38 → 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
  }
@@ -1160,9 +1167,9 @@ function CopyableAddress({
1160
1167
  title: copied ? "Copied!" : `Copy: ${address}`,
1161
1168
  children: [
1162
1169
  label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: label }),
1163
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1.5", children: [
1170
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [
1164
1171
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono", style: { color: "var(--compass-color-text)" }, children: displayAddr }),
1165
- copied ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: 12, className: "flex-shrink-0", style: { color: "var(--compass-color-success)" } }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { size: 12, className: "flex-shrink-0", style: { color: "var(--compass-color-text-tertiary)" } })
1172
+ copied ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: 14, className: "flex-shrink-0", style: { color: "var(--compass-color-success)" } }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { size: 14, className: "flex-shrink-0", style: { color: "var(--compass-color-text-secondary)" } })
1166
1173
  ] })
1167
1174
  ]
1168
1175
  }
@@ -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_TOKENS = ["USDC", "SBC"];
2667
- var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2668
- compact = false,
2669
- hideVisual = false,
2670
- onTransferComplete
2671
- }, ref) {
2672
- const [isModalOpen, setIsModalOpen] = react.useState(false);
2673
- const [activeAction, setActiveAction] = react.useState("deposit");
2674
- const [selectedToken, setSelectedToken] = react.useState(TRANSFER_TOKENS[0]);
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("");
2675
2969
  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);
2679
- const [isBalancesModalOpen, setIsBalancesModalOpen] = react.useState(false);
2680
- const { address, isConnected, signTypedData, switchChain, walletChainId } = useEmbeddableWallet();
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,
3298
+ onTransferComplete
3299
+ }, ref) {
3300
+ const { address, isConnected, signTypedData, switchChain, walletChainId, fundWallet, hasExternalWallet, sendTransaction } = useEmbeddableWallet();
2681
3301
  const { chainId, chain } = useChain();
2682
3302
  const { earnAccountAddress, isDeployed } = useEarnAccount();
2683
3303
  const queryClient = reactQuery.useQueryClient();
3304
+ const showBuyTab = !!fundWallet;
3305
+ const showSendTab = !!sendTransaction && !hasExternalWallet;
3306
+ const buyOnly = !hasExternalWallet && showBuyTab && !sendTransaction;
3307
+ const [isModalOpen, setIsModalOpen] = react.useState(false);
3308
+ const [activeAction, setActiveAction] = react.useState(
3309
+ !hasExternalWallet && fundWallet ? "buy" : "deposit"
3310
+ );
3311
+ const selectedToken = TRANSFER_TOKEN;
3312
+ const [amount, setAmount] = react.useState("");
3313
+ const [txState, setTxState] = react.useState({ status: "idle" });
3314
+ const [isBalancesModalOpen, setIsBalancesModalOpen] = react.useState(false);
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 () => {
@@ -2697,25 +3331,22 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2697
3331
  staleTime: 30 * 1e3
2698
3332
  });
2699
3333
  const { data: eoaBalances } = reactQuery.useQuery({
2700
- queryKey: ["eoaBalances", chainId, address, TRANSFER_TOKENS.join(",")],
3334
+ queryKey: ["eoaBalances", chainId, address, TRANSFER_TOKEN],
2701
3335
  queryFn: async () => {
2702
3336
  if (!address) return {};
2703
- const balances = {};
2704
- for (const token of TRANSFER_TOKENS) {
2705
- try {
2706
- const response = await fetch(
2707
- `/api/compass/token/balance?chain=${chainId}&token=${token}&address=${address}`
2708
- );
2709
- if (response.ok) {
2710
- const data = await response.json();
2711
- balances[token] = data.balance || "0";
2712
- }
2713
- } catch {
3337
+ try {
3338
+ const response = await fetch(
3339
+ `/api/compass/token/balance?chain=${chainId}&token=${TRANSFER_TOKEN}&address=${address}`
3340
+ );
3341
+ if (response.ok) {
3342
+ const data = await response.json();
3343
+ return { [TRANSFER_TOKEN]: data.balance || "0" };
2714
3344
  }
3345
+ } catch {
2715
3346
  }
2716
- return balances;
3347
+ return {};
2717
3348
  },
2718
- enabled: !!address && activeAction === "deposit",
3349
+ enabled: !!address && hasExternalWallet && activeAction === "deposit",
2719
3350
  staleTime: 30 * 1e3
2720
3351
  });
2721
3352
  const earnBalances = balanceData?.balances || {};
@@ -2734,10 +3365,9 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2734
3365
  }
2735
3366
  const resetForm = react.useCallback(() => {
2736
3367
  setAmount("");
2737
- setTransferState("idle");
2738
- setStatusMessage("");
2739
- setError(null);
2740
- }, []);
3368
+ setTxState({ status: "idle" });
3369
+ clearPolling();
3370
+ }, [clearPolling]);
2741
3371
  const handleOpenModal = () => {
2742
3372
  resetForm();
2743
3373
  setIsModalOpen(true);
@@ -2752,7 +3382,8 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2752
3382
  const handleActionChange = (action) => {
2753
3383
  setActiveAction(action);
2754
3384
  setAmount("");
2755
- setError(null);
3385
+ setTxState({ status: "idle" });
3386
+ clearPolling();
2756
3387
  };
2757
3388
  const getMaxBalance = () => {
2758
3389
  if (activeAction === "deposit") {
@@ -2760,37 +3391,28 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2760
3391
  }
2761
3392
  return earnBalanceAmounts[selectedToken] || "0";
2762
3393
  };
2763
- const handleQuickAmount = (percentage) => {
2764
- const max = parseFloat(getMaxBalance());
2765
- if (isNaN(max)) return;
2766
- setAmount(formatAmount(max * percentage));
2767
- };
2768
3394
  const handleTransfer = react.useCallback(async () => {
2769
3395
  if (!address || !amount || !signTypedData) return;
2770
- setError(null);
3396
+ setTxState({ status: "preparing" });
2771
3397
  try {
2772
3398
  const targetChainId = chain.viemChain.id;
2773
3399
  if (walletChainId !== void 0 && walletChainId !== targetChainId) {
2774
3400
  if (!switchChain) {
2775
3401
  throw new Error(`Please switch your wallet to ${chain.name} (chain ID ${targetChainId}) to continue. Your wallet is currently on chain ID ${walletChainId}.`);
2776
3402
  }
2777
- setStatusMessage(`Switching network to ${chain.name}...`);
2778
3403
  try {
2779
3404
  await switchChain(targetChainId);
2780
- } catch (switchError) {
3405
+ } catch {
2781
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}.`);
2782
3407
  }
2783
3408
  } else if (switchChain && walletChainId === void 0) {
2784
- setStatusMessage("Switching network...");
2785
3409
  try {
2786
3410
  await switchChain(targetChainId);
2787
- } catch (switchError) {
3411
+ } catch {
2788
3412
  throw new Error(`Please switch your wallet to ${chain.name} to continue`);
2789
3413
  }
2790
3414
  }
2791
3415
  if (activeAction === "deposit") {
2792
- setTransferState("checking_approval");
2793
- setStatusMessage("Checking token approval...");
2794
3416
  const approveResponse = await fetch("/api/compass/transfer/approve", {
2795
3417
  method: "POST",
2796
3418
  headers: { "Content-Type": "application/json" },
@@ -2809,16 +3431,14 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2809
3431
  if (approvalData.requiresTransaction) {
2810
3432
  throw new Error("This token requires a transaction-based approval. Please approve manually.");
2811
3433
  }
2812
- setTransferState("awaiting_approval_signature");
2813
- setStatusMessage("Please sign the approval...");
3434
+ setTxState({ status: "signing" });
2814
3435
  const approvalSignature = await signTypedData({
2815
3436
  domain: approvalData.domain,
2816
3437
  types: approvalData.normalizedTypes,
2817
3438
  primaryType: "Permit",
2818
3439
  message: approvalData.message
2819
3440
  });
2820
- setTransferState("approving");
2821
- setStatusMessage("Executing approval...");
3441
+ setTxState({ status: "broadcasting" });
2822
3442
  const executeApprovalResponse = await fetch("/api/compass/transfer/execute", {
2823
3443
  method: "POST",
2824
3444
  headers: { "Content-Type": "application/json" },
@@ -2835,8 +3455,7 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2835
3455
  }
2836
3456
  }
2837
3457
  }
2838
- setTransferState("awaiting_transfer_signature");
2839
- setStatusMessage("Preparing transfer...");
3458
+ setTxState({ status: "signing" });
2840
3459
  const prepareResponse = await fetch("/api/compass/transfer/prepare", {
2841
3460
  method: "POST",
2842
3461
  headers: { "Content-Type": "application/json" },
@@ -2853,15 +3472,13 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2853
3472
  throw new Error(errData.error || "Failed to prepare transfer");
2854
3473
  }
2855
3474
  const prepareData = await prepareResponse.json();
2856
- setStatusMessage("Please sign the transfer...");
2857
3475
  const transferSignature = await signTypedData({
2858
3476
  domain: prepareData.domain,
2859
3477
  types: prepareData.normalizedTypes,
2860
3478
  primaryType: prepareData.primaryType,
2861
3479
  message: prepareData.message
2862
3480
  });
2863
- setTransferState("transferring");
2864
- setStatusMessage("Executing transfer...");
3481
+ setTxState({ status: "broadcasting" });
2865
3482
  const executeResponse = await fetch("/api/compass/transfer/execute", {
2866
3483
  method: "POST",
2867
3484
  headers: { "Content-Type": "application/json" },
@@ -2877,17 +3494,12 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2877
3494
  throw new Error(errData.error || "Transfer failed");
2878
3495
  }
2879
3496
  const { txHash } = await executeResponse.json();
2880
- setTransferState("success");
2881
- setStatusMessage("Transfer successful!");
3497
+ setTxState({ status: "submitted", txHash });
3498
+ startPolling(txHash, setTxState);
2882
3499
  onTransferComplete?.(activeAction, selectedToken, amount, txHash);
2883
- queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
2884
- queryClient.invalidateQueries({ queryKey: ["eoaBalances"] });
2885
- setTimeout(() => {
2886
- resetForm();
2887
- }, 2e3);
3500
+ setAmount("");
2888
3501
  } catch (err) {
2889
- setTransferState("error");
2890
- setError(err instanceof Error ? err.message : "Transfer failed");
3502
+ setTxState({ status: "failed", error: err instanceof Error ? err.message : "Transfer failed" });
2891
3503
  }
2892
3504
  }, [
2893
3505
  address,
@@ -2899,14 +3511,13 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2899
3511
  walletChainId,
2900
3512
  signTypedData,
2901
3513
  switchChain,
2902
- queryClient,
2903
3514
  onTransferComplete,
2904
- resetForm
3515
+ startPolling
2905
3516
  ]);
2906
3517
  if (!isConnected) {
2907
3518
  return null;
2908
3519
  }
2909
- const isProcessing = transferState !== "idle" && transferState !== "success" && transferState !== "error";
3520
+ const isProcessing = txState.status !== "idle" && txState.status !== "confirmed" && txState.status !== "failed";
2910
3521
  if (!isDeployed && !hideVisual) {
2911
3522
  return /* @__PURE__ */ jsxRuntime.jsxs(
2912
3523
  "div",
@@ -2988,178 +3599,151 @@ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2988
3599
  /* @__PURE__ */ jsxRuntime.jsx(
2989
3600
  ActionModal,
2990
3601
  {
2991
- isOpen: isModalOpen,
2992
- onClose: handleCloseModal,
2993
- title: "Transfer Funds",
2994
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
2995
- /* @__PURE__ */ jsxRuntime.jsx(
2996
- "div",
2997
- {
2998
- className: "flex gap-1 p-1",
2999
- style: {
3000
- backgroundColor: "var(--compass-color-background)",
3001
- borderRadius: "var(--compass-border-radius-lg)",
3002
- fontFamily: "var(--compass-font-family)"
3003
- },
3004
- children: ["deposit", "withdraw"].map((action) => /* @__PURE__ */ jsxRuntime.jsxs(
3005
- "button",
3006
- {
3007
- onClick: () => handleActionChange(action),
3008
- disabled: isProcessing,
3009
- className: "flex-1 py-2 text-sm font-medium capitalize transition-all flex items-center justify-center gap-2",
3010
- style: {
3011
- backgroundColor: activeAction === action ? "var(--compass-color-surface)" : "transparent",
3012
- color: activeAction === action ? "var(--compass-color-text)" : "var(--compass-color-text-secondary)",
3013
- borderRadius: "var(--compass-border-radius-md)"
3014
- },
3015
- children: [
3016
- action === "deposit" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownLeft, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpRight, { size: 14 }),
3017
- action
3018
- ]
3019
- },
3020
- action
3021
- ))
3022
- }
3023
- ),
3024
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3025
- /* @__PURE__ */ jsxRuntime.jsx(
3026
- "label",
3027
- {
3028
- className: "text-sm font-medium mb-1 block",
3029
- style: { color: "var(--compass-color-text-secondary)" },
3030
- children: "Token"
3031
- }
3032
- ),
3033
- /* @__PURE__ */ jsxRuntime.jsx(
3034
- TokenSelector,
3035
- {
3036
- tokens: TRANSFER_TOKENS,
3037
- selectedToken,
3038
- onSelect: setSelectedToken,
3039
- balances: activeAction === "deposit" ? eoaBalances : earnBalanceAmounts,
3040
- disabled: isProcessing
3041
- }
3042
- )
3043
- ] }),
3044
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3045
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [
3046
- /* @__PURE__ */ jsxRuntime.jsx(
3047
- "label",
3048
- {
3049
- className: "text-sm font-medium",
3050
- style: { color: "var(--compass-color-text-secondary)" },
3051
- children: "Amount"
3052
- }
3053
- ),
3054
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: [
3055
- "Available: ",
3056
- formatAmount(getMaxBalance()),
3057
- " ",
3058
- selectedToken
3059
- ] })
3060
- ] }),
3061
- /* @__PURE__ */ jsxRuntime.jsxs(
3062
- "div",
3063
- {
3064
- className: "flex items-center gap-2 p-3 border",
3065
- style: {
3066
- backgroundColor: "var(--compass-color-background)",
3067
- borderColor: "var(--compass-color-border)",
3068
- borderRadius: "var(--compass-border-radius-lg)"
3069
- },
3070
- children: [
3071
- /* @__PURE__ */ jsxRuntime.jsx(
3072
- "input",
3073
- {
3074
- type: "number",
3075
- value: amount,
3076
- onChange: (e) => {
3077
- setAmount(e.target.value);
3078
- setError(null);
3079
- },
3080
- placeholder: "0.00",
3081
- disabled: isProcessing,
3082
- className: "flex-1 bg-transparent outline-none text-lg font-mono",
3083
- style: { color: "var(--compass-color-text)" }
3084
- }
3085
- ),
3086
- /* @__PURE__ */ jsxRuntime.jsx(
3087
- "span",
3088
- {
3089
- className: "text-sm font-medium",
3090
- style: { color: "var(--compass-color-text-secondary)" },
3091
- children: selectedToken
3092
- }
3093
- )
3094
- ]
3095
- }
3096
- )
3097
- ] }),
3098
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2", children: [0.25, 0.5, 1].map((pct) => /* @__PURE__ */ jsxRuntime.jsx(
3099
- "button",
3100
- {
3101
- onClick: () => handleQuickAmount(pct),
3102
- disabled: isProcessing,
3103
- className: "flex-1 py-1.5 text-xs font-medium transition-colors",
3104
- style: {
3105
- backgroundColor: "var(--compass-color-secondary)",
3106
- color: "var(--compass-color-text-secondary)",
3107
- borderRadius: "var(--compass-border-radius-md)"
3108
- },
3109
- children: pct === 1 ? "Max" : `${pct * 100}%`
3110
- },
3111
- pct
3112
- )) }),
3113
- error && /* @__PURE__ */ jsxRuntime.jsx(
3114
- "div",
3115
- {
3116
- className: "p-3 text-sm",
3117
- style: {
3118
- backgroundColor: "var(--compass-color-error-muted)",
3119
- color: "var(--compass-color-error)",
3120
- borderRadius: "var(--compass-border-radius-lg)"
3121
- },
3122
- children: error
3123
- }
3124
- ),
3125
- statusMessage && !error && /* @__PURE__ */ jsxRuntime.jsx(
3126
- "div",
3127
- {
3128
- className: "p-3 text-sm text-center",
3129
- style: {
3130
- backgroundColor: transferState === "success" ? "var(--compass-color-success-muted)" : "var(--compass-color-primary-muted)",
3131
- color: transferState === "success" ? "var(--compass-color-success)" : "var(--compass-color-primary)",
3132
- borderRadius: "var(--compass-border-radius-lg)"
3133
- },
3134
- children: statusMessage
3135
- }
3136
- ),
3137
- /* @__PURE__ */ jsxRuntime.jsxs(
3138
- "button",
3602
+ isOpen: isModalOpen,
3603
+ onClose: handleCloseModal,
3604
+ title: showSendTab ? "Manage Funds" : buyOnly ? "Add Funds" : "Transfer Funds",
3605
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
3606
+ buyOnly ? null : /* @__PURE__ */ jsxRuntime.jsx(
3607
+ "div",
3139
3608
  {
3140
- onClick: handleTransfer,
3141
- disabled: isProcessing || !amount || parseFloat(amount) <= 0,
3142
- className: "w-full py-3 font-medium transition-colors flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
3609
+ className: "flex gap-1 p-1",
3143
3610
  style: {
3144
- backgroundColor: "var(--compass-color-primary)",
3145
- color: "var(--compass-color-primary-text)",
3611
+ backgroundColor: "var(--compass-color-background)",
3146
3612
  borderRadius: "var(--compass-border-radius-lg)",
3147
3613
  fontFamily: "var(--compass-font-family)"
3148
3614
  },
3149
3615
  children: [
3150
- isProcessing && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 18, className: "animate-spin" }),
3151
- isProcessing ? "Processing..." : activeAction === "deposit" ? "Deposit to Earn Account" : "Withdraw to Wallet"
3152
- ]
3616
+ ...showSendTab ? ["buy", "send"] : ["deposit", "withdraw", ...showBuyTab ? ["buy"] : []]
3617
+ ].map((action) => /* @__PURE__ */ jsxRuntime.jsxs(
3618
+ "button",
3619
+ {
3620
+ onClick: () => handleActionChange(action),
3621
+ disabled: isProcessing,
3622
+ className: "flex-1 py-2 text-sm font-medium capitalize transition-all flex items-center justify-center gap-2",
3623
+ style: {
3624
+ backgroundColor: activeAction === action ? "var(--compass-color-surface)" : "transparent",
3625
+ color: activeAction === action ? "var(--compass-color-text)" : "var(--compass-color-text-secondary)",
3626
+ borderRadius: "var(--compass-border-radius-md)"
3627
+ },
3628
+ children: [
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 }),
3630
+ action
3631
+ ]
3632
+ },
3633
+ action
3634
+ ))
3153
3635
  }
3154
3636
  ),
3155
- /* @__PURE__ */ jsxRuntime.jsx(
3156
- "p",
3637
+ activeAction === "send" ? /* @__PURE__ */ jsxRuntime.jsx(
3638
+ SendForm,
3157
3639
  {
3158
- className: "text-xs text-center",
3159
- style: { color: "var(--compass-color-text-tertiary)" },
3160
- children: "Gas fees are sponsored by Compass"
3640
+ onComplete: () => {
3641
+ queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
3642
+ queryClient.invalidateQueries({ queryKey: ["eoaBalances"] });
3643
+ }
3161
3644
  }
3162
- )
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",
3718
+ {
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",
3722
+ style: {
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)"
3727
+ },
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
+ ] })
3736
+ }
3737
+ ),
3738
+ /* @__PURE__ */ jsxRuntime.jsx(
3739
+ "p",
3740
+ {
3741
+ className: "text-xs text-center",
3742
+ style: { color: "var(--compass-color-text-tertiary)" },
3743
+ children: "Gas fees are sponsored by Compass"
3744
+ }
3745
+ )
3746
+ ] })
3163
3747
  ] })
3164
3748
  }
3165
3749
  )
@@ -3214,7 +3798,7 @@ function truncate(value, decimals) {
3214
3798
  const factor = Math.pow(10, decimals);
3215
3799
  return (Math.floor(value * factor) / factor).toFixed(decimals);
3216
3800
  }
3217
- var BLOCK_EXPLORERS = {
3801
+ var BLOCK_EXPLORERS2 = {
3218
3802
  ethereum: "https://etherscan.io",
3219
3803
  base: "https://basescan.org",
3220
3804
  arbitrum: "https://arbiscan.io"
@@ -3685,7 +4269,7 @@ function SwapForm({
3685
4269
  /* @__PURE__ */ jsxRuntime.jsxs(
3686
4270
  "a",
3687
4271
  {
3688
- href: `${BLOCK_EXPLORERS[chainId] || BLOCK_EXPLORERS.ethereum}/tx/${lastTxHash}`,
4272
+ href: `${BLOCK_EXPLORERS2[chainId] || BLOCK_EXPLORERS2.ethereum}/tx/${lastTxHash}`,
3689
4273
  target: "_blank",
3690
4274
  rel: "noopener noreferrer",
3691
4275
  style: { display: "flex", alignItems: "center", gap: "4px", color: "var(--compass-color-text-secondary)", fontSize: "0.75rem", textDecoration: "none" },
@@ -3875,8 +4459,7 @@ function MarketSelector({
3875
4459
  },
3876
4460
  children: [
3877
4461
  getTypeLabel(selectedMarket.type),
3878
- " \xB7 ",
3879
- formatTvl(selectedMarket.tvl)
4462
+ selectedMarket.type !== "aave" ? ` \xB7 ${formatTvl(selectedMarket.tvl)}` : ""
3880
4463
  ]
3881
4464
  }
3882
4465
  )
@@ -3982,8 +4565,7 @@ function MarketSelector({
3982
4565
  },
3983
4566
  children: [
3984
4567
  getTypeLabel(market.type),
3985
- " \xB7 ",
3986
- formatTvl(market.tvl)
4568
+ market.type !== "aave" ? ` \xB7 ${formatTvl(market.tvl)}` : ""
3987
4569
  ]
3988
4570
  }
3989
4571
  )
@@ -4012,7 +4594,7 @@ function MarketSelector({
4012
4594
  )
4013
4595
  ] });
4014
4596
  }
4015
- var BLOCK_EXPLORERS2 = {
4597
+ var BLOCK_EXPLORERS3 = {
4016
4598
  ethereum: "https://etherscan.io",
4017
4599
  base: "https://basescan.org",
4018
4600
  arbitrum: "https://arbiscan.io"
@@ -4174,7 +4756,7 @@ function PositionCard({ position, chainId }) {
4174
4756
  tx.txHash && /* @__PURE__ */ jsxRuntime.jsx(
4175
4757
  "a",
4176
4758
  {
4177
- href: `${BLOCK_EXPLORERS2[chainId || "ethereum"] || BLOCK_EXPLORERS2.ethereum}/tx/${tx.txHash}`,
4759
+ href: `${BLOCK_EXPLORERS3[chainId || "ethereum"] || BLOCK_EXPLORERS3.ethereum}/tx/${tx.txHash}`,
4178
4760
  target: "_blank",
4179
4761
  rel: "noopener noreferrer",
4180
4762
  style: { color: "var(--compass-color-text-tertiary)", lineHeight: 0 },
@@ -4308,161 +4890,29 @@ function EarningsModal({ isOpen, onClose, positions, totalEarned, isLoading, cha
4308
4890
  /* @__PURE__ */ jsxRuntime.jsxs("span", { style: {
4309
4891
  fontSize: "0.8125rem",
4310
4892
  fontWeight: 600,
4311
- color: totalRealized >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)"
4312
- }, children: [
4313
- totalRealized >= 0 ? "+" : "",
4314
- formatCurrency(totalRealized)
4315
- ] })
4316
- ] })
4317
- ] })
4318
- ] }),
4319
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
4320
- flex: 1,
4321
- overflowY: "auto",
4322
- scrollbarWidth: "none",
4323
- padding: "16px",
4324
- display: "flex",
4325
- flexDirection: "column",
4326
- gap: "10px"
4327
- }, 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: [
4328
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--compass-color-text-secondary)", fontSize: "0.9375rem", margin: "0 0 4px" }, children: "No active positions yet" }),
4329
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--compass-color-text-tertiary)", fontSize: "0.8125rem", margin: 0 }, children: "Deposit into a market to start earning" })
4330
- ] }) : positions.map((position) => /* @__PURE__ */ jsxRuntime.jsx(PositionCard, { position, chainId }, position.id)) })
4331
- ]
4332
- }
4333
- )
4334
- }
4335
- );
4336
- }
4337
- var BLOCK_EXPLORERS3 = {
4338
- ethereum: "https://etherscan.io",
4339
- base: "https://basescan.org",
4340
- arbitrum: "https://arbiscan.io"
4341
- };
4342
- function TxStatus({ state }) {
4343
- const { chainId } = useChain();
4344
- if (state.status === "idle") return null;
4345
- const explorer = BLOCK_EXPLORERS3[chainId] || BLOCK_EXPLORERS3.ethereum;
4346
- return /* @__PURE__ */ jsxRuntime.jsxs(
4347
- "div",
4348
- {
4349
- className: "mt-4 p-4 border",
4350
- style: {
4351
- borderColor: "var(--compass-color-border)",
4352
- borderRadius: "var(--compass-border-radius-xl)",
4353
- fontFamily: "var(--compass-font-family)"
4354
- },
4355
- children: [
4356
- state.status === "preparing" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
4357
- /* @__PURE__ */ jsxRuntime.jsx(
4358
- lucideReact.Loader2,
4359
- {
4360
- className: "h-4 w-4 animate-spin",
4361
- style: { color: "var(--compass-color-primary)" }
4362
- }
4363
- ),
4364
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-secondary)" }, children: "Preparing transaction..." })
4365
- ] }),
4366
- state.status === "signing" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
4367
- /* @__PURE__ */ jsxRuntime.jsx(
4368
- lucideReact.Loader2,
4369
- {
4370
- className: "h-4 w-4 animate-spin",
4371
- style: { color: "var(--compass-color-warning)" }
4372
- }
4373
- ),
4374
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-secondary)" }, children: "Waiting for signature..." })
4375
- ] }),
4376
- state.status === "broadcasting" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
4377
- /* @__PURE__ */ jsxRuntime.jsx(
4378
- lucideReact.Loader2,
4379
- {
4380
- className: "h-4 w-4 animate-spin",
4381
- style: { color: "var(--compass-color-primary)" }
4382
- }
4383
- ),
4384
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-secondary)" }, children: "Broadcasting transaction..." })
4385
- ] }),
4386
- state.status === "submitted" && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4387
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
4388
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
4389
- /* @__PURE__ */ jsxRuntime.jsx(
4390
- lucideReact.Loader2,
4391
- {
4392
- className: "h-4 w-4 animate-spin",
4393
- style: { color: "var(--compass-color-primary)" }
4394
- }
4395
- ),
4396
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text)" }, children: "Transaction submitted" })
4397
- ] }),
4398
- /* @__PURE__ */ jsxRuntime.jsxs(
4399
- "a",
4400
- {
4401
- href: `${explorer}/tx/${state.txHash}`,
4402
- target: "_blank",
4403
- rel: "noopener noreferrer",
4404
- className: "flex items-center gap-1.5 text-xs hover:opacity-70 transition-opacity",
4405
- style: { color: "var(--compass-color-text-secondary)" },
4406
- children: [
4407
- "View on explorer",
4408
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3" })
4409
- ]
4410
- }
4411
- )
4412
- ] }),
4413
- /* @__PURE__ */ jsxRuntime.jsx(
4414
- "p",
4415
- {
4416
- className: "text-xs mt-2",
4417
- style: { color: "var(--compass-color-text-tertiary)" },
4418
- children: "Waiting for on-chain confirmation. Balances will update automatically."
4419
- }
4420
- )
4421
- ] }),
4422
- state.status === "confirmed" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
4423
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
4424
- /* @__PURE__ */ jsxRuntime.jsx(
4425
- lucideReact.CheckCircle,
4426
- {
4427
- className: "h-4 w-4",
4428
- style: { color: "var(--compass-color-success)" }
4429
- }
4430
- ),
4431
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-success)" }, children: "Transaction confirmed" })
4432
- ] }),
4433
- /* @__PURE__ */ jsxRuntime.jsxs(
4434
- "a",
4435
- {
4436
- href: `${explorer}/tx/${state.txHash}`,
4437
- target: "_blank",
4438
- rel: "noopener noreferrer",
4439
- className: "flex items-center gap-1.5 text-xs hover:opacity-70 transition-opacity",
4440
- style: { color: "var(--compass-color-text-secondary)" },
4441
- children: [
4442
- "View on explorer",
4443
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3" })
4444
- ]
4445
- }
4446
- )
4447
- ] }),
4448
- state.status === "failed" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 text-sm", children: [
4449
- /* @__PURE__ */ jsxRuntime.jsx(
4450
- lucideReact.XCircle,
4451
- {
4452
- className: "h-4 w-4 mt-0.5 shrink-0",
4453
- style: { color: "var(--compass-color-error)" }
4454
- }
4455
- ),
4456
- /* @__PURE__ */ jsxRuntime.jsx(
4457
- "span",
4458
- {
4459
- className: "break-all",
4460
- style: { color: "var(--compass-color-error)" },
4461
- children: state.error
4462
- }
4463
- )
4464
- ] })
4465
- ]
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
+ )
4466
4916
  }
4467
4917
  );
4468
4918
  }
@@ -4470,7 +4920,7 @@ function truncate2(value, decimals) {
4470
4920
  const factor = Math.pow(10, decimals);
4471
4921
  return (Math.floor(value * factor) / factor).toFixed(decimals);
4472
4922
  }
4473
- var EVM_CHAIN_IDS = {
4923
+ var EVM_CHAIN_IDS2 = {
4474
4924
  ethereum: 1,
4475
4925
  base: 8453,
4476
4926
  arbitrum: 42161
@@ -4511,8 +4961,8 @@ function EarnAccount({
4511
4961
  chain: chainProp,
4512
4962
  height = "600px"
4513
4963
  }) {
4514
- const { address, isConnected, login, logout, signTypedData, switchChain, walletChainId } = useEmbeddableWallet();
4515
- const { isDeployed } = useEarnAccount();
4964
+ const { address, isConnected, login, logout, signTypedData, switchChain, walletChainId, fundWallet, hasExternalWallet, sendTransaction } = useEmbeddableWallet();
4965
+ const { isDeployed, earnAccountAddress } = useEarnAccount();
4516
4966
  const { chainId: contextChainId } = useChain();
4517
4967
  const CHAIN_ID = chainProp || contextChainId;
4518
4968
  const queryClient = reactQuery.useQueryClient();
@@ -4526,10 +4976,15 @@ function EarnAccount({
4526
4976
  const [isTokenDropdownOpen, setIsTokenDropdownOpen] = react.useState(false);
4527
4977
  const [isFundModalOpen, setIsFundModalOpen] = react.useState(false);
4528
4978
  const [fundAmount, setFundAmount] = react.useState("");
4529
- const [fundAction, setFundAction] = react.useState("deposit");
4979
+ const [fundAction, setFundAction] = react.useState(
4980
+ !hasExternalWallet && fundWallet ? "buy" : "deposit"
4981
+ );
4530
4982
  const [fundStep, setFundStep] = react.useState("idle");
4531
4983
  const [fundTxState, setFundTxState] = react.useState({ status: "idle" });
4532
4984
  const fundPollRef = react.useRef(null);
4985
+ const showBuyTab = !!fundWallet;
4986
+ const showSendTab = !!sendTransaction && !hasExternalWallet;
4987
+ const fundBuyOnly = !hasExternalWallet && showBuyTab && !sendTransaction;
4533
4988
  const actionPollRef = react.useRef(null);
4534
4989
  const actionTimeoutRef = react.useRef(null);
4535
4990
  const [isBalancesModalOpen, setIsBalancesModalOpen] = react.useState(false);
@@ -4569,7 +5024,7 @@ function EarnAccount({
4569
5024
  const fundIsValid = Number(fundAmount) > 0 && !!address && isDeployed;
4570
5025
  const fundPhase = getFundPhase(fundStep);
4571
5026
  const ensureCorrectChain = react.useCallback(async () => {
4572
- const targetChainId = EVM_CHAIN_IDS[CHAIN_ID];
5027
+ const targetChainId = EVM_CHAIN_IDS2[CHAIN_ID];
4573
5028
  if (!targetChainId) return;
4574
5029
  if (walletChainId !== void 0 && walletChainId !== targetChainId) {
4575
5030
  if (!switchChain) {
@@ -4593,7 +5048,7 @@ function EarnAccount({
4593
5048
  return null;
4594
5049
  }
4595
5050
  },
4596
- enabled: !!address && isFundModalOpen && fundAction === "deposit",
5051
+ enabled: !!address && hasExternalWallet && isFundModalOpen && fundAction === "deposit",
4597
5052
  staleTime: 15 * 1e3
4598
5053
  });
4599
5054
  const walletBalance = walletBalanceQuery.data?.balance || "0";
@@ -4877,7 +5332,10 @@ function EarnAccount({
4877
5332
  setFundTxState({ status: "failed", error: "Transaction reverted" });
4878
5333
  return;
4879
5334
  }
4880
- } 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
+ }
4881
5339
  }
4882
5340
  if (polls >= 24) {
4883
5341
  clearFundPolling();
@@ -5672,31 +6130,7 @@ function EarnAccount({
5672
6130
  ]
5673
6131
  }
5674
6132
  )
5675
- ] }),
5676
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex", style: { gap: "6px", marginTop: activeTab === "deposit" && maxAmount >= 10 ? "8px" : "0" }, children: activeTab === "deposit" && maxAmount > 0 && [10, 20, 30].filter((v) => v <= maxAmount).map((val) => /* @__PURE__ */ jsxRuntime.jsxs(
5677
- "button",
5678
- {
5679
- onClick: () => {
5680
- setAmount(val.toString());
5681
- setError(null);
5682
- },
5683
- disabled: isProcessing,
5684
- className: "text-xs font-medium",
5685
- style: {
5686
- padding: "4px 12px",
5687
- backgroundColor: parseFloat(amount) === val ? "transparent" : "var(--compass-color-surface)",
5688
- border: parseFloat(amount) === val ? "1.5px solid var(--compass-color-primary)" : "1.5px solid var(--compass-color-border)",
5689
- color: parseFloat(amount) === val ? "var(--compass-color-primary)" : "var(--compass-color-text-secondary)",
5690
- borderRadius: "var(--compass-border-radius-xl)",
5691
- transition: "all 150ms ease"
5692
- },
5693
- children: [
5694
- "$",
5695
- val
5696
- ]
5697
- },
5698
- val
5699
- )) })
6133
+ ] })
5700
6134
  ]
5701
6135
  }
5702
6136
  ),
@@ -5792,13 +6226,13 @@ function EarnAccount({
5792
6226
  onClose: () => {
5793
6227
  setIsFundModalOpen(false);
5794
6228
  setFundAmount("");
5795
- setFundAction("deposit");
6229
+ setFundAction(fundBuyOnly ? "buy" : showSendTab ? "buy" : "deposit");
5796
6230
  setFundStep("idle");
5797
6231
  setFundTxState({ status: "idle" });
5798
6232
  },
5799
- title: "Transfer Funds",
6233
+ title: showSendTab ? "Manage Funds" : fundBuyOnly ? "Add Funds" : "Transfer Funds",
5800
6234
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
5801
- /* @__PURE__ */ jsxRuntime.jsx(
6235
+ fundBuyOnly ? null : /* @__PURE__ */ jsxRuntime.jsx(
5802
6236
  "div",
5803
6237
  {
5804
6238
  className: "flex gap-1 p-1",
@@ -5807,7 +6241,9 @@ function EarnAccount({
5807
6241
  borderRadius: "var(--compass-border-radius-lg)",
5808
6242
  fontFamily: "var(--compass-font-family)"
5809
6243
  },
5810
- children: ["deposit", "withdraw"].map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
6244
+ children: [
6245
+ ...showSendTab ? ["buy", "send"] : ["deposit", "withdraw", ...showBuyTab ? ["buy"] : []]
6246
+ ].map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
5811
6247
  "button",
5812
6248
  {
5813
6249
  onClick: () => handleFundActionChange(tab),
@@ -5820,7 +6256,7 @@ function EarnAccount({
5820
6256
  border: "none"
5821
6257
  },
5822
6258
  children: [
5823
- 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 }),
5824
6260
  tab
5825
6261
  ]
5826
6262
  },
@@ -5828,137 +6264,158 @@ function EarnAccount({
5828
6264
  ))
5829
6265
  }
5830
6266
  ),
5831
- (fundIsBusy || fundStep === "confirmed") && fundAction === "deposit" && /* @__PURE__ */ jsxRuntime.jsxs(
5832
- "div",
6267
+ fundAction === "send" ? /* @__PURE__ */ jsxRuntime.jsx(
6268
+ SendForm,
5833
6269
  {
5834
- className: "flex gap-3 p-3 text-xs",
5835
- style: {
5836
- backgroundColor: "var(--compass-color-surface)",
5837
- borderRadius: "var(--compass-border-radius-lg)",
5838
- fontFamily: "var(--compass-font-family)"
5839
- },
5840
- children: [
5841
- /* @__PURE__ */ jsxRuntime.jsx(
5842
- FundStepIndicator,
5843
- {
5844
- number: 1,
5845
- label: "Approve",
5846
- state: fundPhase > 1 ? "done" : fundPhase === 1 ? "active" : "pending"
5847
- }
5848
- ),
5849
- /* @__PURE__ */ jsxRuntime.jsx(
5850
- FundStepIndicator,
5851
- {
5852
- number: 2,
5853
- label: "Transfer",
5854
- state: fundStep === "confirmed" ? "done" : fundPhase > 1 ? "active" : "pending"
5855
- }
5856
- )
5857
- ]
6270
+ onComplete: () => {
6271
+ queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
6272
+ queryClient.invalidateQueries({ queryKey: ["walletBalance"] });
6273
+ queryClient.invalidateQueries({ queryKey: ["walletTokenBalance"] });
6274
+ }
5858
6275
  }
5859
- ),
5860
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
5861
- /* @__PURE__ */ jsxRuntime.jsx(
5862
- "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",
5863
6289
  {
5864
- className: "block text-sm mb-1.5",
6290
+ className: "flex gap-3 p-3 text-xs",
5865
6291
  style: {
5866
- color: "var(--compass-color-text-secondary)",
6292
+ backgroundColor: "var(--compass-color-surface)",
6293
+ borderRadius: "var(--compass-border-radius-lg)",
5867
6294
  fontFamily: "var(--compass-font-family)"
5868
6295
  },
5869
- 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
+ ]
5870
6314
  }
5871
6315
  ),
5872
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
6316
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
5873
6317
  /* @__PURE__ */ jsxRuntime.jsx(
5874
- "input",
6318
+ "label",
5875
6319
  {
5876
- type: "number",
5877
- value: fundAmount,
5878
- onChange: (e) => {
5879
- setFundAmount(e.target.value);
5880
- setFundTxState({ status: "idle" });
5881
- },
5882
- disabled: fundIsBusy,
5883
- placeholder: "0.00",
5884
- 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",
5885
6321
  style: {
5886
- backgroundColor: "var(--compass-color-surface)",
5887
- borderColor: "var(--compass-color-border)",
5888
- borderRadius: "var(--compass-border-radius-lg)",
5889
- color: "var(--compass-color-text)",
6322
+ color: "var(--compass-color-text-secondary)",
5890
6323
  fontFamily: "var(--compass-font-family)"
5891
- }
6324
+ },
6325
+ children: "Amount"
5892
6326
  }
5893
6327
  ),
5894
- /* @__PURE__ */ jsxRuntime.jsx(
5895
- "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",
5896
6361
  {
5897
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-xs",
6362
+ className: "text-xs mt-1",
5898
6363
  style: { color: "var(--compass-color-text-tertiary)" },
5899
- 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
+ ]
5900
6380
  }
5901
6381
  )
5902
6382
  ] }),
5903
- fundMaxBalance !== "0" && /* @__PURE__ */ jsxRuntime.jsxs(
6383
+ !fundIsBusy && fundStep !== "confirmed" && fundStep !== "failed" && fundTxState.status === "idle" && /* @__PURE__ */ jsxRuntime.jsx(
5904
6384
  "p",
5905
6385
  {
5906
- className: "text-xs mt-1",
5907
- style: { color: "var(--compass-color-text-tertiary)" },
5908
- children: [
5909
- "Available:",
5910
- " ",
5911
- /* @__PURE__ */ jsxRuntime.jsxs(
5912
- "button",
5913
- {
5914
- onClick: () => setFundAmount(truncate2(parseFloat(fundMaxBalance), 2)),
5915
- disabled: fundIsBusy,
5916
- style: { color: "var(--compass-color-primary)", background: "none", border: "none", padding: 0, cursor: "pointer", fontSize: "inherit" },
5917
- children: [
5918
- truncate2(parseFloat(fundMaxBalance), 2),
5919
- " USDC"
5920
- ]
5921
- }
5922
- )
5923
- ]
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." : ""}`
5924
6392
  }
5925
- )
5926
- ] }),
5927
- !fundIsBusy && fundStep !== "confirmed" && fundStep !== "failed" && fundTxState.status === "idle" && /* @__PURE__ */ jsxRuntime.jsx(
5928
- "p",
5929
- {
5930
- className: "text-xs",
5931
- style: {
5932
- color: "var(--compass-color-text-tertiary)",
5933
- fontFamily: "var(--compass-font-family)"
5934
- },
5935
- 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."
5936
- }
5937
- ),
5938
- /* @__PURE__ */ jsxRuntime.jsx(
5939
- "button",
5940
- {
5941
- onClick: handleFundTransfer,
5942
- disabled: !fundIsValid || fundIsBusy,
5943
- 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",
5944
- style: {
5945
- backgroundColor: "var(--compass-color-primary)",
5946
- color: "var(--compass-color-primary-text)",
5947
- borderRadius: "var(--compass-border-radius-xl)",
5948
- fontFamily: "var(--compass-font-family)",
5949
- transition: "all 200ms ease",
5950
- border: "none"
5951
- },
5952
- children: fundIsBusy ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5953
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
5954
- fundButtonLabel()
5955
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5956
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { className: "h-4 w-4" }),
5957
- fundAction === "deposit" ? "Transfer to Savings Account" : "Withdraw to Wallet"
5958
- ] })
5959
- }
5960
- ),
5961
- /* @__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
+ ] })
5962
6419
  ] })
5963
6420
  }
5964
6421
  ),
@@ -7765,19 +8222,24 @@ function getPhase(step) {
7765
8222
  if (step === "approving" || step === "signing-approval") return 1;
7766
8223
  return 2;
7767
8224
  }
7768
- var EVM_CHAIN_IDS2 = {
8225
+ var EVM_CHAIN_IDS3 = {
7769
8226
  ethereum: 1,
7770
8227
  base: 8453,
7771
8228
  arbitrum: 42161
7772
8229
  };
7773
8230
  function TopUpModal({ isOpen, onClose }) {
7774
- const { address, signTypedData, switchChain, walletChainId } = useEmbeddableWallet();
8231
+ const { address, signTypedData, switchChain, walletChainId, fundWallet, hasExternalWallet, sendTransaction } = useEmbeddableWallet();
7775
8232
  const { chainId } = useChain();
7776
- const { isDeployed } = useCreditAccount();
8233
+ const { isDeployed, creditAccountAddress } = useCreditAccount();
7777
8234
  const queryClient = reactQuery.useQueryClient();
7778
8235
  const prices = useTokenPrices();
7779
8236
  const token = "USDC";
7780
- 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;
7781
8243
  const [amount, setAmount] = react.useState("");
7782
8244
  const [step, setStep] = react.useState("idle");
7783
8245
  const [txState, setTxState] = react.useState({ status: "idle" });
@@ -7826,7 +8288,7 @@ function TopUpModal({ isOpen, onClose }) {
7826
8288
  const isValid = Number(amount) > 0 && !!address && isDeployed;
7827
8289
  const isBusy = step === "approving" || step === "signing-approval" || step === "preparing-transfer" || step === "signing-transfer" || step === "executing";
7828
8290
  const ensureCorrectChain = react.useCallback(async () => {
7829
- const targetChainId = EVM_CHAIN_IDS2[CHAIN_ID];
8291
+ const targetChainId = EVM_CHAIN_IDS3[CHAIN_ID];
7830
8292
  if (!targetChainId) return;
7831
8293
  if (walletChainId !== void 0 && walletChainId !== targetChainId) {
7832
8294
  if (!switchChain) {
@@ -8028,7 +8490,10 @@ function TopUpModal({ isOpen, onClose }) {
8028
8490
  setTxState({ status: "failed", error: "Transaction reverted" });
8029
8491
  return;
8030
8492
  }
8031
- } 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
+ }
8032
8497
  }
8033
8498
  if (polls >= 24) {
8034
8499
  clearPolling();
@@ -8064,8 +8529,8 @@ function TopUpModal({ isOpen, onClose }) {
8064
8529
  color: "var(--compass-color-text-secondary)",
8065
8530
  fontFamily: "var(--compass-font-family)"
8066
8531
  };
8067
- return /* @__PURE__ */ jsxRuntime.jsx(ActionModal, { isOpen, onClose, title: "Transfer Funds", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
8068
- /* @__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(
8069
8534
  "div",
8070
8535
  {
8071
8536
  className: "flex gap-1 p-1",
@@ -8074,7 +8539,9 @@ function TopUpModal({ isOpen, onClose }) {
8074
8539
  borderRadius: "var(--compass-border-radius-lg)",
8075
8540
  fontFamily: "var(--compass-font-family)"
8076
8541
  },
8077
- 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(
8078
8545
  "button",
8079
8546
  {
8080
8547
  onClick: () => handleActionChange(tab),
@@ -8086,7 +8553,7 @@ function TopUpModal({ isOpen, onClose }) {
8086
8553
  borderRadius: "var(--compass-border-radius-md)"
8087
8554
  },
8088
8555
  children: [
8089
- 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 }),
8090
8557
  tab
8091
8558
  ]
8092
8559
  },
@@ -8094,122 +8561,143 @@ function TopUpModal({ isOpen, onClose }) {
8094
8561
  ))
8095
8562
  }
8096
8563
  ),
8097
- (isBusy || step === "confirmed") && action === "deposit" && /* @__PURE__ */ jsxRuntime.jsxs(
8098
- "div",
8564
+ action === "send" ? /* @__PURE__ */ jsxRuntime.jsx(
8565
+ SendForm,
8099
8566
  {
8100
- className: "flex gap-3 p-3 text-xs",
8101
- style: {
8102
- backgroundColor: "var(--compass-color-surface)",
8103
- borderRadius: "var(--compass-border-radius-lg)",
8104
- fontFamily: "var(--compass-font-family)"
8105
- },
8106
- 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: [
8107
8616
  /* @__PURE__ */ jsxRuntime.jsx(
8108
- StepIndicator,
8617
+ "input",
8109
8618
  {
8110
- number: 1,
8111
- label: "Approve",
8112
- 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
8113
8629
  }
8114
8630
  ),
8115
8631
  /* @__PURE__ */ jsxRuntime.jsx(
8116
- StepIndicator,
8632
+ "span",
8117
8633
  {
8118
- number: 2,
8119
- label: "Transfer",
8120
- 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
8121
8637
  }
8122
8638
  )
8123
- ]
8124
- }
8125
- ),
8126
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8127
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm mb-1.5", style: labelStyle, children: "Amount" }),
8128
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
8129
- /* @__PURE__ */ jsxRuntime.jsx(
8130
- "input",
8639
+ ] }),
8640
+ formatUsdEstimate(amount, token, prices) && /* @__PURE__ */ jsxRuntime.jsxs(
8641
+ "p",
8131
8642
  {
8132
- type: "number",
8133
- value: amount,
8134
- onChange: (e) => {
8135
- setAmount(e.target.value);
8136
- setTxState({ status: "idle" });
8137
- },
8138
- disabled: isBusy,
8139
- placeholder: "0.00",
8140
- className: "w-full border px-3 py-2.5 pr-14 text-sm focus:outline-none disabled:opacity-50",
8141
- 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
+ ]
8142
8650
  }
8143
8651
  ),
8144
- /* @__PURE__ */ jsxRuntime.jsx(
8145
- "span",
8652
+ maxBalance && maxBalance !== "0" && /* @__PURE__ */ jsxRuntime.jsxs(
8653
+ "p",
8146
8654
  {
8147
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-xs",
8655
+ className: "text-xs mt-1",
8148
8656
  style: { color: "var(--compass-color-text-tertiary)" },
8149
- children: token
8657
+ children: [
8658
+ "Available: ",
8659
+ truncate6(parseFloat(maxBalance), 2),
8660
+ " ",
8661
+ token
8662
+ ]
8150
8663
  }
8151
8664
  )
8152
8665
  ] }),
8153
- formatUsdEstimate(amount, token, prices) && /* @__PURE__ */ jsxRuntime.jsxs(
8666
+ !isBusy && step !== "confirmed" && step !== "failed" && txState.status === "idle" && /* @__PURE__ */ jsxRuntime.jsx(
8154
8667
  "p",
8155
8668
  {
8156
- className: "text-xs mt-1",
8157
- style: { color: "var(--compass-color-text-tertiary)" },
8158
- children: [
8159
- "\u2248",
8160
- " ",
8161
- formatUsdEstimate(amount, token, prices)
8162
- ]
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." : ""}`
8163
8675
  }
8164
8676
  ),
8165
- maxBalance && maxBalance !== "0" && /* @__PURE__ */ jsxRuntime.jsxs(
8166
- "p",
8677
+ /* @__PURE__ */ jsxRuntime.jsx(
8678
+ "button",
8167
8679
  {
8168
- className: "text-xs mt-1",
8169
- style: { color: "var(--compass-color-text-tertiary)" },
8170
- children: [
8171
- "Available: ",
8172
- truncate6(parseFloat(maxBalance), 2),
8173
- " ",
8174
- token
8175
- ]
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
+ ] })
8176
8697
  }
8177
- )
8178
- ] }),
8179
- !isBusy && step !== "confirmed" && step !== "failed" && txState.status === "idle" && /* @__PURE__ */ jsxRuntime.jsx(
8180
- "p",
8181
- {
8182
- className: "text-xs",
8183
- style: {
8184
- color: "var(--compass-color-text-tertiary)",
8185
- fontFamily: "var(--compass-font-family)"
8186
- },
8187
- 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."
8188
- }
8189
- ),
8190
- /* @__PURE__ */ jsxRuntime.jsx(
8191
- "button",
8192
- {
8193
- onClick: handleTransfer,
8194
- disabled: !isValid || isBusy,
8195
- 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",
8196
- style: {
8197
- backgroundColor: "var(--compass-color-primary)",
8198
- color: "white",
8199
- borderRadius: "var(--compass-border-radius-xl)",
8200
- fontFamily: "var(--compass-font-family)",
8201
- transition: "var(--compass-transition-normal)"
8202
- },
8203
- children: isBusy ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8204
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
8205
- buttonLabel()
8206
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8207
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { className: "h-4 w-4" }),
8208
- action === "deposit" ? "Transfer to Credit Account" : "Withdraw to Wallet"
8209
- ] })
8210
- }
8211
- ),
8212
- /* @__PURE__ */ jsxRuntime.jsx(TxStatus, { state: txState })
8698
+ ),
8699
+ /* @__PURE__ */ jsxRuntime.jsx(TxStatus, { state: txState })
8700
+ ] })
8213
8701
  ] }) });
8214
8702
  }
8215
8703
  function StepIndicator({
@@ -8237,9 +8725,10 @@ function CreditSwapModal({ isOpen, onClose }) {
8237
8725
  const prices = useTokenPrices();
8238
8726
  const { txState, executeBundle, resetState } = useCreditBundle();
8239
8727
  const tokens = CREDIT_TOKENS;
8240
- const fromToken = "USDC";
8241
- 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");
8242
8730
  const [fromAmount, setFromAmount] = react.useState("");
8731
+ const [isFromOpen, setIsFromOpen] = react.useState(false);
8243
8732
  const [isToOpen, setIsToOpen] = react.useState(false);
8244
8733
  react.useEffect(() => {
8245
8734
  if (isOpen) {
@@ -8247,6 +8736,12 @@ function CreditSwapModal({ isOpen, onClose }) {
8247
8736
  resetState();
8248
8737
  }
8249
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]);
8250
8745
  const fromBalance = balances?.find((b) => b.tokenSymbol === fromToken);
8251
8746
  const fromBalanceAmount = fromBalance ? parseFloat(fromBalance.amount) : 0;
8252
8747
  const estimatedOutput = (() => {
@@ -8325,24 +8820,76 @@ function CreditSwapModal({ isOpen, onClose }) {
8325
8820
  }
8326
8821
  }
8327
8822
  ),
8328
- /* @__PURE__ */ jsxRuntime.jsx(
8329
- "div",
8330
- {
8331
- style: {
8332
- display: "flex",
8333
- alignItems: "center",
8334
- fontWeight: 500,
8335
- backgroundColor: "var(--compass-color-background)",
8336
- border: "1.5px solid var(--compass-color-border)",
8337
- color: "var(--compass-color-text)",
8338
- borderRadius: "var(--compass-border-radius-md)",
8339
- padding: "4px 8px",
8340
- fontSize: "0.8125rem",
8341
- flexShrink: 0
8342
- },
8343
- children: fromToken
8344
- }
8345
- )
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
+ ] })
8346
8893
  ] })
8347
8894
  ]
8348
8895
  }
@@ -8549,7 +9096,7 @@ function CreditAccount({
8549
9096
  onBorrow: _onBorrow,
8550
9097
  onRepay: _onRepay
8551
9098
  }) {
8552
- const { address, isConnected, login, logout } = useEmbeddableWallet();
9099
+ const { address, isConnected, login, logout, hasExternalWallet } = useEmbeddableWallet();
8553
9100
  const { isDeployed, creditAccountAddress } = useCreditAccount();
8554
9101
  const { chainId } = useChain();
8555
9102
  const { data: positions, isLoading: positionsLoading } = useCreditPositions();
@@ -8575,6 +9122,25 @@ function CreditAccount({
8575
9122
  return { ...b, usdValue };
8576
9123
  }).filter((b) => b.usdValue >= 0.01).sort((a, b) => a.tokenSymbol.localeCompare(b.tokenSymbol));
8577
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
+ });
8578
9144
  const totalInterestEarnedUsd = (() => {
8579
9145
  if (!positions) return 0;
8580
9146
  let total = 0;
@@ -9575,6 +10141,7 @@ function PositionDetailModal({ position, onClose }) {
9575
10141
  const isPendle = position.venue === "pendle";
9576
10142
  const duration = formatDuration(position.entryTimestamp);
9577
10143
  const expiryDate = formatExpiryUTC(position.expiry);
10144
+ const modalRef = react.useRef(null);
9578
10145
  react.useEffect(() => {
9579
10146
  const handleKeyDown = (e) => {
9580
10147
  if (e.key === "Escape") onClose();
@@ -9582,296 +10149,312 @@ function PositionDetailModal({ position, onClose }) {
9582
10149
  document.addEventListener("keydown", handleKeyDown);
9583
10150
  return () => document.removeEventListener("keydown", handleKeyDown);
9584
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
+ }, []);
9585
10157
  const hasStats = position.apy || isPendle && (duration || expiryDate) || position.pnl;
9586
- return /* @__PURE__ */ jsxRuntime.jsx(
10158
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9587
10159
  "div",
9588
10160
  {
9589
10161
  className: "fixed inset-0 z-50 flex items-center justify-center p-4",
9590
- style: { backgroundColor: "rgba(0, 0, 0, 0.7)" },
10162
+ onWheel: handleOverlayWheel,
9591
10163
  onClick: onClose,
9592
- children: /* @__PURE__ */ jsxRuntime.jsxs(
9593
- "div",
9594
- {
9595
- className: "relative w-full max-w-md rounded-xl border overflow-hidden max-h-[90vh] overflow-y-auto",
9596
- style: {
9597
- backgroundColor: "var(--compass-color-background)",
9598
- borderColor: "var(--compass-color-border)",
9599
- scrollbarWidth: "none"
9600
- },
9601
- onClick: (e) => e.stopPropagation(),
9602
- children: [
9603
- /* @__PURE__ */ jsxRuntime.jsxs(
9604
- "div",
9605
- {
9606
- className: "sticky top-0 flex items-center justify-between p-4 border-b",
9607
- style: {
9608
- backgroundColor: "var(--compass-color-surface)",
9609
- borderColor: "var(--compass-color-border)"
9610
- },
9611
- children: [
9612
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
9613
- /* @__PURE__ */ jsxRuntime.jsx(
9614
- "h2",
9615
- {
9616
- className: "font-semibold text-lg",
9617
- style: { color: "var(--compass-color-text)" },
9618
- children: position.name
9619
- }
9620
- ),
9621
- /* @__PURE__ */ jsxRuntime.jsx(
9622
- "p",
9623
- {
9624
- className: "text-sm",
9625
- style: { color: "var(--compass-color-text-secondary)" },
9626
- children: position.venueName
9627
- }
9628
- )
9629
- ] }),
9630
- /* @__PURE__ */ jsxRuntime.jsx(
9631
- "button",
9632
- {
9633
- onClick: onClose,
9634
- className: "p-2 rounded-lg hover:opacity-80 transition-opacity",
9635
- style: { backgroundColor: "var(--compass-color-background)" },
9636
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 20, style: { color: "var(--compass-color-text-secondary)" } })
9637
- }
9638
- )
9639
- ]
9640
- }
9641
- ),
9642
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 border-b", style: { borderColor: "var(--compass-color-border)" }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
9643
- /* @__PURE__ */ jsxRuntime.jsx(
9644
- "p",
9645
- {
9646
- className: "text-sm mb-1",
9647
- style: { color: "var(--compass-color-text-secondary)" },
9648
- children: "Current Balance"
9649
- }
9650
- ),
9651
- /* @__PURE__ */ jsxRuntime.jsx(
9652
- "p",
9653
- {
9654
- className: "text-3xl font-bold",
9655
- style: { color: "var(--compass-color-text)" },
9656
- children: formatUSD(position.balanceUsd)
9657
- }
9658
- ),
9659
- /* @__PURE__ */ jsxRuntime.jsxs(
9660
- "p",
9661
- {
9662
- className: "text-sm font-mono mt-1",
9663
- style: { color: "var(--compass-color-text-secondary)" },
9664
- children: [
9665
- formatAmount(position.balance),
9666
- " ",
9667
- position.assetSymbol
9668
- ]
9669
- }
9670
- )
9671
- ] }) }),
9672
- hasStats && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3 p-4 border-b", style: { borderColor: "var(--compass-color-border)" }, children: [
9673
- position.apy && /* @__PURE__ */ jsxRuntime.jsxs(
9674
- "div",
9675
- {
9676
- className: "p-3 rounded-lg",
9677
- style: { backgroundColor: "var(--compass-color-surface)" },
9678
- children: [
9679
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
9680
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Percent, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
9681
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "APY" })
9682
- ] }),
9683
- /* @__PURE__ */ jsxRuntime.jsxs(
9684
- "p",
9685
- {
9686
- className: "font-semibold",
9687
- style: { color: "var(--compass-color-success)" },
9688
- children: [
9689
- parseFloat(position.apy).toFixed(2),
9690
- "%"
9691
- ]
9692
- }
9693
- )
9694
- ]
9695
- }
9696
- ),
9697
- isPendle && duration && /* @__PURE__ */ jsxRuntime.jsxs(
9698
- "div",
9699
- {
9700
- className: "p-3 rounded-lg",
9701
- style: { backgroundColor: "var(--compass-color-surface)" },
9702
- children: [
9703
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
9704
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
9705
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Duration" })
9706
- ] }),
9707
- /* @__PURE__ */ jsxRuntime.jsx(
9708
- "p",
9709
- {
9710
- className: "font-semibold",
9711
- style: { color: "var(--compass-color-text)" },
9712
- children: duration
9713
- }
9714
- )
9715
- ]
9716
- }
9717
- ),
9718
- isPendle && expiryDate && /* @__PURE__ */ jsxRuntime.jsxs(
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: [
10185
+ /* @__PURE__ */ jsxRuntime.jsxs(
9719
10186
  "div",
9720
10187
  {
9721
- className: "p-3 rounded-lg",
9722
- 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
+ },
9723
10193
  children: [
9724
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
9725
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Calendar, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
9726
- /* @__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
+ )
9727
10211
  ] }),
9728
10212
  /* @__PURE__ */ jsxRuntime.jsx(
9729
- "p",
10213
+ "button",
9730
10214
  {
9731
- className: "font-semibold",
9732
- style: { color: "var(--compass-color-text)" },
9733
- 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)" } })
9734
10219
  }
9735
10220
  )
9736
10221
  ]
9737
10222
  }
9738
10223
  ),
9739
- position.pnl && /* @__PURE__ */ jsxRuntime.jsxs(
9740
- "div",
9741
- {
9742
- className: "p-3 rounded-lg",
9743
- style: { backgroundColor: "var(--compass-color-surface)" },
9744
- children: [
9745
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
9746
- 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)" } }),
9747
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Total P&L" })
9748
- ] }),
9749
- /* @__PURE__ */ jsxRuntime.jsxs(
9750
- "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",
9751
10362
  {
9752
- className: "font-semibold",
9753
- style: {
9754
- color: isPnlPositive ? "var(--compass-color-success)" : "var(--compass-color-error)"
9755
- },
10363
+ className: "flex items-center justify-between p-3 rounded-lg",
10364
+ style: { backgroundColor: "var(--compass-color-surface)" },
9756
10365
  children: [
9757
- isPnlPositive ? "+" : "",
9758
- formatUSD(position.pnl.totalPnl)
9759
- ]
9760
- }
9761
- )
9762
- ]
9763
- }
9764
- )
9765
- ] }),
9766
- (position.deposits.length > 0 || position.withdrawals.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
9767
- /* @__PURE__ */ jsxRuntime.jsx(
9768
- "h3",
9769
- {
9770
- className: "font-semibold mb-3",
9771
- style: { color: "var(--compass-color-text)" },
9772
- children: "Transaction History"
9773
- }
9774
- ),
9775
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
9776
- position.deposits.map((tx, i) => {
9777
- const date = formatDate(tx.timestamp);
9778
- return /* @__PURE__ */ jsxRuntime.jsxs(
9779
- "div",
9780
- {
9781
- className: "flex items-center justify-between p-3 rounded-lg",
9782
- style: { backgroundColor: "var(--compass-color-surface)" },
9783
- children: [
9784
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
9785
- /* @__PURE__ */ jsxRuntime.jsxs(
9786
- "p",
9787
- {
9788
- className: "font-medium",
9789
- style: { color: "var(--compass-color-success)" },
9790
- children: [
9791
- "+",
9792
- formatAmount(tx.amount),
9793
- " ",
9794
- position.assetSymbol
9795
- ]
9796
- }
9797
- ),
9798
- /* @__PURE__ */ jsxRuntime.jsx(
9799
- "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",
9800
10391
  {
9801
- className: "text-xs",
9802
- style: { color: "var(--compass-color-text-tertiary)" },
9803
- 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)" } })
9804
10398
  }
9805
10399
  )
9806
- ] }),
9807
- tx.txHash && /* @__PURE__ */ jsxRuntime.jsx(
9808
- "a",
9809
- {
9810
- href: `https://etherscan.io/tx/${tx.txHash}`,
9811
- target: "_blank",
9812
- rel: "noopener noreferrer",
9813
- className: "p-2 rounded-lg hover:opacity-80 transition-opacity",
9814
- style: { backgroundColor: "var(--compass-color-background)" },
9815
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
9816
- }
9817
- )
9818
- ]
9819
- },
9820
- `deposit-${i}`
9821
- );
9822
- }),
9823
- position.withdrawals.map((tx, i) => {
9824
- const date = formatDate(tx.timestamp);
9825
- return /* @__PURE__ */ jsxRuntime.jsxs(
9826
- "div",
9827
- {
9828
- className: "flex items-center justify-between p-3 rounded-lg",
9829
- style: { backgroundColor: "var(--compass-color-surface)" },
9830
- children: [
9831
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
9832
- /* @__PURE__ */ jsxRuntime.jsxs(
9833
- "p",
9834
- {
9835
- className: "font-medium",
9836
- style: { color: "var(--compass-color-error)" },
9837
- children: [
9838
- "-",
9839
- formatAmount(tx.amount),
9840
- " ",
9841
- position.assetSymbol
9842
- ]
9843
- }
9844
- ),
9845
- /* @__PURE__ */ jsxRuntime.jsx(
9846
- "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",
9847
10438
  {
9848
- className: "text-xs",
9849
- style: { color: "var(--compass-color-text-tertiary)" },
9850
- 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)" } })
9851
10445
  }
9852
10446
  )
9853
- ] }),
9854
- tx.txHash && /* @__PURE__ */ jsxRuntime.jsx(
9855
- "a",
9856
- {
9857
- href: `https://etherscan.io/tx/${tx.txHash}`,
9858
- target: "_blank",
9859
- rel: "noopener noreferrer",
9860
- className: "p-2 rounded-lg hover:opacity-80 transition-opacity",
9861
- style: { backgroundColor: "var(--compass-color-background)" },
9862
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
9863
- }
9864
- )
9865
- ]
9866
- },
9867
- `withdraw-${i}`
9868
- );
9869
- })
10447
+ ]
10448
+ },
10449
+ `withdraw-${i}`
10450
+ );
10451
+ })
10452
+ ] })
9870
10453
  ] })
9871
- ] })
9872
- ]
9873
- }
9874
- )
10454
+ ]
10455
+ }
10456
+ )
10457
+ ]
9875
10458
  }
9876
10459
  );
9877
10460
  }
@@ -10640,7 +11223,7 @@ function AllocationEditor({
10640
11223
  ) })
10641
11224
  ] });
10642
11225
  }
10643
- var EVM_CHAIN_IDS3 = {
11226
+ var EVM_CHAIN_IDS4 = {
10644
11227
  ethereum: 1,
10645
11228
  base: 8453,
10646
11229
  arbitrum: 42161
@@ -10674,7 +11257,7 @@ function RebalancingWidget({
10674
11257
  }) {
10675
11258
  const { chainId: contextChainId, setChainId } = useChain();
10676
11259
  const CHAIN_ID = chain || contextChainId;
10677
- const { address, signTypedData, isConnected, login, logout, switchChain, walletChainId } = useEmbeddableWallet();
11260
+ const { address, signTypedData, isConnected, login, logout, switchChain, walletChainId, fundWallet, hasExternalWallet } = useEmbeddableWallet();
10678
11261
  const { earnAccountAddress } = useEarnAccount();
10679
11262
  const queryClient = reactQuery.useQueryClient();
10680
11263
  const { portfolio, earnAccountMarkets, isMarketsLoading, isLoading, isError, error, refetch } = useRebalancingData(chain);
@@ -10710,6 +11293,7 @@ function RebalancingWidget({
10710
11293
  const [txHash, setTxHash] = react.useState(null);
10711
11294
  const [txConfirmed, setTxConfirmed] = react.useState(false);
10712
11295
  const [hasInitializedTargets, setHasInitializedTargets] = react.useState(false);
11296
+ const [originalAllocations, setOriginalAllocations] = react.useState({});
10713
11297
  const [isSwapModalOpen, setIsSwapModalOpen] = react.useState(false);
10714
11298
  const [isBalancesModalOpen, setIsBalancesModalOpen] = react.useState(false);
10715
11299
  const [isAddMarketExpanded, setIsAddMarketExpanded] = react.useState(false);
@@ -10722,12 +11306,14 @@ function RebalancingWidget({
10722
11306
  const [selectedToken, setSelectedToken] = react.useState("USDC");
10723
11307
  const [depositAmount, setDepositAmount] = react.useState("");
10724
11308
  const [isTokenDropdownOpen, setIsTokenDropdownOpen] = react.useState(false);
10725
- const [isProcessing, setIsProcessing] = react.useState(false);
10726
- const [depositError, setDepositError] = react.useState(null);
10727
- const [depositStatus, setDepositStatus] = react.useState("");
11309
+ const [depositTxState, setDepositTxState] = react.useState({ status: "idle" });
10728
11310
  const earnBalanceRef = react.useRef(null);
11311
+ const { startPolling: startDepositPolling, clearPolling: clearDepositPolling } = useTxPolling({
11312
+ queryKeysToInvalidate: [["earnAccountBalances"], ["rebalancing"]]
11313
+ });
10729
11314
  react.useEffect(() => {
10730
11315
  setTargets([]);
11316
+ setOriginalAllocations({});
10731
11317
  setPreviewPlan(null);
10732
11318
  setServerPreview(null);
10733
11319
  setWidgetState("editing");
@@ -10739,20 +11325,26 @@ function RebalancingWidget({
10739
11325
  setIsSwapModalOpen(false);
10740
11326
  setSelectedMarket(null);
10741
11327
  setDepositAmount("");
10742
- setDepositError(null);
10743
- setDepositStatus("");
10744
- }, [CHAIN_ID]);
11328
+ setDepositTxState({ status: "idle" });
11329
+ clearDepositPolling();
11330
+ }, [CHAIN_ID, clearDepositPolling]);
10745
11331
  react.useEffect(() => {
10746
11332
  if (portfolio && portfolio.positions.length > 0 && !hasInitializedTargets && !isLoading) {
11333
+ const origAllocs = {};
10747
11334
  setTargets(
10748
- portfolio.positions.map((p) => ({
10749
- venueType: p.venueType,
10750
- venueAddress: p.venueAddress,
10751
- venueName: p.venueName,
10752
- token: p.token,
10753
- targetPercent: parseFloat(p.allocationPercent.toFixed(2))
10754
- }))
11335
+ portfolio.positions.map((p) => {
11336
+ const pct = parseFloat(p.allocationPercent.toFixed(2));
11337
+ origAllocs[`${p.venueType}-${p.venueAddress.toLowerCase()}`] = pct;
11338
+ return {
11339
+ venueType: p.venueType,
11340
+ venueAddress: p.venueAddress,
11341
+ venueName: p.venueName,
11342
+ token: p.token,
11343
+ targetPercent: pct
11344
+ };
11345
+ })
10755
11346
  );
11347
+ setOriginalAllocations(origAllocs);
10756
11348
  setHasInitializedTargets(true);
10757
11349
  }
10758
11350
  }, [portfolio, hasInitializedTargets, isLoading]);
@@ -10784,15 +11376,21 @@ function RebalancingWidget({
10784
11376
  }, [portfolio, targets, hasChanges, defaultSlippage, minRebalanceThresholdUsd]);
10785
11377
  const handleResetToCurrent = react.useCallback(() => {
10786
11378
  if (!portfolio) return;
11379
+ const origAllocs = {};
10787
11380
  setTargets(
10788
- portfolio.positions.map((p) => ({
10789
- venueType: p.venueType,
10790
- venueAddress: p.venueAddress,
10791
- venueName: p.venueName,
10792
- token: p.token,
10793
- targetPercent: parseFloat(p.allocationPercent.toFixed(2))
10794
- }))
11381
+ portfolio.positions.map((p) => {
11382
+ const pct = parseFloat(p.allocationPercent.toFixed(2));
11383
+ origAllocs[`${p.venueType}-${p.venueAddress.toLowerCase()}`] = pct;
11384
+ return {
11385
+ venueType: p.venueType,
11386
+ venueAddress: p.venueAddress,
11387
+ venueName: p.venueName,
11388
+ token: p.token,
11389
+ targetPercent: pct
11390
+ };
11391
+ })
10795
11392
  );
11393
+ setOriginalAllocations(origAllocs);
10796
11394
  setPreviewPlan(null);
10797
11395
  setServerPreview(null);
10798
11396
  setWidgetState("editing");
@@ -10810,7 +11408,7 @@ function RebalancingWidget({
10810
11408
  setTargets((prev) => prev.map((t, i) => i === index ? { ...t, targetPercent: rounded } : t));
10811
11409
  }, []);
10812
11410
  const ensureCorrectChain = react.useCallback(async () => {
10813
- const targetChainId = EVM_CHAIN_IDS3[CHAIN_ID];
11411
+ const targetChainId = EVM_CHAIN_IDS4[CHAIN_ID];
10814
11412
  if (!targetChainId) return;
10815
11413
  if (walletChainId !== void 0 && walletChainId !== targetChainId) {
10816
11414
  if (!switchChain) {
@@ -10835,7 +11433,8 @@ function RebalancingWidget({
10835
11433
  venueType: t.venueType === "vault" ? "VAULT" : t.venueType === "aave" ? "AAVE" : "PENDLE_PT",
10836
11434
  venueAddress: t.venueAddress,
10837
11435
  targetPercent: t.targetPercent,
10838
- token: t.token
11436
+ token: t.token,
11437
+ originalPercent: originalAllocations[`${t.venueType}-${t.venueAddress.toLowerCase()}`] ?? 0
10839
11438
  })),
10840
11439
  slippage: defaultSlippage
10841
11440
  })
@@ -10853,7 +11452,7 @@ function RebalancingWidget({
10853
11452
  setWidgetState("error");
10854
11453
  onError?.(err instanceof Error ? err : new Error("Preview failed"));
10855
11454
  }
10856
- }, [portfolio, hasChanges, address, CHAIN_ID, targets, defaultSlippage, clientPreview, onError, ensureCorrectChain]);
11455
+ }, [portfolio, hasChanges, address, CHAIN_ID, targets, defaultSlippage, clientPreview, onError, ensureCorrectChain, originalAllocations]);
10857
11456
  const handleExecute = react.useCallback(async () => {
10858
11457
  if (!serverPreview?.eip712 || !address) return;
10859
11458
  setWidgetState("signing");
@@ -10941,13 +11540,11 @@ function RebalancingWidget({
10941
11540
  };
10942
11541
  const handleDeposit = react.useCallback(async () => {
10943
11542
  if (!selectedMarket || !depositAmount || parseFloat(depositAmount) <= 0 || !address || !signTypedData) return;
10944
- setIsProcessing(true);
10945
- setDepositError(null);
11543
+ setDepositTxState({ status: "preparing" });
10946
11544
  const underlyingToken = selectedMarket.underlyingToken;
10947
11545
  try {
10948
11546
  let resultTxHash;
10949
11547
  if (needsSwap) {
10950
- setDepositStatus("Getting swap quote...");
10951
11548
  const quoteResponse = await fetch(
10952
11549
  `/api/compass/swap/quote?owner=${address}&chain=${CHAIN_ID}&tokenIn=${selectedToken}&tokenOut=${underlyingToken}&amountIn=${depositAmount}`
10953
11550
  );
@@ -10961,7 +11558,6 @@ function RebalancingWidget({
10961
11558
  throw new Error("Invalid swap quote - no output amount");
10962
11559
  }
10963
11560
  const actualDepositAmount = (parseFloat(estimatedOutput) * 0.99999).toString();
10964
- setDepositStatus("Preparing swap and deposit...");
10965
11561
  const bundleActions = [
10966
11562
  {
10967
11563
  body: {
@@ -10995,7 +11591,7 @@ function RebalancingWidget({
10995
11591
  throw new Error(errorData.error || "Failed to prepare bundle");
10996
11592
  }
10997
11593
  const { eip712, normalizedTypes, domain, message } = await prepareResponse.json();
10998
- setDepositStatus("Please sign the transaction...");
11594
+ setDepositTxState({ status: "signing" });
10999
11595
  await ensureCorrectChain();
11000
11596
  const signature = await signTypedData({
11001
11597
  domain,
@@ -11003,7 +11599,7 @@ function RebalancingWidget({
11003
11599
  primaryType: "SafeTx",
11004
11600
  message
11005
11601
  });
11006
- setDepositStatus("Executing swap and deposit...");
11602
+ setDepositTxState({ status: "broadcasting" });
11007
11603
  const executeResponse = await fetch("/api/compass/bundle/execute", {
11008
11604
  method: "POST",
11009
11605
  headers: { "Content-Type": "application/json" },
@@ -11021,7 +11617,6 @@ function RebalancingWidget({
11021
11617
  const result = await executeResponse.json();
11022
11618
  resultTxHash = result.txHash;
11023
11619
  } else {
11024
- setDepositStatus("Preparing deposit...");
11025
11620
  const venueParams = buildVenueParamsFromMarket(selectedMarket);
11026
11621
  const prepareResponse = await fetch("/api/compass/deposit/prepare", {
11027
11622
  method: "POST",
@@ -11038,7 +11633,7 @@ function RebalancingWidget({
11038
11633
  throw new Error(errData.error || "Failed to prepare deposit");
11039
11634
  }
11040
11635
  const prepareData = await prepareResponse.json();
11041
- setDepositStatus("Please sign the transaction...");
11636
+ setDepositTxState({ status: "signing" });
11042
11637
  await ensureCorrectChain();
11043
11638
  const signature = await signTypedData({
11044
11639
  domain: prepareData.domain,
@@ -11046,7 +11641,7 @@ function RebalancingWidget({
11046
11641
  primaryType: "SafeTx",
11047
11642
  message: prepareData.message
11048
11643
  });
11049
- setDepositStatus("Executing deposit...");
11644
+ setDepositTxState({ status: "broadcasting" });
11050
11645
  const executeResponse = await fetch("/api/compass/deposit/execute", {
11051
11646
  method: "POST",
11052
11647
  headers: { "Content-Type": "application/json" },
@@ -11064,7 +11659,8 @@ function RebalancingWidget({
11064
11659
  const result = await executeResponse.json();
11065
11660
  resultTxHash = result.txHash;
11066
11661
  }
11067
- setDepositStatus("Deposit successful!");
11662
+ setDepositTxState({ status: "submitted", txHash: resultTxHash });
11663
+ startDepositPolling(resultTxHash, setDepositTxState);
11068
11664
  setDepositAmount("");
11069
11665
  queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
11070
11666
  queryClient.invalidateQueries({ queryKey: ["rebalancing"] });
@@ -11076,15 +11672,11 @@ function RebalancingWidget({
11076
11672
  refetch();
11077
11673
  }, delay);
11078
11674
  }
11079
- setTimeout(() => setDepositStatus(""), 3e3);
11080
11675
  } catch (err) {
11081
- setDepositError(err instanceof Error ? err.message : "Deposit failed");
11082
- setDepositStatus("");
11676
+ setDepositTxState({ status: "failed", error: err instanceof Error ? err.message : "Deposit failed" });
11083
11677
  onError?.(err instanceof Error ? err : new Error("Deposit failed"));
11084
- } finally {
11085
- setIsProcessing(false);
11086
11678
  }
11087
- }, [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]);
11088
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: [
11089
11681
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minHeight: 0, overflowY: "auto", scrollbarWidth: "none", display: "flex", flexDirection: "column" }, children: [
11090
11682
  showChainSwitcher && !chain && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "0 4px", marginBottom: "8px" }, children: /* @__PURE__ */ jsxRuntime.jsx(ChainSwitcher, {}) }),
@@ -11113,8 +11705,19 @@ function RebalancingWidget({
11113
11705
  }
11114
11706
  )
11115
11707
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11116
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg font-semibold mb-2", style: { color: "var(--compass-color-text)" }, children: "No positions found" }),
11117
- /* @__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
+ ) })
11118
11721
  ] }) }),
11119
11722
  state !== "loading" && state !== "empty" && portfolio && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11120
11723
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center text-center", style: { padding: "16px 16px 12px" }, children: [
@@ -11281,7 +11884,7 @@ function RebalancingWidget({
11281
11884
  selectedMarket,
11282
11885
  onMarketSelect: (market) => {
11283
11886
  setSelectedMarket(market);
11284
- setDepositError(null);
11887
+ if (depositTxState.status === "failed") setDepositTxState({ status: "idle" });
11285
11888
  },
11286
11889
  isLoading: isMarketsLoading
11287
11890
  }
@@ -11304,10 +11907,10 @@ function RebalancingWidget({
11304
11907
  value: depositAmount,
11305
11908
  onChange: (e) => {
11306
11909
  setDepositAmount(e.target.value);
11307
- setDepositError(null);
11910
+ if (depositTxState.status === "failed") setDepositTxState({ status: "idle" });
11308
11911
  },
11309
11912
  placeholder: "0.00",
11310
- disabled: isProcessing,
11913
+ disabled: depositTxState.status !== "idle" && depositTxState.status !== "confirmed" && depositTxState.status !== "failed",
11311
11914
  className: "flex-1 font-bold bg-transparent outline-none",
11312
11915
  style: {
11313
11916
  color: "var(--compass-color-text)",
@@ -11323,7 +11926,7 @@ function RebalancingWidget({
11323
11926
  "button",
11324
11927
  {
11325
11928
  onClick: () => setIsTokenDropdownOpen(!isTokenDropdownOpen),
11326
- disabled: isProcessing,
11929
+ disabled: depositTxState.status !== "idle" && depositTxState.status !== "confirmed" && depositTxState.status !== "failed",
11327
11930
  className: "flex items-center font-semibold",
11328
11931
  style: {
11329
11932
  backgroundColor: "var(--compass-color-surface-hover, var(--compass-color-surface))",
@@ -11360,7 +11963,7 @@ function RebalancingWidget({
11360
11963
  onClick: () => {
11361
11964
  setSelectedToken(token);
11362
11965
  setIsTokenDropdownOpen(false);
11363
- setDepositError(null);
11966
+ if (depositTxState.status === "failed") setDepositTxState({ status: "idle" });
11364
11967
  },
11365
11968
  className: "w-full text-left font-medium",
11366
11969
  style: {
@@ -11386,7 +11989,7 @@ function RebalancingWidget({
11386
11989
  "button",
11387
11990
  {
11388
11991
  onClick: () => setDepositAmount(earnBalancesQuery.toString()),
11389
- disabled: isProcessing,
11992
+ disabled: depositTxState.status !== "idle" && depositTxState.status !== "confirmed" && depositTxState.status !== "failed",
11390
11993
  className: "font-semibold",
11391
11994
  style: { color: "var(--compass-color-primary)", fontSize: "0.8125rem", background: "none", border: "none", padding: 0 },
11392
11995
  children: [
@@ -11423,26 +12026,12 @@ function RebalancingWidget({
11423
12026
  ]
11424
12027
  }
11425
12028
  ),
11426
- depositError && /* @__PURE__ */ jsxRuntime.jsx(
11427
- "div",
11428
- {
11429
- className: "flex items-center",
11430
- style: {
11431
- backgroundColor: "var(--compass-color-error-muted)",
11432
- color: "var(--compass-color-error)",
11433
- borderRadius: "var(--compass-border-radius-lg)",
11434
- padding: "12px 14px",
11435
- gap: "8px",
11436
- fontSize: "0.875rem"
11437
- },
11438
- children: depositError
11439
- }
11440
- ),
12029
+ /* @__PURE__ */ jsxRuntime.jsx(TxStatus, { state: depositTxState }),
11441
12030
  /* @__PURE__ */ jsxRuntime.jsx(
11442
12031
  "button",
11443
12032
  {
11444
12033
  onClick: !isConnected && login ? login : handleDeposit,
11445
- 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),
11446
12035
  className: "w-full font-bold",
11447
12036
  style: {
11448
12037
  backgroundColor: "var(--compass-color-primary)",
@@ -11453,26 +12042,13 @@ function RebalancingWidget({
11453
12042
  fontSize: "1rem",
11454
12043
  transition: "all 200ms ease",
11455
12044
  border: "none",
11456
- 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
11457
12046
  },
11458
- 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: [
11459
12048
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 16, className: "animate-spin" }),
11460
12049
  "Processing..."
11461
12050
  ] }) : !selectedMarket ? "Select a market" : needsSwap ? "Swap & Deposit" : "Deposit"
11462
12051
  }
11463
- ),
11464
- depositStatus && !depositError && /* @__PURE__ */ jsxRuntime.jsx(
11465
- "div",
11466
- {
11467
- className: "text-sm text-center",
11468
- style: {
11469
- backgroundColor: depositStatus.includes("successful") ? "var(--compass-color-success-muted)" : "var(--compass-color-primary-muted, rgba(99, 102, 241, 0.1))",
11470
- color: depositStatus.includes("successful") ? "var(--compass-color-success)" : "var(--compass-color-primary)",
11471
- borderRadius: "var(--compass-border-radius-lg)",
11472
- padding: "10px 12px"
11473
- },
11474
- children: depositStatus
11475
- }
11476
12052
  )
11477
12053
  ] }),
11478
12054
  clientPreview && clientPreview.actions.length > 0 && state === "editing" && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -11489,11 +12065,7 @@ function RebalancingWidget({
11489
12065
  clientPreview.warnings.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 flex flex-col gap-1", children: clientPreview.warnings.map((w, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 text-xs", style: { color: "var(--compass-color-warning, #eab308)" }, children: [
11490
12066
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { size: 12, className: "flex-shrink-0 mt-0.5" }),
11491
12067
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: w })
11492
- ] }, i)) }),
11493
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs mt-2", style: { color: "var(--compass-color-text-tertiary)" }, children: [
11494
- "Estimated gas savings: ",
11495
- clientPreview.estimatedGasSavings
11496
- ] })
12068
+ ] }, i)) })
11497
12069
  ]
11498
12070
  }
11499
12071
  ),
@@ -11736,46 +12308,8 @@ function RebalancingWidget({
11736
12308
  onClose: () => setIsBalancesModalOpen(false),
11737
12309
  title: "Balance Breakdown",
11738
12310
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "12px" }, children: [
11739
- address && /* @__PURE__ */ jsxRuntime.jsxs(
11740
- "div",
11741
- {
11742
- className: "flex items-center justify-between",
11743
- style: {
11744
- backgroundColor: "var(--compass-color-surface)",
11745
- borderRadius: "var(--compass-border-radius-lg)",
11746
- padding: "10px 16px",
11747
- border: "1.5px solid var(--compass-color-border)"
11748
- },
11749
- children: [
11750
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-tertiary)", fontSize: "0.8125rem" }, children: "Wallet" }),
11751
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "var(--compass-color-text)", fontSize: "0.8125rem", fontFamily: "monospace" }, children: [
11752
- address.slice(0, 6),
11753
- "...",
11754
- address.slice(-4)
11755
- ] })
11756
- ]
11757
- }
11758
- ),
11759
- earnAccountAddress && /* @__PURE__ */ jsxRuntime.jsxs(
11760
- "div",
11761
- {
11762
- className: "flex items-center justify-between",
11763
- style: {
11764
- backgroundColor: "var(--compass-color-surface)",
11765
- borderRadius: "var(--compass-border-radius-lg)",
11766
- padding: "10px 16px",
11767
- border: "1.5px solid var(--compass-color-border)"
11768
- },
11769
- children: [
11770
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-tertiary)", fontSize: "0.8125rem" }, children: "Product Account" }),
11771
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "var(--compass-color-text)", fontSize: "0.8125rem", fontFamily: "monospace" }, children: [
11772
- earnAccountAddress.slice(0, 6),
11773
- "...",
11774
- earnAccountAddress.slice(-4)
11775
- ] })
11776
- ]
11777
- }
11778
- ),
12311
+ address && /* @__PURE__ */ jsxRuntime.jsx(CopyableAddress, { address, label: "Wallet" }),
12312
+ earnAccountAddress && /* @__PURE__ */ jsxRuntime.jsx(CopyableAddress, { address: earnAccountAddress, label: "Product Account" }),
11779
12313
  portfolio && portfolio.idleBalances.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11780
12314
  /* @__PURE__ */ jsxRuntime.jsx(
11781
12315
  "span",