@compass-labs/widgets 0.1.26 → 0.1.27

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
@@ -1404,6 +1404,7 @@ function TokenSelector({
1404
1404
  borderColor: "var(--compass-color-border)",
1405
1405
  boxShadow: "var(--compass-shadow-lg)",
1406
1406
  maxHeight: "200px",
1407
+ scrollbarWidth: "none",
1407
1408
  borderRadius: "var(--compass-border-radius-lg)"
1408
1409
  },
1409
1410
  children: tokens.map((token) => {
@@ -1967,7 +1968,8 @@ function TransactionHistory({
1967
1968
  className: "mt-2 max-h-48 overflow-y-auto border",
1968
1969
  style: {
1969
1970
  borderColor: "var(--compass-color-border)",
1970
- borderRadius: "var(--compass-border-radius-lg)"
1971
+ borderRadius: "var(--compass-border-radius-lg)",
1972
+ scrollbarWidth: "none"
1971
1973
  },
1972
1974
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { children: allEvents.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs(
1973
1975
  "div",
@@ -2451,10 +2453,11 @@ function AccountBalancesModal({
2451
2453
  ] });
2452
2454
  }
2453
2455
  var TRANSFER_TOKENS = ["USDC"];
2454
- function EarnAccountBalance({
2456
+ var EarnAccountBalance = react.forwardRef(function EarnAccountBalance2({
2455
2457
  compact = false,
2458
+ hideVisual = false,
2456
2459
  onTransferComplete
2457
- }) {
2460
+ }, ref) {
2458
2461
  const [isModalOpen, setIsModalOpen] = react.useState(false);
2459
2462
  const [activeAction, setActiveAction] = react.useState("deposit");
2460
2463
  const [selectedToken, setSelectedToken] = react.useState(TRANSFER_TOKENS[0]);
@@ -2528,6 +2531,9 @@ function EarnAccountBalance({
2528
2531
  resetForm();
2529
2532
  setIsModalOpen(true);
2530
2533
  };
2534
+ react.useImperativeHandle(ref, () => ({
2535
+ openTransferModal: handleOpenModal
2536
+ }));
2531
2537
  const handleCloseModal = () => {
2532
2538
  setIsModalOpen(false);
2533
2539
  resetForm();
@@ -2690,7 +2696,7 @@ function EarnAccountBalance({
2690
2696
  return null;
2691
2697
  }
2692
2698
  const isProcessing = transferState !== "idle" && transferState !== "success" && transferState !== "error";
2693
- if (!isDeployed) {
2699
+ if (!isDeployed && !hideVisual) {
2694
2700
  return /* @__PURE__ */ jsxRuntime.jsxs(
2695
2701
  "div",
2696
2702
  {
@@ -2716,44 +2722,46 @@ function EarnAccountBalance({
2716
2722
  );
2717
2723
  }
2718
2724
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2719
- /* @__PURE__ */ jsxRuntime.jsxs(
2720
- "button",
2721
- {
2722
- onClick: () => setIsBalancesModalOpen(true),
2723
- className: `flex items-center gap-2 border transition-colors hover:border-[var(--compass-color-primary)] ${compact ? "px-2 py-1.5" : "px-3 py-2"}`,
2724
- style: {
2725
- backgroundColor: "var(--compass-color-surface)",
2726
- borderColor: "var(--compass-color-border)",
2727
- cursor: "pointer",
2728
- borderRadius: "var(--compass-border-radius-lg)",
2729
- fontFamily: "var(--compass-font-family)"
2730
- },
2731
- children: [
2732
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Wallet, { size: compact ? 14 : 16, style: { color: "var(--compass-color-primary)" } }),
2733
- balancesLoading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: compact ? 12 : 14, className: "animate-spin", style: { color: "var(--compass-color-text-secondary)" } }) : /* @__PURE__ */ jsxRuntime.jsx(
2734
- "span",
2735
- {
2736
- className: `font-medium ${compact ? "text-xs" : "text-sm"}`,
2737
- style: { color: "var(--compass-color-text)" },
2738
- children: formatUSD(totalUsdValue)
2739
- }
2740
- )
2741
- ]
2742
- }
2743
- ),
2744
- /* @__PURE__ */ jsxRuntime.jsx(
2745
- "button",
2746
- {
2747
- onClick: handleOpenModal,
2748
- className: `font-medium transition-colors ${compact ? "px-2 py-1 text-xs" : "px-3 py-1.5 text-sm"}`,
2749
- style: {
2750
- backgroundColor: "var(--compass-color-primary)",
2751
- color: "var(--compass-color-primary-text)",
2752
- borderRadius: "var(--compass-border-radius-md)"
2753
- },
2754
- children: "Fund"
2755
- }
2756
- ),
2725
+ !hideVisual && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2726
+ /* @__PURE__ */ jsxRuntime.jsxs(
2727
+ "button",
2728
+ {
2729
+ onClick: () => setIsBalancesModalOpen(true),
2730
+ className: `flex items-center gap-2 border transition-colors hover:border-[var(--compass-color-primary)] ${compact ? "px-2 py-1.5" : "px-3 py-2"}`,
2731
+ style: {
2732
+ backgroundColor: "var(--compass-color-surface)",
2733
+ borderColor: "var(--compass-color-border)",
2734
+ cursor: "pointer",
2735
+ borderRadius: "var(--compass-border-radius-lg)",
2736
+ fontFamily: "var(--compass-font-family)"
2737
+ },
2738
+ children: [
2739
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Wallet, { size: compact ? 14 : 16, style: { color: "var(--compass-color-primary)" } }),
2740
+ balancesLoading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: compact ? 12 : 14, className: "animate-spin", style: { color: "var(--compass-color-text-secondary)" } }) : /* @__PURE__ */ jsxRuntime.jsx(
2741
+ "span",
2742
+ {
2743
+ className: `font-medium ${compact ? "text-xs" : "text-sm"}`,
2744
+ style: { color: "var(--compass-color-text)" },
2745
+ children: formatUSD(totalUsdValue)
2746
+ }
2747
+ )
2748
+ ]
2749
+ }
2750
+ ),
2751
+ /* @__PURE__ */ jsxRuntime.jsx(
2752
+ "button",
2753
+ {
2754
+ onClick: handleOpenModal,
2755
+ className: `font-medium transition-colors ${compact ? "px-2 py-1 text-xs" : "px-3 py-1.5 text-sm"}`,
2756
+ style: {
2757
+ backgroundColor: "var(--compass-color-primary)",
2758
+ color: "var(--compass-color-primary-text)",
2759
+ borderRadius: "var(--compass-border-radius-md)"
2760
+ },
2761
+ children: "Fund"
2762
+ }
2763
+ )
2764
+ ] }),
2757
2765
  /* @__PURE__ */ jsxRuntime.jsx(
2758
2766
  AccountBalancesModal,
2759
2767
  {
@@ -2944,1270 +2952,435 @@ function EarnAccountBalance({
2944
2952
  }
2945
2953
  )
2946
2954
  ] });
2947
- }
2948
- function formatTVL(tvl) {
2949
- if (!tvl) return "$0";
2950
- const num = parseFloat(tvl);
2951
- if (isNaN(num)) return "$0";
2952
- if (num >= 1e9) return `$${(num / 1e9).toFixed(2)}B`;
2953
- if (num >= 1e6) return `$${(num / 1e6).toFixed(2)}M`;
2954
- if (num >= 1e3) return `$${(num / 1e3).toFixed(2)}K`;
2955
- return `$${num.toFixed(2)}`;
2956
- }
2957
- function formatAPY(apy) {
2958
- if (!apy) return "0.00%";
2959
- const num = parseFloat(apy);
2960
- if (isNaN(num)) return "0.00%";
2961
- return `${num.toFixed(2)}%`;
2962
- }
2963
- function VaultCard({
2964
- vault,
2965
- showApy,
2966
- sortBy,
2967
- showTvl,
2968
- showUserPosition,
2969
- onClick
2955
+ });
2956
+ var MARKET_TABS = [
2957
+ { value: "variable", label: "VARIABLE" },
2958
+ { value: "fixed", label: "FIXED" }
2959
+ ];
2960
+ var TAB_TO_MARKET_TYPES = {
2961
+ variable: ["aave", "vaults"],
2962
+ fixed: ["pendle"]
2963
+ };
2964
+ function MarketSelector({
2965
+ markets,
2966
+ selectedMarket,
2967
+ onMarketSelect,
2968
+ marketTab,
2969
+ onMarketTabChange,
2970
+ isLoading,
2971
+ allowedMarketIds
2970
2972
  }) {
2971
- const getDisplayApy = () => {
2972
- switch (sortBy) {
2973
- case "apy_30d":
2974
- return { value: vault.apy30d, period: "30d" };
2975
- case "apy_90d":
2976
- return { value: vault.apy90d, period: "90d" };
2977
- case "apy_7d":
2978
- case "tvl":
2979
- default:
2980
- return { value: vault.apy7d, period: "7d" };
2973
+ const [isDropdownOpen, setIsDropdownOpen] = react.useState(false);
2974
+ const dropdownRef = react.useRef(null);
2975
+ react.useEffect(() => {
2976
+ function handleClickOutside(e) {
2977
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
2978
+ setIsDropdownOpen(false);
2979
+ }
2981
2980
  }
2981
+ document.addEventListener("mousedown", handleClickOutside);
2982
+ return () => document.removeEventListener("mousedown", handleClickOutside);
2983
+ }, []);
2984
+ const allowedTypes = TAB_TO_MARKET_TYPES[marketTab];
2985
+ const showTvl = marketTab === "variable";
2986
+ const filteredMarkets = react.useMemo(() => {
2987
+ return markets.filter((m) => {
2988
+ if (!allowedTypes.includes(m.type)) return false;
2989
+ if (allowedMarketIds !== void 0) {
2990
+ return allowedMarketIds.includes(m.id);
2991
+ }
2992
+ return true;
2993
+ }).sort((a, b) => b.apy - a.apy);
2994
+ }, [markets, allowedTypes, allowedMarketIds]);
2995
+ const hasMarkets = filteredMarkets.length > 0;
2996
+ const formatTvl = (value) => {
2997
+ if (value >= 1e9) return `$${(value / 1e9).toFixed(1)}B`;
2998
+ if (value >= 1e6) return `$${(value / 1e6).toFixed(1)}M`;
2999
+ if (value >= 1e3) return `$${(value / 1e3).toFixed(0)}K`;
3000
+ return `$${value}`;
2982
3001
  };
2983
- const displayApy = getDisplayApy();
2984
- const hasPosition = vault.userPosition && parseFloat(vault.userPosition.balance) > 0;
2985
3002
  return /* @__PURE__ */ jsxRuntime.jsxs(
2986
- "button",
3003
+ "div",
2987
3004
  {
2988
- onClick,
2989
- className: "w-full border text-left hover:scale-[1.01]",
2990
3005
  style: {
2991
- backgroundColor: "var(--compass-color-surface)",
2992
- borderColor: hasPosition ? "var(--compass-color-primary)" : "var(--compass-color-border)",
2993
- borderRadius: "var(--compass-border-radius-xl)",
2994
- fontFamily: "var(--compass-font-family)",
2995
- padding: "var(--compass-spacing-card)",
2996
- transition: "var(--compass-transition-normal)"
3006
+ display: "flex",
3007
+ flexDirection: "column",
3008
+ gap: "calc(var(--compass-spacing-unit) * 1.5)"
2997
3009
  },
2998
3010
  children: [
2999
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
3000
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
3001
- /* @__PURE__ */ jsxRuntime.jsx(
3002
- "h3",
3003
- {
3004
- className: "font-semibold",
3005
- style: {
3006
- fontSize: "var(--compass-font-size-body)",
3007
- color: "var(--compass-color-text)"
3008
- },
3009
- children: vault.name
3010
- }
3011
- ),
3012
- /* @__PURE__ */ jsxRuntime.jsx(
3013
- "span",
3014
- {
3015
- className: "text-sm",
3016
- style: { color: "var(--compass-color-text-secondary)" },
3017
- children: vault.assetSymbol
3018
- }
3019
- )
3020
- ] }),
3021
- showApy && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-end", children: [
3022
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
3023
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { size: 16, style: { color: "var(--compass-color-success)" } }),
3024
- /* @__PURE__ */ jsxRuntime.jsx(
3025
- "span",
3011
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3012
+ /* @__PURE__ */ jsxRuntime.jsx(
3013
+ "label",
3014
+ {
3015
+ style: {
3016
+ display: "block",
3017
+ fontSize: "var(--compass-font-size-sm)",
3018
+ color: "var(--compass-color-text-secondary)",
3019
+ marginBottom: "var(--compass-spacing-unit)"
3020
+ },
3021
+ children: "Market Type"
3022
+ }
3023
+ ),
3024
+ /* @__PURE__ */ jsxRuntime.jsx(
3025
+ "div",
3026
+ {
3027
+ style: {
3028
+ display: "flex",
3029
+ borderRadius: "var(--compass-border-radius-md, 8px)",
3030
+ border: "1px solid var(--compass-color-border, rgba(255, 255, 255, 0.1))",
3031
+ overflow: "hidden"
3032
+ },
3033
+ children: MARKET_TABS.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
3034
+ "button",
3026
3035
  {
3027
- className: "font-mono text-lg font-bold",
3028
- style: { color: "var(--compass-color-success)" },
3029
- children: formatAPY(displayApy.value)
3030
- }
3031
- )
3032
- ] }),
3036
+ onClick: () => onMarketTabChange(tab.value),
3037
+ style: {
3038
+ flex: 1,
3039
+ padding: "6px 8px",
3040
+ backgroundColor: marketTab === tab.value ? "var(--compass-color-primary, #6366f1)" : "var(--compass-color-surface, #12121a)",
3041
+ color: marketTab === tab.value ? "var(--compass-color-primary-text, white)" : "var(--compass-color-text, #e4e4e7)",
3042
+ border: "none",
3043
+ cursor: "pointer",
3044
+ fontSize: "12px",
3045
+ fontWeight: 500,
3046
+ fontFamily: "var(--compass-font-family)",
3047
+ transition: "var(--compass-transition-normal, all 0.2s ease)"
3048
+ },
3049
+ children: tab.label
3050
+ },
3051
+ tab.value
3052
+ ))
3053
+ }
3054
+ )
3055
+ ] }),
3056
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3057
+ /* @__PURE__ */ jsxRuntime.jsx(
3058
+ "label",
3059
+ {
3060
+ style: {
3061
+ display: "block",
3062
+ fontSize: "var(--compass-font-size-sm)",
3063
+ color: "var(--compass-color-text-secondary)",
3064
+ marginBottom: "var(--compass-spacing-unit)"
3065
+ },
3066
+ children: "Select Market"
3067
+ }
3068
+ ),
3069
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: dropdownRef, style: { position: "relative" }, children: [
3033
3070
  /* @__PURE__ */ jsxRuntime.jsxs(
3034
- "span",
3071
+ "button",
3035
3072
  {
3036
- className: "text-xs",
3037
- style: { color: "var(--compass-color-text-tertiary)" },
3073
+ onClick: () => hasMarkets && setIsDropdownOpen(!isDropdownOpen),
3074
+ disabled: !hasMarkets || isLoading,
3075
+ style: {
3076
+ width: "100%",
3077
+ padding: "8px 12px",
3078
+ backgroundColor: "var(--compass-color-surface, #12121a)",
3079
+ border: "1px solid var(--compass-color-border, rgba(255, 255, 255, 0.1))",
3080
+ borderRadius: "var(--compass-border-radius-md, 8px)",
3081
+ fontSize: "13px",
3082
+ display: "flex",
3083
+ justifyContent: "space-between",
3084
+ alignItems: "center",
3085
+ cursor: hasMarkets ? "pointer" : "not-allowed",
3086
+ opacity: hasMarkets ? 1 : 0.5,
3087
+ fontFamily: "var(--compass-font-family)"
3088
+ },
3038
3089
  children: [
3039
- displayApy.period,
3040
- " APY"
3090
+ /* @__PURE__ */ jsxRuntime.jsx(
3091
+ "span",
3092
+ {
3093
+ style: {
3094
+ color: selectedMarket ? "var(--compass-color-text)" : "var(--compass-color-text-secondary)"
3095
+ },
3096
+ children: isLoading ? "Loading markets..." : !hasMarkets ? "No markets available" : selectedMarket ? selectedMarket.name : "Select a market"
3097
+ }
3098
+ ),
3099
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "var(--compass-spacing-unit)" }, children: [
3100
+ selectedMarket && /* @__PURE__ */ jsxRuntime.jsxs(
3101
+ "span",
3102
+ {
3103
+ style: {
3104
+ color: "var(--compass-color-success)",
3105
+ fontSize: "var(--compass-font-size-sm)"
3106
+ },
3107
+ children: [
3108
+ selectedMarket.apy.toFixed(2),
3109
+ "%"
3110
+ ]
3111
+ }
3112
+ ),
3113
+ /* @__PURE__ */ jsxRuntime.jsx(
3114
+ lucideReact.ChevronDown,
3115
+ {
3116
+ size: 16,
3117
+ style: {
3118
+ color: "var(--compass-color-text-secondary)",
3119
+ transform: isDropdownOpen ? "rotate(180deg)" : "rotate(0deg)",
3120
+ transition: "var(--compass-transition-normal)"
3121
+ }
3122
+ }
3123
+ )
3124
+ ] })
3041
3125
  ]
3042
3126
  }
3043
- )
3044
- ] })
3045
- ] }),
3046
- (showTvl || showUserPosition && hasPosition) && /* @__PURE__ */ jsxRuntime.jsxs(
3047
- "div",
3048
- {
3049
- className: "flex items-center justify-between border-t",
3050
- style: {
3051
- borderColor: "var(--compass-color-border)",
3052
- marginTop: "calc(var(--compass-spacing-unit) * 2)",
3053
- paddingTop: "calc(var(--compass-spacing-unit) * 2)"
3054
- },
3055
- children: [
3056
- showTvl && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
3057
- /* @__PURE__ */ jsxRuntime.jsx(
3058
- "span",
3059
- {
3060
- className: "text-xs",
3061
- style: { color: "var(--compass-color-text-tertiary)" },
3062
- children: "TVL"
3063
- }
3064
- ),
3065
- /* @__PURE__ */ jsxRuntime.jsx(
3066
- "span",
3067
- {
3068
- className: "font-mono text-sm",
3069
- style: { color: "var(--compass-color-text-secondary)" },
3070
- children: formatTVL(vault.tvlUsd)
3071
- }
3072
- )
3073
- ] }),
3074
- showUserPosition && hasPosition && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
3075
- /* @__PURE__ */ jsxRuntime.jsx(
3076
- "span",
3077
- {
3078
- className: "text-xs",
3079
- style: { color: "var(--compass-color-text-tertiary)" },
3080
- children: "Position"
3081
- }
3082
- ),
3083
- /* @__PURE__ */ jsxRuntime.jsxs(
3084
- "span",
3127
+ ),
3128
+ isDropdownOpen && /* @__PURE__ */ jsxRuntime.jsx(
3129
+ "div",
3130
+ {
3131
+ style: {
3132
+ position: "absolute",
3133
+ top: "100%",
3134
+ left: 0,
3135
+ right: 0,
3136
+ marginTop: "var(--compass-spacing-unit, 8px)",
3137
+ backgroundColor: "var(--compass-color-background, #0a0a0f)",
3138
+ border: "1px solid var(--compass-color-border, rgba(255, 255, 255, 0.1))",
3139
+ borderRadius: "var(--compass-border-radius-lg, 12px)",
3140
+ boxShadow: "var(--compass-shadow-lg, 0 25px 50px -12px rgba(0, 0, 0, 0.5))",
3141
+ zIndex: 50,
3142
+ maxHeight: "200px",
3143
+ overflowY: "auto",
3144
+ scrollbarWidth: "none"
3145
+ },
3146
+ children: filteredMarkets.map((market) => /* @__PURE__ */ jsxRuntime.jsxs(
3147
+ "button",
3085
3148
  {
3086
- className: "font-mono text-sm font-medium",
3087
- style: { color: "var(--compass-color-primary)" },
3149
+ onClick: () => {
3150
+ onMarketSelect(market);
3151
+ setIsDropdownOpen(false);
3152
+ },
3153
+ style: {
3154
+ width: "100%",
3155
+ padding: "calc(var(--compass-spacing-unit, 8px) * 2)",
3156
+ backgroundColor: selectedMarket?.id === market.id ? "var(--compass-color-primary-muted, rgba(99, 102, 241, 0.15))" : "var(--compass-color-surface, #12121a)",
3157
+ border: "none",
3158
+ borderBottom: "1px solid var(--compass-color-border, rgba(255, 255, 255, 0.1))",
3159
+ cursor: "pointer",
3160
+ textAlign: "left",
3161
+ fontFamily: "var(--compass-font-family)"
3162
+ },
3088
3163
  children: [
3089
- parseFloat(vault.userPosition.balance).toFixed(4),
3090
- " ",
3091
- vault.assetSymbol
3164
+ /* @__PURE__ */ jsxRuntime.jsxs(
3165
+ "div",
3166
+ {
3167
+ style: {
3168
+ display: "flex",
3169
+ justifyContent: "space-between",
3170
+ alignItems: "center"
3171
+ },
3172
+ children: [
3173
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text)" }, children: market.name }),
3174
+ /* @__PURE__ */ jsxRuntime.jsxs(
3175
+ "span",
3176
+ {
3177
+ style: {
3178
+ color: "var(--compass-color-success)",
3179
+ fontSize: "var(--compass-font-size-sm)"
3180
+ },
3181
+ children: [
3182
+ market.apy.toFixed(2),
3183
+ "% APY"
3184
+ ]
3185
+ }
3186
+ )
3187
+ ]
3188
+ }
3189
+ ),
3190
+ (showTvl && market.tvl > 0 || market.expiry) && /* @__PURE__ */ jsxRuntime.jsxs(
3191
+ "div",
3192
+ {
3193
+ style: {
3194
+ fontSize: "var(--compass-font-size-xs)",
3195
+ color: "var(--compass-color-text-tertiary)",
3196
+ marginTop: "calc(var(--compass-spacing-unit) * 0.5)"
3197
+ },
3198
+ children: [
3199
+ showTvl && market.tvl > 0 && `TVL: ${formatTvl(market.tvl)}`,
3200
+ showTvl && market.tvl > 0 && market.expiry && " \xB7 ",
3201
+ market.expiry && `Expires: ${market.expiry}`
3202
+ ]
3203
+ }
3204
+ )
3092
3205
  ]
3093
- }
3094
- )
3095
- ] })
3096
- ]
3097
- }
3098
- )
3206
+ },
3207
+ market.id
3208
+ ))
3209
+ }
3210
+ )
3211
+ ] })
3212
+ ] })
3099
3213
  ]
3100
3214
  }
3101
3215
  );
3102
3216
  }
3103
- function useVaultsData(options = {}) {
3104
- const { address } = useEmbeddableWallet();
3105
- const { chainId } = useChain();
3106
- const { sortBy = "apy_7d", assetFilter, minApy, minTvl } = options;
3107
- const vaultsQuery = reactQuery.useQuery({
3108
- queryKey: ["vaults", chainId, sortBy, assetFilter, minApy, minTvl],
3109
- queryFn: async () => {
3110
- const assetSymbol = assetFilter && assetFilter.length === 1 ? assetFilter[0] : void 0;
3111
- const params = new URLSearchParams({
3112
- chain: chainId,
3113
- orderBy: sortBy === "tvl" ? "tvl_usd" : sortBy,
3114
- direction: "desc",
3115
- limit: "100",
3116
- ...assetSymbol && { assetSymbol }
3117
- });
3118
- const response = await fetch(`/api/compass/vaults?${params}`);
3119
- if (!response.ok) {
3120
- throw new Error("Failed to fetch vaults");
3121
- }
3122
- const data = await response.json();
3123
- let vaults = (data.vaults || []).map((v) => ({
3124
- vaultAddress: v.vaultAddress,
3125
- name: v.name || "Unknown Vault",
3126
- assetSymbol: v.assetSymbol || "UNKNOWN",
3127
- apy7d: v.apy7d ?? null,
3128
- apy30d: v.apy30d ?? null,
3129
- apy90d: v.apy90d ?? null,
3130
- tvlUsd: v.tvlUsd ?? null
3131
- }));
3132
- if (assetFilter && assetFilter.length > 1) {
3133
- vaults = vaults.filter(
3134
- (v) => assetFilter.includes(v.assetSymbol.toUpperCase())
3135
- );
3136
- }
3137
- if (minApy !== void 0 && minApy > 0) {
3138
- vaults = vaults.filter((v) => {
3139
- const apy = parseFloat(v.apy7d || "0");
3140
- return apy >= minApy;
3141
- });
3142
- }
3143
- if (minTvl !== void 0 && minTvl > 0) {
3144
- vaults = vaults.filter((v) => {
3145
- const tvl = parseFloat(v.tvlUsd || "0");
3146
- return tvl >= minTvl;
3147
- });
3148
- }
3149
- return vaults;
3150
- },
3151
- staleTime: 30 * 1e3
3152
- });
3153
- const positionsQuery = reactQuery.useQuery({
3154
- queryKey: ["vaultPositions", chainId, address],
3155
- queryFn: async () => {
3156
- if (!address) return [];
3157
- const params = new URLSearchParams({
3158
- chain: chainId,
3159
- owner: address
3160
- });
3161
- const response = await fetch(`/api/compass/positions?${params}`);
3162
- if (!response.ok) {
3163
- throw new Error("Failed to fetch positions");
3164
- }
3165
- const data = await response.json();
3166
- return (data.vaults || []).map((p) => ({
3167
- vaultAddress: p.vaultAddress,
3168
- balance: p.balance || "0",
3169
- pnl: p.pnl ? {
3170
- unrealizedPnl: p.pnl.unrealizedPnl || "0",
3171
- realizedPnl: p.pnl.realizedPnl || "0",
3172
- totalPnl: p.pnl.totalPnl || "0",
3173
- totalDeposited: p.pnl.totalDeposited || "0"
3174
- } : void 0,
3175
- deposits: (p.deposits || []).map((d) => ({
3176
- amount: d.inputAmount || d.amount || "0",
3177
- blockNumber: d.blockNumber || 0,
3178
- txHash: d.transactionHash || d.txHash || ""
3179
- })),
3180
- withdrawals: (p.withdrawals || []).map((w) => ({
3181
- amount: w.outputAmount || w.amount || "0",
3182
- blockNumber: w.blockNumber || 0,
3183
- txHash: w.transactionHash || w.txHash || ""
3184
- }))
3185
- }));
3186
- },
3187
- enabled: !!address,
3188
- staleTime: 30 * 1e3
3189
- });
3190
- const vaultsWithPositions = (vaultsQuery.data || []).map((vault) => {
3191
- const position = positionsQuery.data?.find(
3192
- (p) => p.vaultAddress.toLowerCase() === vault.vaultAddress.toLowerCase()
3193
- );
3194
- return { ...vault, userPosition: position };
3195
- });
3196
- return {
3197
- vaults: vaultsWithPositions,
3198
- isLoading: vaultsQuery.isLoading,
3199
- isError: vaultsQuery.isError,
3200
- error: vaultsQuery.error,
3201
- refetch: () => {
3202
- vaultsQuery.refetch();
3203
- positionsQuery.refetch();
3204
- }
3205
- };
3206
- }
3207
- function VaultsList({
3208
- showApy = true,
3209
- apyPeriods = ["7d", "30d", "90d"],
3210
- showTvl = true,
3211
- showUserPosition = true,
3212
- showPnL = true,
3213
- showHistory = true,
3214
- showSearch = true,
3215
- showSort = true,
3216
- defaultSort = "apy_7d",
3217
- assetFilter,
3218
- minApy,
3219
- minTvl: initialMinTvl,
3220
- showTvlFilter = true,
3221
- onVaultSelect,
3222
- onDeposit,
3223
- onWithdraw
3224
- }) {
3225
- const [searchQuery, setSearchQuery] = react.useState("");
3226
- const [sortBy, setSortBy] = react.useState(defaultSort);
3227
- const [selectedVault, setSelectedVault] = react.useState(null);
3228
- const [minTvlFilter, setMinTvlFilter] = react.useState(initialMinTvl);
3229
- const [showFilterPanel, setShowFilterPanel] = react.useState(false);
3230
- const { vaults, isLoading, isError, refetch } = useVaultsData({
3231
- sortBy,
3232
- assetFilter,
3233
- minApy,
3234
- minTvl: minTvlFilter
3235
- });
3236
- const handleMinTvlChange = react.useCallback((value) => {
3237
- const num = parseFloat(value);
3238
- setMinTvlFilter(isNaN(num) || num <= 0 ? void 0 : num);
3239
- }, []);
3240
- const filteredVaults = react.useMemo(() => {
3241
- if (!searchQuery) return vaults;
3242
- const query = searchQuery.toLowerCase();
3243
- return vaults.filter(
3244
- (v) => v.name.toLowerCase().includes(query) || v.assetSymbol.toLowerCase().includes(query)
3245
- );
3246
- }, [vaults, searchQuery]);
3247
- const handleVaultClick = (vault) => {
3248
- setSelectedVault(vault);
3249
- onVaultSelect?.(vault);
3250
- };
3251
- const handleActionSuccess = (action, amount, txHash) => {
3252
- if (action === "deposit") {
3253
- onDeposit?.(selectedVault, amount, txHash);
3254
- } else {
3255
- onWithdraw?.(selectedVault, amount, txHash);
3256
- }
3257
- refetch();
3217
+ function PositionCard({ position }) {
3218
+ const [isHistoryExpanded, setIsHistoryExpanded] = react.useState(false);
3219
+ const formatShortDate = (dateStr) => {
3220
+ if (!dateStr) return null;
3221
+ const date = new Date(dateStr);
3222
+ if (isNaN(date.getTime())) return null;
3223
+ return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
3258
3224
  };
3259
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "var(--compass-spacing-card)" }, children: [
3260
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between flex-wrap", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
3261
- /* @__PURE__ */ jsxRuntime.jsx(ChainSwitcher, {}),
3262
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
3263
- /* @__PURE__ */ jsxRuntime.jsx(EarnAccountBalance, { compact: true }),
3264
- /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
3265
- ] })
3266
- ] }),
3267
- (showSearch || showSort || showTvlFilter) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
3268
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
3269
- showSearch && /* @__PURE__ */ jsxRuntime.jsxs(
3225
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3226
+ "div",
3227
+ {
3228
+ style: {
3229
+ backgroundColor: "var(--compass-color-surface, #12121a)",
3230
+ border: "1px solid var(--compass-color-border, rgba(255, 255, 255, 0.1))",
3231
+ borderRadius: "var(--compass-border-radius-lg, 12px)",
3232
+ padding: "var(--compass-spacing-card, 16px)"
3233
+ },
3234
+ children: [
3235
+ /* @__PURE__ */ jsxRuntime.jsxs(
3270
3236
  "div",
3271
3237
  {
3272
- className: "flex-1 flex items-center border",
3273
3238
  style: {
3274
- backgroundColor: "var(--compass-color-background)",
3275
- borderColor: "var(--compass-color-border)",
3276
- borderRadius: "var(--compass-border-radius-lg)",
3277
- fontFamily: "var(--compass-font-family)",
3278
- padding: "var(--compass-spacing-input)",
3279
- gap: "calc(var(--compass-spacing-unit) * 0.5)"
3239
+ display: "flex",
3240
+ justifyContent: "space-between",
3241
+ alignItems: "flex-start",
3242
+ marginBottom: "calc(var(--compass-spacing-unit) * 2)"
3280
3243
  },
3281
3244
  children: [
3282
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { size: 16, style: { color: "var(--compass-color-text-tertiary)" } }),
3283
3245
  /* @__PURE__ */ jsxRuntime.jsx(
3284
- "input",
3246
+ "span",
3247
+ {
3248
+ style: {
3249
+ fontWeight: 600,
3250
+ color: "var(--compass-color-text)",
3251
+ fontSize: "var(--compass-font-size-base)"
3252
+ },
3253
+ children: position.marketName
3254
+ }
3255
+ ),
3256
+ /* @__PURE__ */ jsxRuntime.jsxs(
3257
+ "span",
3285
3258
  {
3286
- type: "text",
3287
- placeholder: "Search vaults...",
3288
- value: searchQuery,
3289
- onChange: (e) => setSearchQuery(e.target.value),
3290
- className: "flex-1 bg-transparent outline-none text-sm",
3291
- style: { color: "var(--compass-color-text)" }
3259
+ style: {
3260
+ color: "var(--compass-color-success)",
3261
+ fontSize: "var(--compass-font-size-sm)",
3262
+ backgroundColor: "var(--compass-color-success-muted)",
3263
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 1.5)",
3264
+ borderRadius: "var(--compass-border-radius-full)"
3265
+ },
3266
+ children: [
3267
+ "APY: ",
3268
+ position.apy.toFixed(2),
3269
+ "%"
3270
+ ]
3292
3271
  }
3293
3272
  )
3294
3273
  ]
3295
3274
  }
3296
3275
  ),
3297
- showSort && /* @__PURE__ */ jsxRuntime.jsxs(
3298
- "select",
3299
- {
3300
- value: sortBy,
3301
- onChange: (e) => setSortBy(e.target.value),
3302
- className: "border text-sm cursor-pointer",
3303
- style: {
3304
- backgroundColor: "var(--compass-color-background)",
3305
- borderColor: "var(--compass-color-border)",
3306
- color: "var(--compass-color-text)",
3307
- borderRadius: "var(--compass-border-radius-lg)",
3308
- fontFamily: "var(--compass-font-family)",
3309
- padding: "var(--compass-spacing-input)"
3310
- },
3311
- children: [
3312
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "apy_7d", children: "APY (7D)" }),
3313
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "apy_30d", children: "APY (30D)" }),
3314
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "apy_90d", children: "APY (90D)" }),
3315
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "tvl", children: "TVL" })
3316
- ]
3317
- }
3318
- ),
3319
- showTvlFilter && /* @__PURE__ */ jsxRuntime.jsxs(
3320
- "button",
3276
+ /* @__PURE__ */ jsxRuntime.jsxs(
3277
+ "div",
3321
3278
  {
3322
- onClick: () => setShowFilterPanel(!showFilterPanel),
3323
- className: "border text-sm flex items-center",
3324
3279
  style: {
3325
- backgroundColor: showFilterPanel || minTvlFilter ? "var(--compass-color-primary-muted)" : "var(--compass-color-background)",
3326
- borderColor: showFilterPanel || minTvlFilter ? "var(--compass-color-primary)" : "var(--compass-color-border)",
3327
- color: showFilterPanel || minTvlFilter ? "var(--compass-color-primary)" : "var(--compass-color-text)",
3328
- borderRadius: "var(--compass-border-radius-lg)",
3329
- fontFamily: "var(--compass-font-family)",
3330
- padding: "var(--compass-spacing-input)",
3331
- gap: "calc(var(--compass-spacing-unit) * 0.5)",
3332
- transition: "var(--compass-transition-normal)"
3280
+ display: "grid",
3281
+ gridTemplateColumns: "1fr 1fr",
3282
+ gap: "calc(var(--compass-spacing-unit) * 2)",
3283
+ marginBottom: "calc(var(--compass-spacing-unit) * 2)"
3333
3284
  },
3334
3285
  children: [
3335
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SlidersHorizontal, { size: 14 }),
3336
- "Filter"
3337
- ]
3338
- }
3339
- )
3340
- ] }),
3341
- showTvlFilter && showFilterPanel && /* @__PURE__ */ jsxRuntime.jsxs(
3342
- "div",
3343
- {
3344
- className: "flex items-center border",
3345
- style: {
3346
- backgroundColor: "var(--compass-color-surface)",
3347
- borderColor: "var(--compass-color-border)",
3348
- borderRadius: "var(--compass-border-radius-lg)",
3349
- fontFamily: "var(--compass-font-family)",
3350
- padding: "calc(var(--compass-spacing-unit) * 0.75)",
3351
- gap: "calc(var(--compass-spacing-unit) * 0.75)"
3352
- },
3353
- children: [
3354
- /* @__PURE__ */ jsxRuntime.jsx(
3355
- "label",
3356
- {
3357
- className: "text-sm font-medium whitespace-nowrap",
3358
- style: { color: "var(--compass-color-text-secondary)" },
3359
- children: "Min TVL:"
3360
- }
3361
- ),
3362
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
3363
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-tertiary)" }, children: "$" }),
3364
- /* @__PURE__ */ jsxRuntime.jsx(
3365
- "input",
3366
- {
3367
- type: "number",
3368
- placeholder: "0",
3369
- value: minTvlFilter || "",
3370
- onChange: (e) => handleMinTvlChange(e.target.value),
3371
- className: "w-24 border text-sm bg-transparent",
3372
- style: {
3373
- borderColor: "var(--compass-color-border)",
3374
- color: "var(--compass-color-text)",
3375
- borderRadius: "var(--compass-border-radius-sm)",
3376
- fontFamily: "var(--compass-font-family)",
3377
- padding: "calc(var(--compass-spacing-unit) * 0.25) calc(var(--compass-spacing-unit) * 0.5)"
3378
- }
3379
- }
3380
- )
3381
- ] }),
3382
- minTvlFilter && /* @__PURE__ */ jsxRuntime.jsx(
3383
- "button",
3384
- {
3385
- onClick: () => setMinTvlFilter(void 0),
3386
- className: "text-xs",
3387
- style: {
3388
- backgroundColor: "var(--compass-color-error-muted)",
3389
- color: "var(--compass-color-error)",
3390
- borderRadius: "var(--compass-border-radius-sm)",
3391
- padding: "calc(var(--compass-spacing-unit) * 0.25) calc(var(--compass-spacing-unit) * 0.5)",
3392
- transition: "var(--compass-transition-fast)"
3393
- },
3394
- children: "Clear"
3395
- }
3396
- )
3397
- ]
3398
- }
3399
- )
3400
- ] }),
3401
- isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center", style: { padding: "calc(var(--compass-spacing-unit) * 3) 0" }, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 24, className: "animate-spin", style: { color: "var(--compass-color-primary)" } }) }) : isError ? /* @__PURE__ */ jsxRuntime.jsx(
3402
- "div",
3403
- {
3404
- className: "text-center",
3405
- style: {
3406
- backgroundColor: "var(--compass-color-error-muted)",
3407
- color: "var(--compass-color-error)",
3408
- borderRadius: "var(--compass-border-radius-lg)",
3409
- padding: "var(--compass-spacing-card)"
3410
- },
3411
- children: "Failed to load vaults. Please try again."
3412
- }
3413
- ) : filteredVaults.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
3414
- "div",
3415
- {
3416
- className: "text-center",
3417
- style: { color: "var(--compass-color-text-secondary)", padding: "calc(var(--compass-spacing-unit) * 2)" },
3418
- children: "No vaults found"
3419
- }
3420
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.75)" }, children: filteredVaults.map((vault) => /* @__PURE__ */ jsxRuntime.jsx(
3421
- VaultCard,
3422
- {
3423
- vault,
3424
- showApy,
3425
- sortBy,
3426
- showTvl,
3427
- showUserPosition,
3428
- onClick: () => handleVaultClick(vault)
3429
- },
3430
- vault.vaultAddress
3431
- )) }),
3432
- selectedVault && /* @__PURE__ */ jsxRuntime.jsx(
3433
- ActionModal,
3434
- {
3435
- isOpen: !!selectedVault,
3436
- onClose: () => setSelectedVault(null),
3437
- title: selectedVault.name,
3438
- children: /* @__PURE__ */ jsxRuntime.jsx(EarnAccountGuard, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "var(--compass-spacing-card)" }, children: [
3439
- showPnL && selectedVault.userPosition?.pnl && /* @__PURE__ */ jsxRuntime.jsx(
3440
- PnLSummary,
3441
- {
3442
- pnl: selectedVault.userPosition.pnl,
3443
- tokenSymbol: selectedVault.assetSymbol,
3444
- tokenPrice: 1
3445
- }
3446
- ),
3447
- showHistory && (selectedVault.userPosition?.deposits?.length || selectedVault.userPosition?.withdrawals?.length) && /* @__PURE__ */ jsxRuntime.jsx(
3448
- TransactionHistory,
3449
- {
3450
- deposits: selectedVault.userPosition?.deposits,
3451
- withdrawals: selectedVault.userPosition?.withdrawals,
3452
- tokenSymbol: selectedVault.assetSymbol
3453
- }
3454
- ),
3455
- /* @__PURE__ */ jsxRuntime.jsx(
3456
- DepositWithdrawForm,
3457
- {
3458
- venueType: "VAULT",
3459
- venueAddress: selectedVault.vaultAddress,
3460
- venueToken: selectedVault.assetSymbol,
3461
- positionBalance: selectedVault.userPosition?.balance,
3462
- onSuccess: handleActionSuccess
3463
- }
3464
- )
3465
- ] }) })
3466
- }
3467
- )
3468
- ] });
3469
- }
3470
- function useAaveData(options = {}) {
3471
- const { address } = useEmbeddableWallet();
3472
- const { chainId } = useChain();
3473
- const { assetFilter } = options;
3474
- const marketsQuery = reactQuery.useQuery({
3475
- queryKey: ["aaveMarkets", chainId, assetFilter],
3476
- queryFn: async () => {
3477
- const params = new URLSearchParams({ chain: chainId });
3478
- const response = await fetch(`/api/compass/aave/markets?${params}`);
3479
- if (!response.ok) {
3480
- throw new Error("Failed to fetch Aave markets");
3481
- }
3482
- const data = await response.json();
3483
- const marketsDict = data.markets || {};
3484
- let markets = [];
3485
- for (const [symbol, tokenData] of Object.entries(marketsDict)) {
3486
- if (assetFilter && assetFilter.length > 0) {
3487
- if (!assetFilter.includes(symbol.toUpperCase())) {
3488
- continue;
3489
- }
3490
- }
3491
- const token = tokenData;
3492
- const chainData = token.chains?.[chainId];
3493
- if (chainData) {
3494
- markets.push({
3495
- marketAddress: chainData.address || "",
3496
- reserveSymbol: symbol,
3497
- underlyingSymbol: symbol,
3498
- supplyApy: chainData.supplyApy?.toString() ?? null,
3499
- borrowApy: chainData.borrowApy?.toString() ?? null
3500
- });
3501
- }
3502
- }
3503
- markets.sort((a, b) => {
3504
- return parseFloat(b.supplyApy || "0") - parseFloat(a.supplyApy || "0");
3505
- });
3506
- return markets;
3507
- },
3508
- staleTime: 30 * 1e3
3509
- });
3510
- const positionsQuery = reactQuery.useQuery({
3511
- queryKey: ["aavePositions", chainId, address],
3512
- queryFn: async () => {
3513
- if (!address) return [];
3514
- const params = new URLSearchParams({
3515
- chain: chainId,
3516
- owner: address
3517
- });
3518
- const response = await fetch(`/api/compass/positions?${params}`);
3519
- if (!response.ok) {
3520
- throw new Error("Failed to fetch positions");
3521
- }
3522
- const data = await response.json();
3523
- return (data.aave || []).map((p) => ({
3524
- marketAddress: p.marketAddress || "",
3525
- reserveSymbol: p.reserveSymbol || "",
3526
- balance: p.balance || "0",
3527
- pnl: p.pnl ? {
3528
- unrealizedPnl: p.pnl.unrealizedPnl || "0",
3529
- realizedPnl: p.pnl.realizedPnl || "0",
3530
- totalPnl: p.pnl.totalPnl || "0",
3531
- totalDeposited: p.pnl.totalDeposited || "0"
3532
- } : void 0,
3533
- deposits: (p.deposits || []).map((d) => ({
3534
- amount: d.inputAmount || d.amount || "0",
3535
- blockNumber: d.blockNumber || 0,
3536
- txHash: d.transactionHash || d.txHash || ""
3537
- })),
3538
- withdrawals: (p.withdrawals || []).map((w) => ({
3539
- amount: w.outputAmount || w.amount || "0",
3540
- blockNumber: w.blockNumber || 0,
3541
- txHash: w.transactionHash || w.txHash || ""
3542
- }))
3543
- }));
3544
- },
3545
- enabled: !!address,
3546
- staleTime: 30 * 1e3
3547
- });
3548
- const marketsWithPositions = (marketsQuery.data || []).map((market) => {
3549
- const position = positionsQuery.data?.find(
3550
- (p) => p.reserveSymbol.toLowerCase() === market.reserveSymbol.toLowerCase()
3551
- );
3552
- return { ...market, userPosition: position };
3553
- });
3554
- return {
3555
- markets: marketsWithPositions,
3556
- isLoading: marketsQuery.isLoading,
3557
- isError: marketsQuery.isError,
3558
- error: marketsQuery.error,
3559
- refetch: () => {
3560
- marketsQuery.refetch();
3561
- positionsQuery.refetch();
3562
- }
3563
- };
3564
- }
3565
- function formatAPY2(apy) {
3566
- if (!apy) return "0.00%";
3567
- const num = parseFloat(apy);
3568
- return `${num.toFixed(2)}%`;
3569
- }
3570
- function AaveMarketsList({
3571
- showApy = true,
3572
- showUserPosition = true,
3573
- showPnL = true,
3574
- showHistory = true,
3575
- showSearch = true,
3576
- assetFilter,
3577
- onMarketSelect,
3578
- onSupply,
3579
- onWithdraw
3580
- }) {
3581
- const [searchQuery, setSearchQuery] = react.useState("");
3582
- const [selectedMarket, setSelectedMarket] = react.useState(null);
3583
- const { markets, isLoading, isError, refetch } = useAaveData({ assetFilter });
3584
- const filteredMarkets = react.useMemo(() => {
3585
- if (!searchQuery) return markets;
3586
- const query = searchQuery.toLowerCase();
3587
- return markets.filter(
3588
- (m) => m.reserveSymbol.toLowerCase().includes(query) || m.underlyingSymbol.toLowerCase().includes(query)
3589
- );
3590
- }, [markets, searchQuery]);
3591
- const handleMarketClick = (market) => {
3592
- setSelectedMarket(market);
3593
- onMarketSelect?.(market);
3594
- };
3595
- const handleActionSuccess = (action, amount, txHash) => {
3596
- if (action === "deposit") {
3597
- onSupply?.(selectedMarket, amount, txHash);
3598
- } else {
3599
- onWithdraw?.(selectedMarket, amount, txHash);
3600
- }
3601
- refetch();
3602
- };
3603
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "var(--compass-spacing-card)" }, children: [
3604
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between flex-wrap", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
3605
- /* @__PURE__ */ jsxRuntime.jsx(ChainSwitcher, {}),
3606
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
3607
- /* @__PURE__ */ jsxRuntime.jsx(EarnAccountBalance, { compact: true }),
3608
- /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
3609
- ] })
3610
- ] }),
3611
- showSearch && /* @__PURE__ */ jsxRuntime.jsxs(
3612
- "div",
3613
- {
3614
- className: "flex items-center border",
3615
- style: {
3616
- backgroundColor: "var(--compass-color-background)",
3617
- borderColor: "var(--compass-color-border)",
3618
- borderRadius: "var(--compass-border-radius-lg)",
3619
- fontFamily: "var(--compass-font-family)",
3620
- padding: "var(--compass-spacing-input)",
3621
- gap: "calc(var(--compass-spacing-unit) * 0.5)"
3622
- },
3623
- children: [
3624
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { size: 16, style: { color: "var(--compass-color-text-tertiary)" } }),
3625
- /* @__PURE__ */ jsxRuntime.jsx(
3626
- "input",
3627
- {
3628
- type: "text",
3629
- placeholder: "Search markets...",
3630
- value: searchQuery,
3631
- onChange: (e) => setSearchQuery(e.target.value),
3632
- className: "flex-1 bg-transparent outline-none text-sm",
3633
- style: { color: "var(--compass-color-text)" }
3634
- }
3635
- )
3636
- ]
3637
- }
3638
- ),
3639
- isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center", style: { padding: "calc(var(--compass-spacing-unit) * 3) 0" }, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 24, className: "animate-spin", style: { color: "var(--compass-color-primary)" } }) }) : isError ? /* @__PURE__ */ jsxRuntime.jsx(
3640
- "div",
3641
- {
3642
- className: "text-center",
3643
- style: {
3644
- backgroundColor: "var(--compass-color-error-muted)",
3645
- color: "var(--compass-color-error)",
3646
- borderRadius: "var(--compass-border-radius-lg)",
3647
- padding: "var(--compass-spacing-card)"
3648
- },
3649
- children: "Failed to load Aave markets. Please try again."
3650
- }
3651
- ) : filteredMarkets.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
3652
- "div",
3653
- {
3654
- className: "text-center",
3655
- style: { color: "var(--compass-color-text-secondary)", padding: "calc(var(--compass-spacing-unit) * 2)" },
3656
- children: "No markets found"
3657
- }
3658
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.75)" }, children: filteredMarkets.map((market) => {
3659
- const hasPosition = market.userPosition && parseFloat(market.userPosition.balance) > 0;
3660
- return /* @__PURE__ */ jsxRuntime.jsxs(
3661
- "button",
3662
- {
3663
- onClick: () => handleMarketClick(market),
3664
- className: "w-full border text-left hover:scale-[1.01]",
3665
- style: {
3666
- backgroundColor: "var(--compass-color-surface)",
3667
- borderColor: hasPosition ? "var(--compass-color-primary)" : "var(--compass-color-border)",
3668
- borderRadius: "var(--compass-border-radius-xl)",
3669
- fontFamily: "var(--compass-font-family)",
3670
- padding: "var(--compass-spacing-card)",
3671
- transition: "var(--compass-transition-normal)"
3672
- },
3673
- children: [
3674
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
3675
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.25)" }, children: [
3286
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3676
3287
  /* @__PURE__ */ jsxRuntime.jsx(
3677
- "h3",
3288
+ "div",
3678
3289
  {
3679
- className: "font-semibold",
3680
3290
  style: {
3681
- fontSize: "var(--compass-font-size-body)",
3682
- color: "var(--compass-color-text)"
3291
+ fontSize: "var(--compass-font-size-xs)",
3292
+ color: "var(--compass-color-text-tertiary)",
3293
+ marginBottom: "calc(var(--compass-spacing-unit) * 0.5)"
3683
3294
  },
3684
- children: market.underlyingSymbol
3295
+ children: "Amount"
3685
3296
  }
3686
3297
  ),
3687
- /* @__PURE__ */ jsxRuntime.jsx(
3688
- "span",
3689
- {
3690
- className: "text-sm",
3691
- style: { color: "var(--compass-color-text-secondary)" },
3692
- children: "Aave V3"
3693
- }
3694
- )
3298
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "var(--compass-color-text)", fontWeight: 500 }, children: [
3299
+ position.amount.toLocaleString(),
3300
+ " ",
3301
+ position.token
3302
+ ] })
3695
3303
  ] }),
3696
- showApy && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.25)" }, children: [
3697
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { size: 14, style: { color: "var(--compass-color-success)" } }),
3698
- /* @__PURE__ */ jsxRuntime.jsx(
3699
- "span",
3700
- {
3701
- className: "font-mono font-semibold",
3702
- style: { color: "var(--compass-color-success)" },
3703
- children: formatAPY2(market.supplyApy)
3704
- }
3705
- )
3706
- ] })
3707
- ] }),
3708
- showUserPosition && hasPosition && /* @__PURE__ */ jsxRuntime.jsx(
3709
- "div",
3710
- {
3711
- className: "flex items-center justify-end border-t",
3712
- style: {
3713
- borderColor: "var(--compass-color-border)",
3714
- marginTop: "calc(var(--compass-spacing-unit) * 0.75)",
3715
- paddingTop: "calc(var(--compass-spacing-unit) * 0.75)"
3716
- },
3717
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-end", children: [
3304
+ (() => {
3305
+ const deposits = position.transactions.filter((t) => t.type === "deposit" && t.timestamp);
3306
+ const earliest = deposits.length > 0 ? deposits.reduce((min, t) => !min || t.timestamp < min ? t.timestamp : min, "") : void 0;
3307
+ const formatted = formatShortDate(earliest);
3308
+ if (!formatted) return null;
3309
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3718
3310
  /* @__PURE__ */ jsxRuntime.jsx(
3719
- "span",
3311
+ "div",
3720
3312
  {
3721
- className: "text-xs",
3722
- style: { color: "var(--compass-color-text-tertiary)" },
3723
- children: "Your Position"
3313
+ style: {
3314
+ fontSize: "var(--compass-font-size-xs)",
3315
+ color: "var(--compass-color-text-tertiary)",
3316
+ marginBottom: "calc(var(--compass-spacing-unit) * 0.5)"
3317
+ },
3318
+ children: "Entered"
3724
3319
  }
3725
3320
  ),
3726
- /* @__PURE__ */ jsxRuntime.jsxs(
3727
- "span",
3728
- {
3729
- className: "font-mono text-sm font-medium",
3730
- style: { color: "var(--compass-color-primary)" },
3731
- children: [
3732
- parseFloat(market.userPosition.balance).toFixed(4),
3733
- " ",
3734
- market.underlyingSymbol
3735
- ]
3736
- }
3737
- )
3738
- ] })
3739
- }
3740
- )
3741
- ]
3742
- },
3743
- market.marketAddress
3744
- );
3745
- }) }),
3746
- selectedMarket && /* @__PURE__ */ jsxRuntime.jsx(
3747
- ActionModal,
3748
- {
3749
- isOpen: !!selectedMarket,
3750
- onClose: () => setSelectedMarket(null),
3751
- title: `${selectedMarket.underlyingSymbol} - Aave`,
3752
- children: /* @__PURE__ */ jsxRuntime.jsx(EarnAccountGuard, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "var(--compass-spacing-card)" }, children: [
3753
- showPnL && selectedMarket.userPosition?.pnl && /* @__PURE__ */ jsxRuntime.jsx(
3754
- PnLSummary,
3755
- {
3756
- pnl: selectedMarket.userPosition.pnl,
3757
- tokenSymbol: selectedMarket.underlyingSymbol,
3758
- tokenPrice: 1
3759
- }
3760
- ),
3761
- showHistory && (selectedMarket.userPosition?.deposits?.length || selectedMarket.userPosition?.withdrawals?.length) && /* @__PURE__ */ jsxRuntime.jsx(
3762
- TransactionHistory,
3763
- {
3764
- deposits: selectedMarket.userPosition?.deposits,
3765
- withdrawals: selectedMarket.userPosition?.withdrawals,
3766
- tokenSymbol: selectedMarket.underlyingSymbol
3767
- }
3768
- ),
3769
- /* @__PURE__ */ jsxRuntime.jsx(
3770
- DepositWithdrawForm,
3771
- {
3772
- venueType: "AAVE",
3773
- venueAddress: selectedMarket.marketAddress,
3774
- venueToken: selectedMarket.underlyingSymbol,
3775
- positionBalance: selectedMarket.userPosition?.balance,
3776
- onSuccess: handleActionSuccess
3777
- }
3778
- )
3779
- ] }) })
3780
- }
3781
- )
3782
- ] });
3783
- }
3784
- var MARKET_TABS = [
3785
- { value: "variable", label: "VARIABLE" },
3786
- { value: "fixed", label: "FIXED" }
3787
- ];
3788
- var TAB_TO_MARKET_TYPES = {
3789
- variable: ["aave", "vaults"],
3790
- fixed: ["pendle"]
3791
- };
3792
- function MarketSelector({
3793
- markets,
3794
- selectedMarket,
3795
- onMarketSelect,
3796
- marketTab,
3797
- onMarketTabChange,
3798
- isLoading,
3799
- allowedMarketIds
3800
- }) {
3801
- const [isDropdownOpen, setIsDropdownOpen] = react.useState(false);
3802
- const dropdownRef = react.useRef(null);
3803
- react.useEffect(() => {
3804
- function handleClickOutside(e) {
3805
- if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
3806
- setIsDropdownOpen(false);
3807
- }
3808
- }
3809
- document.addEventListener("mousedown", handleClickOutside);
3810
- return () => document.removeEventListener("mousedown", handleClickOutside);
3811
- }, []);
3812
- const allowedTypes = TAB_TO_MARKET_TYPES[marketTab];
3813
- const showTvl = marketTab === "variable";
3814
- const filteredMarkets = react.useMemo(() => {
3815
- return markets.filter((m) => {
3816
- if (!allowedTypes.includes(m.type)) return false;
3817
- if (allowedMarketIds !== void 0) {
3818
- return allowedMarketIds.includes(m.id);
3819
- }
3820
- return true;
3821
- }).sort((a, b) => b.apy - a.apy);
3822
- }, [markets, allowedTypes, allowedMarketIds]);
3823
- const hasMarkets = filteredMarkets.length > 0;
3824
- const formatTvl = (value) => {
3825
- if (value >= 1e9) return `$${(value / 1e9).toFixed(1)}B`;
3826
- if (value >= 1e6) return `$${(value / 1e6).toFixed(1)}M`;
3827
- if (value >= 1e3) return `$${(value / 1e3).toFixed(0)}K`;
3828
- return `$${value}`;
3829
- };
3830
- return /* @__PURE__ */ jsxRuntime.jsxs(
3831
- "div",
3832
- {
3833
- style: {
3834
- display: "flex",
3835
- flexDirection: "column",
3836
- gap: "calc(var(--compass-spacing-unit) * 1.5)"
3837
- },
3838
- children: [
3839
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3840
- /* @__PURE__ */ jsxRuntime.jsx(
3841
- "label",
3842
- {
3843
- style: {
3844
- display: "block",
3845
- fontSize: "var(--compass-font-size-sm)",
3846
- color: "var(--compass-color-text-secondary)",
3847
- marginBottom: "var(--compass-spacing-unit)"
3848
- },
3849
- children: "Market Type"
3850
- }
3851
- ),
3852
- /* @__PURE__ */ jsxRuntime.jsx(
3853
- "div",
3854
- {
3855
- style: {
3856
- display: "flex",
3857
- borderRadius: "var(--compass-border-radius-md, 8px)",
3858
- border: "1px solid var(--compass-color-border, rgba(255, 255, 255, 0.1))",
3859
- overflow: "hidden"
3860
- },
3861
- children: MARKET_TABS.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
3862
- "button",
3321
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--compass-color-text)" }, children: formatted })
3322
+ ] });
3323
+ })()
3324
+ ]
3325
+ }
3326
+ ),
3327
+ /* @__PURE__ */ jsxRuntime.jsxs(
3328
+ "div",
3329
+ {
3330
+ style: {
3331
+ paddingTop: "calc(var(--compass-spacing-unit) * 2)",
3332
+ borderTop: "1px solid var(--compass-color-border)",
3333
+ display: "flex",
3334
+ flexDirection: "column",
3335
+ gap: "calc(var(--compass-spacing-unit) * 1.5)"
3336
+ },
3337
+ children: [
3338
+ position.pnl ? /* @__PURE__ */ jsxRuntime.jsxs(
3339
+ "div",
3863
3340
  {
3864
- onClick: () => onMarketTabChange(tab.value),
3865
3341
  style: {
3866
- flex: 1,
3867
- padding: "6px 8px",
3868
- backgroundColor: marketTab === tab.value ? "var(--compass-color-primary, #6366f1)" : "var(--compass-color-surface, #12121a)",
3869
- color: marketTab === tab.value ? "var(--compass-color-primary-text, white)" : "var(--compass-color-text, #e4e4e7)",
3870
- border: "none",
3871
- cursor: "pointer",
3872
- fontSize: "12px",
3873
- fontWeight: 500,
3874
- fontFamily: "var(--compass-font-family)",
3875
- transition: "var(--compass-transition-normal, all 0.2s ease)"
3342
+ display: "grid",
3343
+ gridTemplateColumns: "1fr 1fr",
3344
+ gap: "calc(var(--compass-spacing-unit) * 2)"
3876
3345
  },
3877
- children: tab.label
3878
- },
3879
- tab.value
3880
- ))
3881
- }
3882
- )
3883
- ] }),
3884
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3885
- /* @__PURE__ */ jsxRuntime.jsx(
3886
- "label",
3887
- {
3888
- style: {
3889
- display: "block",
3890
- fontSize: "var(--compass-font-size-sm)",
3891
- color: "var(--compass-color-text-secondary)",
3892
- marginBottom: "var(--compass-spacing-unit)"
3893
- },
3894
- children: "Select Market"
3895
- }
3896
- ),
3897
- /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: dropdownRef, style: { position: "relative" }, children: [
3898
- /* @__PURE__ */ jsxRuntime.jsxs(
3899
- "button",
3900
- {
3901
- onClick: () => hasMarkets && setIsDropdownOpen(!isDropdownOpen),
3902
- disabled: !hasMarkets || isLoading,
3903
- style: {
3904
- width: "100%",
3905
- padding: "8px 12px",
3906
- backgroundColor: "var(--compass-color-surface, #12121a)",
3907
- border: "1px solid var(--compass-color-border, rgba(255, 255, 255, 0.1))",
3908
- borderRadius: "var(--compass-border-radius-md, 8px)",
3909
- fontSize: "13px",
3910
- display: "flex",
3911
- justifyContent: "space-between",
3912
- alignItems: "center",
3913
- cursor: hasMarkets ? "pointer" : "not-allowed",
3914
- opacity: hasMarkets ? 1 : 0.5,
3915
- fontFamily: "var(--compass-font-family)"
3916
- },
3917
- children: [
3918
- /* @__PURE__ */ jsxRuntime.jsx(
3919
- "span",
3920
- {
3921
- style: {
3922
- color: selectedMarket ? "var(--compass-color-text)" : "var(--compass-color-text-secondary)"
3923
- },
3924
- children: isLoading ? "Loading markets..." : !hasMarkets ? "No markets available" : selectedMarket ? selectedMarket.name : "Select a market"
3925
- }
3926
- ),
3927
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "var(--compass-spacing-unit)" }, children: [
3928
- selectedMarket && /* @__PURE__ */ jsxRuntime.jsxs(
3929
- "span",
3930
- {
3931
- style: {
3932
- color: "var(--compass-color-success)",
3933
- fontSize: "var(--compass-font-size-sm)"
3934
- },
3935
- children: [
3936
- selectedMarket.apy.toFixed(2),
3937
- "%"
3938
- ]
3939
- }
3940
- ),
3941
- /* @__PURE__ */ jsxRuntime.jsx(
3942
- lucideReact.ChevronDown,
3943
- {
3944
- size: 16,
3945
- style: {
3946
- color: "var(--compass-color-text-secondary)",
3947
- transform: isDropdownOpen ? "rotate(180deg)" : "rotate(0deg)",
3948
- transition: "var(--compass-transition-normal)"
3346
+ children: [
3347
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3348
+ /* @__PURE__ */ jsxRuntime.jsx(
3349
+ "div",
3350
+ {
3351
+ style: {
3352
+ fontSize: "var(--compass-font-size-xs)",
3353
+ color: "var(--compass-color-text-tertiary)",
3354
+ marginBottom: "calc(var(--compass-spacing-unit) * 0.5)"
3355
+ },
3356
+ children: "Unrealised P&L"
3949
3357
  }
3950
- }
3951
- )
3952
- ] })
3953
- ]
3954
- }
3955
- ),
3956
- isDropdownOpen && /* @__PURE__ */ jsxRuntime.jsx(
3957
- "div",
3958
- {
3959
- style: {
3960
- position: "absolute",
3961
- top: "100%",
3962
- left: 0,
3963
- right: 0,
3964
- marginTop: "var(--compass-spacing-unit, 8px)",
3965
- backgroundColor: "var(--compass-color-background, #0a0a0f)",
3966
- border: "1px solid var(--compass-color-border, rgba(255, 255, 255, 0.1))",
3967
- borderRadius: "var(--compass-border-radius-lg, 12px)",
3968
- boxShadow: "var(--compass-shadow-lg, 0 25px 50px -12px rgba(0, 0, 0, 0.5))",
3969
- zIndex: 50,
3970
- maxHeight: "200px",
3971
- overflowY: "auto"
3972
- },
3973
- children: filteredMarkets.map((market) => /* @__PURE__ */ jsxRuntime.jsxs(
3974
- "button",
3975
- {
3976
- onClick: () => {
3977
- onMarketSelect(market);
3978
- setIsDropdownOpen(false);
3979
- },
3980
- style: {
3981
- width: "100%",
3982
- padding: "calc(var(--compass-spacing-unit, 8px) * 2)",
3983
- backgroundColor: selectedMarket?.id === market.id ? "var(--compass-color-primary-muted, rgba(99, 102, 241, 0.15))" : "var(--compass-color-surface, #12121a)",
3984
- border: "none",
3985
- borderBottom: "1px solid var(--compass-color-border, rgba(255, 255, 255, 0.1))",
3986
- cursor: "pointer",
3987
- textAlign: "left",
3988
- fontFamily: "var(--compass-font-family)"
3989
- },
3990
- children: [
3358
+ ),
3991
3359
  /* @__PURE__ */ jsxRuntime.jsxs(
3992
3360
  "div",
3993
3361
  {
3994
3362
  style: {
3995
- display: "flex",
3996
- justifyContent: "space-between",
3997
- alignItems: "center"
3363
+ fontWeight: 600,
3364
+ color: position.pnl.unrealizedPnl >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)"
3998
3365
  },
3999
3366
  children: [
4000
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text)" }, children: market.name }),
4001
- /* @__PURE__ */ jsxRuntime.jsxs(
4002
- "span",
4003
- {
4004
- style: {
4005
- color: "var(--compass-color-success)",
4006
- fontSize: "var(--compass-font-size-sm)"
4007
- },
4008
- children: [
4009
- market.apy.toFixed(2),
4010
- "% APY"
4011
- ]
4012
- }
4013
- )
3367
+ position.pnl.unrealizedPnl >= 0 ? "+" : "",
3368
+ "$",
3369
+ position.pnl.unrealizedPnl.toFixed(2)
4014
3370
  ]
4015
3371
  }
4016
- ),
4017
- (showTvl && market.tvl > 0 || market.expiry) && /* @__PURE__ */ jsxRuntime.jsxs(
3372
+ )
3373
+ ] }),
3374
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3375
+ /* @__PURE__ */ jsxRuntime.jsx(
4018
3376
  "div",
4019
3377
  {
4020
3378
  style: {
4021
3379
  fontSize: "var(--compass-font-size-xs)",
4022
3380
  color: "var(--compass-color-text-tertiary)",
4023
- marginTop: "calc(var(--compass-spacing-unit) * 0.5)"
3381
+ marginBottom: "calc(var(--compass-spacing-unit) * 0.5)"
4024
3382
  },
4025
- children: [
4026
- showTvl && market.tvl > 0 && `TVL: ${formatTvl(market.tvl)}`,
4027
- showTvl && market.tvl > 0 && market.expiry && " \xB7 ",
4028
- market.expiry && `Expires: ${market.expiry}`
4029
- ]
4030
- }
4031
- )
4032
- ]
4033
- },
4034
- market.id
4035
- ))
4036
- }
4037
- )
4038
- ] })
4039
- ] })
4040
- ]
4041
- }
4042
- );
4043
- }
4044
- function PositionCard({ position }) {
4045
- const [isHistoryExpanded, setIsHistoryExpanded] = react.useState(false);
4046
- const formatShortDate = (dateStr) => {
4047
- if (!dateStr) return null;
4048
- const date = new Date(dateStr);
4049
- if (isNaN(date.getTime())) return null;
4050
- return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
4051
- };
4052
- return /* @__PURE__ */ jsxRuntime.jsxs(
4053
- "div",
4054
- {
4055
- style: {
4056
- backgroundColor: "var(--compass-color-surface, #12121a)",
4057
- border: "1px solid var(--compass-color-border, rgba(255, 255, 255, 0.1))",
4058
- borderRadius: "var(--compass-border-radius-lg, 12px)",
4059
- padding: "var(--compass-spacing-card, 16px)"
4060
- },
4061
- children: [
4062
- /* @__PURE__ */ jsxRuntime.jsxs(
4063
- "div",
4064
- {
4065
- style: {
4066
- display: "flex",
4067
- justifyContent: "space-between",
4068
- alignItems: "flex-start",
4069
- marginBottom: "calc(var(--compass-spacing-unit) * 2)"
4070
- },
4071
- children: [
4072
- /* @__PURE__ */ jsxRuntime.jsx(
4073
- "span",
4074
- {
4075
- style: {
4076
- fontWeight: 600,
4077
- color: "var(--compass-color-text)",
4078
- fontSize: "var(--compass-font-size-base)"
4079
- },
4080
- children: position.marketName
4081
- }
4082
- ),
4083
- /* @__PURE__ */ jsxRuntime.jsxs(
4084
- "span",
4085
- {
4086
- style: {
4087
- color: "var(--compass-color-success)",
4088
- fontSize: "var(--compass-font-size-sm)",
4089
- backgroundColor: "var(--compass-color-success-muted)",
4090
- padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 1.5)",
4091
- borderRadius: "var(--compass-border-radius-full)"
4092
- },
4093
- children: [
4094
- "APY: ",
4095
- position.apy.toFixed(2),
4096
- "%"
4097
- ]
4098
- }
4099
- )
4100
- ]
4101
- }
4102
- ),
4103
- /* @__PURE__ */ jsxRuntime.jsxs(
4104
- "div",
4105
- {
4106
- style: {
4107
- display: "grid",
4108
- gridTemplateColumns: "1fr 1fr",
4109
- gap: "calc(var(--compass-spacing-unit) * 2)",
4110
- marginBottom: "calc(var(--compass-spacing-unit) * 2)"
4111
- },
4112
- children: [
4113
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4114
- /* @__PURE__ */ jsxRuntime.jsx(
4115
- "div",
4116
- {
4117
- style: {
4118
- fontSize: "var(--compass-font-size-xs)",
4119
- color: "var(--compass-color-text-tertiary)",
4120
- marginBottom: "calc(var(--compass-spacing-unit) * 0.5)"
4121
- },
4122
- children: "Amount"
4123
- }
4124
- ),
4125
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "var(--compass-color-text)", fontWeight: 500 }, children: [
4126
- position.amount.toLocaleString(),
4127
- " ",
4128
- position.token
4129
- ] })
4130
- ] }),
4131
- (() => {
4132
- const deposits = position.transactions.filter((t) => t.type === "deposit" && t.timestamp);
4133
- const earliest = deposits.length > 0 ? deposits.reduce((min, t) => !min || t.timestamp < min ? t.timestamp : min, "") : void 0;
4134
- const formatted = formatShortDate(earliest);
4135
- if (!formatted) return null;
4136
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4137
- /* @__PURE__ */ jsxRuntime.jsx(
4138
- "div",
4139
- {
4140
- style: {
4141
- fontSize: "var(--compass-font-size-xs)",
4142
- color: "var(--compass-color-text-tertiary)",
4143
- marginBottom: "calc(var(--compass-spacing-unit) * 0.5)"
4144
- },
4145
- children: "Entered"
4146
- }
4147
- ),
4148
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--compass-color-text)" }, children: formatted })
4149
- ] });
4150
- })()
4151
- ]
4152
- }
4153
- ),
4154
- /* @__PURE__ */ jsxRuntime.jsxs(
4155
- "div",
4156
- {
4157
- style: {
4158
- paddingTop: "calc(var(--compass-spacing-unit) * 2)",
4159
- borderTop: "1px solid var(--compass-color-border)",
4160
- display: "flex",
4161
- flexDirection: "column",
4162
- gap: "calc(var(--compass-spacing-unit) * 1.5)"
4163
- },
4164
- children: [
4165
- position.pnl ? /* @__PURE__ */ jsxRuntime.jsxs(
4166
- "div",
4167
- {
4168
- style: {
4169
- display: "grid",
4170
- gridTemplateColumns: "1fr 1fr",
4171
- gap: "calc(var(--compass-spacing-unit) * 2)"
4172
- },
4173
- children: [
4174
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4175
- /* @__PURE__ */ jsxRuntime.jsx(
4176
- "div",
4177
- {
4178
- style: {
4179
- fontSize: "var(--compass-font-size-xs)",
4180
- color: "var(--compass-color-text-tertiary)",
4181
- marginBottom: "calc(var(--compass-spacing-unit) * 0.5)"
4182
- },
4183
- children: "Unrealised P&L"
4184
- }
4185
- ),
4186
- /* @__PURE__ */ jsxRuntime.jsxs(
4187
- "div",
4188
- {
4189
- style: {
4190
- fontWeight: 600,
4191
- color: position.pnl.unrealizedPnl >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)"
4192
- },
4193
- children: [
4194
- position.pnl.unrealizedPnl >= 0 ? "+" : "",
4195
- "$",
4196
- position.pnl.unrealizedPnl.toFixed(2)
4197
- ]
4198
- }
4199
- )
4200
- ] }),
4201
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4202
- /* @__PURE__ */ jsxRuntime.jsx(
4203
- "div",
4204
- {
4205
- style: {
4206
- fontSize: "var(--compass-font-size-xs)",
4207
- color: "var(--compass-color-text-tertiary)",
4208
- marginBottom: "calc(var(--compass-spacing-unit) * 0.5)"
4209
- },
4210
- children: "Realised P&L"
3383
+ children: "Realised P&L"
4211
3384
  }
4212
3385
  ),
4213
3386
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -4568,6 +3741,7 @@ function EarningsModal({ isOpen, onClose, positions, totalEarned, isLoading }) {
4568
3741
  style: {
4569
3742
  flex: 1,
4570
3743
  overflowY: "auto",
3744
+ scrollbarWidth: "none",
4571
3745
  padding: "var(--compass-spacing-card, 16px)",
4572
3746
  display: "flex",
4573
3747
  flexDirection: "column",
@@ -4761,7 +3935,7 @@ function EarnAccount({
4761
3935
  if (!response.ok) return [];
4762
3936
  const data = await response.json();
4763
3937
  const marketsList = data.markets || [];
4764
- const parseExpiry = (expiry) => {
3938
+ const parseExpiry2 = (expiry) => {
4765
3939
  if (!expiry || expiry === 0) return void 0;
4766
3940
  const timestamp = typeof expiry === "number" ? expiry : parseInt(expiry, 10);
4767
3941
  if (isNaN(timestamp) || timestamp <= 0) return void 0;
@@ -4771,7 +3945,7 @@ function EarnAccount({
4771
3945
  return date.toLocaleDateString("en-US", { month: "short", year: "numeric" });
4772
3946
  };
4773
3947
  return marketsList.map((m) => {
4774
- const expiryStr = parseExpiry(m.expiry);
3948
+ const expiryStr = parseExpiry2(m.expiry);
4775
3949
  return {
4776
3950
  id: `pendle-${m.marketAddress || m.address}`,
4777
3951
  name: m.name || `${m.underlyingSymbol || "PT"}${expiryStr ? ` ${expiryStr}` : ""}`,
@@ -6018,551 +5192,53 @@ function EarnAccount({
6018
5192
  )
6019
5193
  ] });
6020
5194
  }
6021
- function usePendleData(options = {}) {
6022
- const { address } = useEmbeddableWallet();
5195
+ function useSwapQuote({ fromToken, toToken, amount, enabled = true }) {
6023
5196
  const { chainId } = useChain();
6024
- const { sortBy = "fixed_apy", assetFilter, minTvl } = options;
6025
- const marketsQuery = reactQuery.useQuery({
6026
- queryKey: ["pendleMarkets", chainId, sortBy, assetFilter, minTvl],
5197
+ const { address } = useCompassWallet();
5198
+ const query = reactQuery.useQuery({
5199
+ queryKey: ["swapQuote", chainId, fromToken, toToken, amount, address],
6027
5200
  queryFn: async () => {
6028
- const underlyingSymbol = assetFilter && assetFilter.length === 1 ? assetFilter[0] : void 0;
6029
- const orderBy = sortBy === "tvl" ? "tvl_usd" : "implied_apy";
6030
- const params = new URLSearchParams({
6031
- chain: chainId,
6032
- orderBy,
6033
- direction: "desc",
6034
- limit: "100",
6035
- ...underlyingSymbol && { underlyingSymbol }
6036
- });
6037
- const response = await fetch(`/api/compass/pendle/markets?${params}`);
6038
- if (!response.ok) {
6039
- throw new Error("Failed to fetch Pendle markets");
6040
- }
6041
- const data = await response.json();
6042
- const now = Date.now() / 1e3;
6043
- let markets = (data.markets || []).filter((m) => m.expiry && m.expiry > now).map((m) => ({
6044
- marketAddress: m.marketAddress || "",
6045
- ptAddress: m.ptAddress || "",
6046
- name: m.ptName || `PT-${m.underlyingSymbol || "UNKNOWN"}`,
6047
- underlyingSymbol: m.underlyingSymbol || "UNKNOWN",
6048
- // Use impliedApy as the main APY (this is what wallet-earn displays)
6049
- fixedApy: m.impliedApy?.toString() ?? null,
6050
- impliedApy: m.impliedApy?.toString() ?? null,
6051
- tvlUsd: m.tvlUsd?.toString() ?? null,
6052
- // Convert Unix timestamp to ISO string for display
6053
- expiry: new Date(m.expiry * 1e3).toISOString()
6054
- }));
6055
- if (assetFilter && assetFilter.length > 1) {
6056
- markets = markets.filter(
6057
- (m) => assetFilter.includes(m.underlyingSymbol.toUpperCase())
6058
- );
6059
- }
6060
- if (minTvl !== void 0 && minTvl > 0) {
6061
- markets = markets.filter((m) => {
6062
- const tvl = parseFloat(m.tvlUsd || "0");
6063
- return tvl >= minTvl;
6064
- });
5201
+ if (!fromToken || !toToken || !amount || parseFloat(amount) <= 0 || !address) {
5202
+ return null;
6065
5203
  }
6066
- if (sortBy === "expiry") {
6067
- markets.sort((a, b) => {
6068
- return new Date(a.expiry).getTime() - new Date(b.expiry).getTime();
5204
+ try {
5205
+ const params = new URLSearchParams({
5206
+ owner: address,
5207
+ chain: chainId,
5208
+ tokenIn: fromToken,
5209
+ tokenOut: toToken,
5210
+ amountIn: amount
6069
5211
  });
5212
+ const response = await fetch(`/api/compass/swap/quote?${params}`);
5213
+ if (!response.ok) {
5214
+ const errorData = await response.json();
5215
+ const errorMessage = errorData.message || errorData.error || "Failed to get swap quote";
5216
+ throw new Error(errorMessage);
5217
+ }
5218
+ const data = await response.json();
5219
+ const outputAmount = data.estimatedAmountOut || "0";
5220
+ const inputAmountNum = parseFloat(amount);
5221
+ const outputAmountNum = parseFloat(outputAmount);
5222
+ return {
5223
+ inputAmount: amount,
5224
+ outputAmount,
5225
+ rate: inputAmountNum > 0 ? (outputAmountNum / inputAmountNum).toString() : "0"
5226
+ };
5227
+ } catch (error) {
5228
+ throw error;
6070
5229
  }
6071
- return markets;
6072
- },
6073
- staleTime: 30 * 1e3
6074
- });
6075
- const positionsQuery = reactQuery.useQuery({
6076
- queryKey: ["pendlePositions", chainId, address],
6077
- queryFn: async () => {
6078
- if (!address) return [];
6079
- const params = new URLSearchParams({
6080
- chain: chainId,
6081
- owner: address
6082
- });
6083
- const response = await fetch(`/api/compass/positions?${params}`);
6084
- if (!response.ok) {
6085
- throw new Error("Failed to fetch positions");
6086
- }
6087
- const data = await response.json();
6088
- return (data.pendlePt || []).map((p) => ({
6089
- marketAddress: p.marketAddress || "",
6090
- balance: p.balance || "0",
6091
- pnl: p.pnl ? {
6092
- unrealizedPnl: p.pnl.unrealizedPnl || "0",
6093
- realizedPnl: p.pnl.realizedPnl || "0",
6094
- totalPnl: p.pnl.totalPnl || "0",
6095
- totalDeposited: p.pnl.totalDeposited || "0"
6096
- } : void 0,
6097
- deposits: (p.deposits || []).map((d) => ({
6098
- amount: d.inputAmount || d.amount || "0",
6099
- blockNumber: d.blockNumber || 0,
6100
- txHash: d.transactionHash || d.txHash || ""
6101
- })),
6102
- withdrawals: (p.withdrawals || []).map((w) => ({
6103
- amount: w.outputAmount || w.amount || "0",
6104
- blockNumber: w.blockNumber || 0,
6105
- txHash: w.transactionHash || w.txHash || ""
6106
- }))
6107
- }));
6108
5230
  },
6109
- enabled: !!address,
6110
- staleTime: 30 * 1e3
6111
- });
6112
- const marketsWithPositions = (marketsQuery.data || []).map((market) => {
6113
- const position = positionsQuery.data?.find(
6114
- (p) => p.marketAddress.toLowerCase() === market.marketAddress.toLowerCase()
6115
- );
6116
- return { ...market, userPosition: position };
5231
+ enabled: enabled && !!address && !!fromToken && !!toToken && !!amount && parseFloat(amount) > 0,
5232
+ staleTime: 10 * 1e3,
5233
+ refetchInterval: 15 * 1e3,
5234
+ retry: 1
6117
5235
  });
6118
5236
  return {
6119
- markets: marketsWithPositions,
6120
- isLoading: marketsQuery.isLoading,
6121
- isError: marketsQuery.isError,
6122
- error: marketsQuery.error,
6123
- refetch: () => {
6124
- marketsQuery.refetch();
6125
- positionsQuery.refetch();
6126
- }
6127
- };
6128
- }
6129
- function formatAPY3(apy) {
6130
- if (!apy) return "0.00%";
6131
- return `${parseFloat(apy).toFixed(2)}%`;
6132
- }
6133
- function formatExpiry(expiry) {
6134
- const date = new Date(expiry);
6135
- return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
6136
- }
6137
- function formatTVL2(tvl) {
6138
- if (!tvl) return "$0";
6139
- const num = parseFloat(tvl);
6140
- if (num >= 1e9) return `$${(num / 1e9).toFixed(2)}B`;
6141
- if (num >= 1e6) return `$${(num / 1e6).toFixed(2)}M`;
6142
- if (num >= 1e3) return `$${(num / 1e3).toFixed(2)}K`;
6143
- return `$${num.toFixed(2)}`;
6144
- }
6145
- function PendleMarketsList({
6146
- showApy = true,
6147
- showTvl = true,
6148
- showExpiry = true,
6149
- showUserPosition = true,
6150
- showPnL = true,
6151
- showHistory = true,
6152
- showSearch = true,
6153
- showSort = true,
6154
- defaultSort = "fixed_apy",
6155
- assetFilter,
6156
- minTvl: initialMinTvl,
6157
- showTvlFilter = true,
6158
- onMarketSelect,
6159
- onDeposit,
6160
- onWithdraw
6161
- }) {
6162
- const [searchQuery, setSearchQuery] = react.useState("");
6163
- const [sortBy, setSortBy] = react.useState(defaultSort);
6164
- const [selectedMarket, setSelectedMarket] = react.useState(null);
6165
- const [minTvlFilter, setMinTvlFilter] = react.useState(initialMinTvl);
6166
- const [showFilterPanel, setShowFilterPanel] = react.useState(false);
6167
- const { markets, isLoading, isError, refetch } = usePendleData({ sortBy, assetFilter, minTvl: minTvlFilter });
6168
- const handleMinTvlChange = react.useCallback((value) => {
6169
- const num = parseFloat(value);
6170
- setMinTvlFilter(isNaN(num) || num <= 0 ? void 0 : num);
6171
- }, []);
6172
- const filteredMarkets = react.useMemo(() => {
6173
- if (!searchQuery) return markets;
6174
- const query = searchQuery.toLowerCase();
6175
- return markets.filter(
6176
- (m) => m.name.toLowerCase().includes(query) || m.underlyingSymbol.toLowerCase().includes(query)
6177
- );
6178
- }, [markets, searchQuery]);
6179
- const handleMarketClick = (market) => {
6180
- setSelectedMarket(market);
6181
- onMarketSelect?.(market);
6182
- };
6183
- const handleActionSuccess = (action, amount, txHash) => {
6184
- if (action === "deposit") {
6185
- onDeposit?.(selectedMarket, amount, txHash);
6186
- } else {
6187
- onWithdraw?.(selectedMarket, amount, txHash);
6188
- }
6189
- refetch();
6190
- };
6191
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "var(--compass-spacing-card)" }, children: [
6192
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between flex-wrap", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
6193
- /* @__PURE__ */ jsxRuntime.jsx(ChainSwitcher, {}),
6194
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
6195
- /* @__PURE__ */ jsxRuntime.jsx(EarnAccountBalance, { compact: true }),
6196
- /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
6197
- ] })
6198
- ] }),
6199
- (showSearch || showSort || showTvlFilter) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
6200
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
6201
- showSearch && /* @__PURE__ */ jsxRuntime.jsxs(
6202
- "div",
6203
- {
6204
- className: "flex-1 flex items-center border",
6205
- style: {
6206
- backgroundColor: "var(--compass-color-background)",
6207
- borderColor: "var(--compass-color-border)",
6208
- borderRadius: "var(--compass-border-radius-lg)",
6209
- fontFamily: "var(--compass-font-family)",
6210
- padding: "var(--compass-spacing-input)",
6211
- gap: "calc(var(--compass-spacing-unit) * 0.5)"
6212
- },
6213
- children: [
6214
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { size: 16, style: { color: "var(--compass-color-text-tertiary)" } }),
6215
- /* @__PURE__ */ jsxRuntime.jsx(
6216
- "input",
6217
- {
6218
- type: "text",
6219
- placeholder: "Search markets...",
6220
- value: searchQuery,
6221
- onChange: (e) => setSearchQuery(e.target.value),
6222
- className: "flex-1 bg-transparent outline-none text-sm",
6223
- style: { color: "var(--compass-color-text)" }
6224
- }
6225
- )
6226
- ]
6227
- }
6228
- ),
6229
- showSort && /* @__PURE__ */ jsxRuntime.jsxs(
6230
- "select",
6231
- {
6232
- value: sortBy,
6233
- onChange: (e) => setSortBy(e.target.value),
6234
- className: "border text-sm cursor-pointer",
6235
- style: {
6236
- backgroundColor: "var(--compass-color-background)",
6237
- borderColor: "var(--compass-color-border)",
6238
- color: "var(--compass-color-text)",
6239
- borderRadius: "var(--compass-border-radius-lg)",
6240
- fontFamily: "var(--compass-font-family)",
6241
- padding: "var(--compass-spacing-input)"
6242
- },
6243
- children: [
6244
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "fixed_apy", children: "Fixed APY" }),
6245
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "tvl", children: "TVL" }),
6246
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "expiry", children: "Expiry" })
6247
- ]
6248
- }
6249
- ),
6250
- showTvlFilter && /* @__PURE__ */ jsxRuntime.jsxs(
6251
- "button",
6252
- {
6253
- onClick: () => setShowFilterPanel(!showFilterPanel),
6254
- className: "border text-sm flex items-center",
6255
- style: {
6256
- backgroundColor: showFilterPanel || minTvlFilter ? "var(--compass-color-primary-muted)" : "var(--compass-color-background)",
6257
- borderColor: showFilterPanel || minTvlFilter ? "var(--compass-color-primary)" : "var(--compass-color-border)",
6258
- color: showFilterPanel || minTvlFilter ? "var(--compass-color-primary)" : "var(--compass-color-text)",
6259
- borderRadius: "var(--compass-border-radius-lg)",
6260
- fontFamily: "var(--compass-font-family)",
6261
- padding: "var(--compass-spacing-input)",
6262
- gap: "calc(var(--compass-spacing-unit) * 0.5)",
6263
- transition: "var(--compass-transition-normal)"
6264
- },
6265
- children: [
6266
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SlidersHorizontal, { size: 14 }),
6267
- "Filter"
6268
- ]
6269
- }
6270
- )
6271
- ] }),
6272
- showTvlFilter && showFilterPanel && /* @__PURE__ */ jsxRuntime.jsxs(
6273
- "div",
6274
- {
6275
- className: "flex items-center border",
6276
- style: {
6277
- backgroundColor: "var(--compass-color-surface)",
6278
- borderColor: "var(--compass-color-border)",
6279
- borderRadius: "var(--compass-border-radius-lg)",
6280
- fontFamily: "var(--compass-font-family)",
6281
- padding: "calc(var(--compass-spacing-unit) * 0.75)",
6282
- gap: "calc(var(--compass-spacing-unit) * 0.75)"
6283
- },
6284
- children: [
6285
- /* @__PURE__ */ jsxRuntime.jsx(
6286
- "label",
6287
- {
6288
- className: "text-sm font-medium whitespace-nowrap",
6289
- style: { color: "var(--compass-color-text-secondary)" },
6290
- children: "Min TVL:"
6291
- }
6292
- ),
6293
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
6294
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-tertiary)" }, children: "$" }),
6295
- /* @__PURE__ */ jsxRuntime.jsx(
6296
- "input",
6297
- {
6298
- type: "number",
6299
- placeholder: "0",
6300
- value: minTvlFilter || "",
6301
- onChange: (e) => handleMinTvlChange(e.target.value),
6302
- className: "w-24 border text-sm bg-transparent",
6303
- style: {
6304
- borderColor: "var(--compass-color-border)",
6305
- color: "var(--compass-color-text)",
6306
- borderRadius: "var(--compass-border-radius-sm)",
6307
- fontFamily: "var(--compass-font-family)",
6308
- padding: "calc(var(--compass-spacing-unit) * 0.25) calc(var(--compass-spacing-unit) * 0.5)"
6309
- }
6310
- }
6311
- )
6312
- ] }),
6313
- minTvlFilter && /* @__PURE__ */ jsxRuntime.jsx(
6314
- "button",
6315
- {
6316
- onClick: () => setMinTvlFilter(void 0),
6317
- className: "text-xs",
6318
- style: {
6319
- backgroundColor: "var(--compass-color-error-muted)",
6320
- color: "var(--compass-color-error)",
6321
- borderRadius: "var(--compass-border-radius-sm)",
6322
- padding: "calc(var(--compass-spacing-unit) * 0.25) calc(var(--compass-spacing-unit) * 0.5)",
6323
- transition: "var(--compass-transition-fast)"
6324
- },
6325
- children: "Clear"
6326
- }
6327
- )
6328
- ]
6329
- }
6330
- )
6331
- ] }),
6332
- isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center", style: { padding: "calc(var(--compass-spacing-unit) * 3) 0" }, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 24, className: "animate-spin", style: { color: "var(--compass-color-primary)" } }) }) : isError ? /* @__PURE__ */ jsxRuntime.jsx(
6333
- "div",
6334
- {
6335
- className: "text-center",
6336
- style: {
6337
- backgroundColor: "var(--compass-color-error-muted)",
6338
- color: "var(--compass-color-error)",
6339
- borderRadius: "var(--compass-border-radius-lg)",
6340
- padding: "var(--compass-spacing-card)"
6341
- },
6342
- children: "Failed to load Pendle markets. Please try again."
6343
- }
6344
- ) : filteredMarkets.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
6345
- "div",
6346
- {
6347
- className: "text-center",
6348
- style: { color: "var(--compass-color-text-secondary)", padding: "calc(var(--compass-spacing-unit) * 2)" },
6349
- children: "No active markets found"
6350
- }
6351
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.75)" }, children: filteredMarkets.map((market) => {
6352
- const hasPosition = market.userPosition && parseFloat(market.userPosition.balance) > 0;
6353
- return /* @__PURE__ */ jsxRuntime.jsxs(
6354
- "button",
6355
- {
6356
- onClick: () => handleMarketClick(market),
6357
- className: "w-full border text-left hover:scale-[1.01]",
6358
- style: {
6359
- backgroundColor: "var(--compass-color-surface)",
6360
- borderColor: hasPosition ? "var(--compass-color-primary)" : "var(--compass-color-border)",
6361
- borderRadius: "var(--compass-border-radius-xl)",
6362
- fontFamily: "var(--compass-font-family)",
6363
- padding: "var(--compass-spacing-card)",
6364
- transition: "var(--compass-transition-normal)"
6365
- },
6366
- children: [
6367
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
6368
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.25)" }, children: [
6369
- /* @__PURE__ */ jsxRuntime.jsx(
6370
- "h3",
6371
- {
6372
- className: "font-semibold",
6373
- style: {
6374
- fontSize: "var(--compass-font-size-body)",
6375
- color: "var(--compass-color-text)"
6376
- },
6377
- children: market.name
6378
- }
6379
- ),
6380
- /* @__PURE__ */ jsxRuntime.jsx(
6381
- "span",
6382
- {
6383
- className: "text-sm",
6384
- style: { color: "var(--compass-color-text-secondary)" },
6385
- children: market.underlyingSymbol
6386
- }
6387
- )
6388
- ] }),
6389
- showApy && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.25)" }, children: [
6390
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { size: 14, style: { color: "var(--compass-color-success)" } }),
6391
- /* @__PURE__ */ jsxRuntime.jsx(
6392
- "span",
6393
- {
6394
- className: "font-mono font-semibold",
6395
- style: { color: "var(--compass-color-success)" },
6396
- children: formatAPY3(market.fixedApy)
6397
- }
6398
- )
6399
- ] })
6400
- ] }),
6401
- /* @__PURE__ */ jsxRuntime.jsxs(
6402
- "div",
6403
- {
6404
- className: "flex items-center justify-between border-t",
6405
- style: {
6406
- borderColor: "var(--compass-color-border)",
6407
- marginTop: "calc(var(--compass-spacing-unit) * 0.75)",
6408
- paddingTop: "calc(var(--compass-spacing-unit) * 0.75)"
6409
- },
6410
- children: [
6411
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex", style: { gap: "var(--compass-spacing-card)" }, children: [
6412
- showTvl && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
6413
- /* @__PURE__ */ jsxRuntime.jsx(
6414
- "span",
6415
- {
6416
- className: "text-xs",
6417
- style: { color: "var(--compass-color-text-tertiary)" },
6418
- children: "TVL"
6419
- }
6420
- ),
6421
- /* @__PURE__ */ jsxRuntime.jsx(
6422
- "span",
6423
- {
6424
- className: "font-mono text-sm",
6425
- style: { color: "var(--compass-color-text-secondary)" },
6426
- children: formatTVL2(market.tvlUsd)
6427
- }
6428
- )
6429
- ] }),
6430
- showExpiry && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
6431
- /* @__PURE__ */ jsxRuntime.jsx(
6432
- "span",
6433
- {
6434
- className: "text-xs",
6435
- style: { color: "var(--compass-color-text-tertiary)" },
6436
- children: "Expiry"
6437
- }
6438
- ),
6439
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.25)" }, children: [
6440
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Calendar, { size: 12, style: { color: "var(--compass-color-text-secondary)" } }),
6441
- /* @__PURE__ */ jsxRuntime.jsx(
6442
- "span",
6443
- {
6444
- className: "font-mono text-sm",
6445
- style: { color: "var(--compass-color-text-secondary)" },
6446
- children: formatExpiry(market.expiry)
6447
- }
6448
- )
6449
- ] })
6450
- ] })
6451
- ] }),
6452
- showUserPosition && hasPosition && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-end", children: [
6453
- /* @__PURE__ */ jsxRuntime.jsx(
6454
- "span",
6455
- {
6456
- className: "text-xs",
6457
- style: { color: "var(--compass-color-text-tertiary)" },
6458
- children: "Your Position"
6459
- }
6460
- ),
6461
- /* @__PURE__ */ jsxRuntime.jsxs(
6462
- "span",
6463
- {
6464
- className: "font-mono text-sm font-medium",
6465
- style: { color: "var(--compass-color-primary)" },
6466
- children: [
6467
- parseFloat(market.userPosition.balance).toFixed(4),
6468
- " PT"
6469
- ]
6470
- }
6471
- )
6472
- ] })
6473
- ]
6474
- }
6475
- )
6476
- ]
6477
- },
6478
- market.marketAddress
6479
- );
6480
- }) }),
6481
- selectedMarket && /* @__PURE__ */ jsxRuntime.jsx(
6482
- ActionModal,
6483
- {
6484
- isOpen: !!selectedMarket,
6485
- onClose: () => setSelectedMarket(null),
6486
- title: selectedMarket.name,
6487
- children: /* @__PURE__ */ jsxRuntime.jsx(EarnAccountGuard, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "var(--compass-spacing-card)" }, children: [
6488
- showPnL && selectedMarket.userPosition?.pnl && /* @__PURE__ */ jsxRuntime.jsx(
6489
- PnLSummary,
6490
- {
6491
- pnl: selectedMarket.userPosition.pnl,
6492
- tokenSymbol: selectedMarket.underlyingSymbol,
6493
- tokenPrice: 1
6494
- }
6495
- ),
6496
- showHistory && (selectedMarket.userPosition?.deposits?.length || selectedMarket.userPosition?.withdrawals?.length) && /* @__PURE__ */ jsxRuntime.jsx(
6497
- TransactionHistory,
6498
- {
6499
- deposits: selectedMarket.userPosition?.deposits,
6500
- withdrawals: selectedMarket.userPosition?.withdrawals,
6501
- tokenSymbol: selectedMarket.underlyingSymbol
6502
- }
6503
- ),
6504
- /* @__PURE__ */ jsxRuntime.jsx(
6505
- DepositWithdrawForm,
6506
- {
6507
- venueType: "PENDLE_PT",
6508
- venueAddress: selectedMarket.marketAddress,
6509
- venueToken: selectedMarket.underlyingSymbol,
6510
- positionBalance: selectedMarket.userPosition?.balance,
6511
- onSuccess: handleActionSuccess
6512
- }
6513
- )
6514
- ] }) })
6515
- }
6516
- )
6517
- ] });
6518
- }
6519
- function useSwapQuote({ fromToken, toToken, amount, enabled = true }) {
6520
- const { chainId } = useChain();
6521
- const { address } = useCompassWallet();
6522
- const query = reactQuery.useQuery({
6523
- queryKey: ["swapQuote", chainId, fromToken, toToken, amount, address],
6524
- queryFn: async () => {
6525
- if (!fromToken || !toToken || !amount || parseFloat(amount) <= 0 || !address) {
6526
- return null;
6527
- }
6528
- try {
6529
- const params = new URLSearchParams({
6530
- owner: address,
6531
- chain: chainId,
6532
- tokenIn: fromToken,
6533
- tokenOut: toToken,
6534
- amountIn: amount
6535
- });
6536
- const response = await fetch(`/api/compass/swap/quote?${params}`);
6537
- if (!response.ok) {
6538
- const errorData = await response.json();
6539
- const errorMessage = errorData.message || errorData.error || "Failed to get swap quote";
6540
- throw new Error(errorMessage);
6541
- }
6542
- const data = await response.json();
6543
- const outputAmount = data.estimatedAmountOut || "0";
6544
- const inputAmountNum = parseFloat(amount);
6545
- const outputAmountNum = parseFloat(outputAmount);
6546
- return {
6547
- inputAmount: amount,
6548
- outputAmount,
6549
- rate: inputAmountNum > 0 ? (outputAmountNum / inputAmountNum).toString() : "0"
6550
- };
6551
- } catch (error) {
6552
- throw error;
6553
- }
6554
- },
6555
- enabled: enabled && !!address && !!fromToken && !!toToken && !!amount && parseFloat(amount) > 0,
6556
- staleTime: 10 * 1e3,
6557
- refetchInterval: 15 * 1e3,
6558
- retry: 1
6559
- });
6560
- return {
6561
- quote: query.data,
6562
- isLoading: query.isLoading,
6563
- isError: query.isError,
6564
- error: query.error,
6565
- refetch: query.refetch
5237
+ quote: query.data,
5238
+ isLoading: query.isLoading,
5239
+ isError: query.isError,
5240
+ error: query.error,
5241
+ refetch: query.refetch
6566
5242
  };
6567
5243
  }
6568
5244
 
@@ -7399,343 +6075,2039 @@ function PositionDetailModal({ position, onClose }) {
7399
6075
  isPendle && duration && /* @__PURE__ */ jsxRuntime.jsxs(
7400
6076
  "div",
7401
6077
  {
7402
- className: "p-3 rounded-lg",
7403
- style: { backgroundColor: "var(--compass-color-surface)" },
6078
+ className: "p-3 rounded-lg",
6079
+ style: { backgroundColor: "var(--compass-color-surface)" },
6080
+ children: [
6081
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
6082
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
6083
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Duration" })
6084
+ ] }),
6085
+ /* @__PURE__ */ jsxRuntime.jsx(
6086
+ "p",
6087
+ {
6088
+ className: "font-semibold",
6089
+ style: { color: "var(--compass-color-text)" },
6090
+ children: duration
6091
+ }
6092
+ )
6093
+ ]
6094
+ }
6095
+ ),
6096
+ isPendle && expiryDate && /* @__PURE__ */ jsxRuntime.jsxs(
6097
+ "div",
6098
+ {
6099
+ className: "p-3 rounded-lg",
6100
+ style: { backgroundColor: "var(--compass-color-surface)" },
6101
+ children: [
6102
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
6103
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Calendar, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
6104
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Expiry (UTC)" })
6105
+ ] }),
6106
+ /* @__PURE__ */ jsxRuntime.jsx(
6107
+ "p",
6108
+ {
6109
+ className: "font-semibold",
6110
+ style: { color: "var(--compass-color-text)" },
6111
+ children: expiryDate
6112
+ }
6113
+ )
6114
+ ]
6115
+ }
6116
+ ),
6117
+ position.pnl && /* @__PURE__ */ jsxRuntime.jsxs(
6118
+ "div",
6119
+ {
6120
+ className: "p-3 rounded-lg",
6121
+ style: { backgroundColor: "var(--compass-color-surface)" },
6122
+ children: [
6123
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
6124
+ 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)" } }),
6125
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Total P&L" })
6126
+ ] }),
6127
+ /* @__PURE__ */ jsxRuntime.jsxs(
6128
+ "p",
6129
+ {
6130
+ className: "font-semibold",
6131
+ style: {
6132
+ color: isPnlPositive ? "var(--compass-color-success)" : "var(--compass-color-error)"
6133
+ },
6134
+ children: [
6135
+ isPnlPositive ? "+" : "",
6136
+ formatUSD(position.pnl.totalPnl)
6137
+ ]
6138
+ }
6139
+ )
6140
+ ]
6141
+ }
6142
+ )
6143
+ ] }),
6144
+ (position.deposits.length > 0 || position.withdrawals.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
6145
+ /* @__PURE__ */ jsxRuntime.jsx(
6146
+ "h3",
6147
+ {
6148
+ className: "font-semibold mb-3",
6149
+ style: { color: "var(--compass-color-text)" },
6150
+ children: "Transaction History"
6151
+ }
6152
+ ),
6153
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
6154
+ position.deposits.map((tx, i) => {
6155
+ const date = formatDate(tx.timestamp);
6156
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6157
+ "div",
6158
+ {
6159
+ className: "flex items-center justify-between p-3 rounded-lg",
6160
+ style: { backgroundColor: "var(--compass-color-surface)" },
6161
+ children: [
6162
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
6163
+ /* @__PURE__ */ jsxRuntime.jsxs(
6164
+ "p",
6165
+ {
6166
+ className: "font-medium",
6167
+ style: { color: "var(--compass-color-success)" },
6168
+ children: [
6169
+ "+",
6170
+ formatAmount(tx.amount),
6171
+ " ",
6172
+ position.assetSymbol
6173
+ ]
6174
+ }
6175
+ ),
6176
+ /* @__PURE__ */ jsxRuntime.jsx(
6177
+ "p",
6178
+ {
6179
+ className: "text-xs",
6180
+ style: { color: "var(--compass-color-text-tertiary)" },
6181
+ children: date ? `Deposit - ${date}` : "Deposit"
6182
+ }
6183
+ )
6184
+ ] }),
6185
+ tx.txHash && /* @__PURE__ */ jsxRuntime.jsx(
6186
+ "a",
6187
+ {
6188
+ href: `https://etherscan.io/tx/${tx.txHash}`,
6189
+ target: "_blank",
6190
+ rel: "noopener noreferrer",
6191
+ className: "p-2 rounded-lg hover:opacity-80 transition-opacity",
6192
+ style: { backgroundColor: "var(--compass-color-background)" },
6193
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
6194
+ }
6195
+ )
6196
+ ]
6197
+ },
6198
+ `deposit-${i}`
6199
+ );
6200
+ }),
6201
+ position.withdrawals.map((tx, i) => {
6202
+ const date = formatDate(tx.timestamp);
6203
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6204
+ "div",
6205
+ {
6206
+ className: "flex items-center justify-between p-3 rounded-lg",
6207
+ style: { backgroundColor: "var(--compass-color-surface)" },
6208
+ children: [
6209
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
6210
+ /* @__PURE__ */ jsxRuntime.jsxs(
6211
+ "p",
6212
+ {
6213
+ className: "font-medium",
6214
+ style: { color: "var(--compass-color-error)" },
6215
+ children: [
6216
+ "-",
6217
+ formatAmount(tx.amount),
6218
+ " ",
6219
+ position.assetSymbol
6220
+ ]
6221
+ }
6222
+ ),
6223
+ /* @__PURE__ */ jsxRuntime.jsx(
6224
+ "p",
6225
+ {
6226
+ className: "text-xs",
6227
+ style: { color: "var(--compass-color-text-tertiary)" },
6228
+ children: date ? `Withdrawal - ${date}` : "Withdrawal"
6229
+ }
6230
+ )
6231
+ ] }),
6232
+ tx.txHash && /* @__PURE__ */ jsxRuntime.jsx(
6233
+ "a",
6234
+ {
6235
+ href: `https://etherscan.io/tx/${tx.txHash}`,
6236
+ target: "_blank",
6237
+ rel: "noopener noreferrer",
6238
+ className: "p-2 rounded-lg hover:opacity-80 transition-opacity",
6239
+ style: { backgroundColor: "var(--compass-color-background)" },
6240
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
6241
+ }
6242
+ )
6243
+ ]
6244
+ },
6245
+ `withdraw-${i}`
6246
+ );
6247
+ })
6248
+ ] })
6249
+ ] })
6250
+ ]
6251
+ }
6252
+ )
6253
+ }
6254
+ );
6255
+ }
6256
+ function EarnPositionsList({
6257
+ showApy = true,
6258
+ showPnL = true,
6259
+ defaultCollapsed = [],
6260
+ onPositionSelect
6261
+ }) {
6262
+ const { isConnected } = useEmbeddableWallet();
6263
+ const { venuePositions, totalPositions, totalBalanceUsd, isLoading, isError } = usePositionsData();
6264
+ const [selectedPosition, setSelectedPosition] = react.useState(null);
6265
+ const handlePositionClick = (position) => {
6266
+ setSelectedPosition(position);
6267
+ onPositionSelect?.(position);
6268
+ };
6269
+ const renderContent = () => {
6270
+ if (!isConnected) {
6271
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6272
+ "div",
6273
+ {
6274
+ className: "flex flex-col items-center justify-center py-12 text-center",
6275
+ style: { color: "var(--compass-color-text-secondary)" },
6276
+ children: [
6277
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Inbox, { size: 48, className: "mb-4 opacity-50" }),
6278
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "Connect your wallet" }),
6279
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mt-1", children: "to view your earn positions" })
6280
+ ]
6281
+ }
6282
+ );
6283
+ }
6284
+ if (isLoading) {
6285
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(
6286
+ lucideReact.Loader2,
6287
+ {
6288
+ size: 32,
6289
+ className: "animate-spin",
6290
+ style: { color: "var(--compass-color-primary)" }
6291
+ }
6292
+ ) });
6293
+ }
6294
+ if (isError) {
6295
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6296
+ "div",
6297
+ {
6298
+ className: "flex flex-col items-center justify-center py-12 text-center",
6299
+ style: { color: "var(--compass-color-error)" },
6300
+ children: [
6301
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "Failed to load positions" }),
6302
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mt-1", children: "Please try again later" })
6303
+ ]
6304
+ }
6305
+ );
6306
+ }
6307
+ if (totalPositions === 0) {
6308
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6309
+ "div",
6310
+ {
6311
+ className: "flex flex-col items-center justify-center py-12 text-center",
6312
+ style: { color: "var(--compass-color-text-secondary)" },
6313
+ children: [
6314
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Inbox, { size: 48, className: "mb-4 opacity-50" }),
6315
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "No positions yet" }),
6316
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mt-1", children: "Deposit to a vault or market to get started" })
6317
+ ]
6318
+ }
6319
+ );
6320
+ }
6321
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6322
+ /* @__PURE__ */ jsxRuntime.jsxs(
6323
+ "div",
6324
+ {
6325
+ className: "flex items-center justify-between p-4 rounded-xl",
6326
+ style: { backgroundColor: "var(--compass-color-surface)" },
6327
+ children: [
6328
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
6329
+ /* @__PURE__ */ jsxRuntime.jsx(
6330
+ "h2",
6331
+ {
6332
+ className: "font-bold text-lg",
6333
+ style: { color: "var(--compass-color-text)" },
6334
+ children: "Your Positions"
6335
+ }
6336
+ ),
6337
+ /* @__PURE__ */ jsxRuntime.jsxs(
6338
+ "p",
6339
+ {
6340
+ className: "text-sm",
6341
+ style: { color: "var(--compass-color-text-secondary)" },
6342
+ children: [
6343
+ totalPositions,
6344
+ " position",
6345
+ totalPositions !== 1 ? "s" : "",
6346
+ " across ",
6347
+ venuePositions.length,
6348
+ " venue",
6349
+ venuePositions.length !== 1 ? "s" : ""
6350
+ ]
6351
+ }
6352
+ )
6353
+ ] }),
6354
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
6355
+ /* @__PURE__ */ jsxRuntime.jsx(
6356
+ "p",
6357
+ {
6358
+ className: "text-sm",
6359
+ style: { color: "var(--compass-color-text-secondary)" },
6360
+ children: "Total Value"
6361
+ }
6362
+ ),
6363
+ /* @__PURE__ */ jsxRuntime.jsx(
6364
+ "p",
6365
+ {
6366
+ className: "font-bold text-xl",
6367
+ style: { color: "var(--compass-color-text)" },
6368
+ children: formatUSD(totalBalanceUsd)
6369
+ }
6370
+ )
6371
+ ] })
6372
+ ]
6373
+ }
6374
+ ),
6375
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: venuePositions.map((vp) => /* @__PURE__ */ jsxRuntime.jsx(
6376
+ VenueSection,
6377
+ {
6378
+ venuePositions: vp,
6379
+ defaultCollapsed: defaultCollapsed.includes(vp.venue),
6380
+ showApy,
6381
+ showPnL,
6382
+ onPositionSelect: handlePositionClick
6383
+ },
6384
+ vp.venue
6385
+ )) })
6386
+ ] });
6387
+ };
6388
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6389
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
6390
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
6391
+ /* @__PURE__ */ jsxRuntime.jsx(ChainSwitcher, {}),
6392
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
6393
+ /* @__PURE__ */ jsxRuntime.jsx(EarnAccountBalance, { compact: true }),
6394
+ /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
6395
+ ] })
6396
+ ] }),
6397
+ renderContent()
6398
+ ] }),
6399
+ selectedPosition && /* @__PURE__ */ jsxRuntime.jsx(
6400
+ PositionDetailModal,
6401
+ {
6402
+ position: selectedPosition,
6403
+ onClose: () => setSelectedPosition(null)
6404
+ }
6405
+ )
6406
+ ] });
6407
+ }
6408
+ function parseExpiry(expiry) {
6409
+ if (!expiry || expiry === 0) return void 0;
6410
+ const timestamp = typeof expiry === "number" ? expiry : parseInt(expiry, 10);
6411
+ if (isNaN(timestamp) || timestamp <= 0) return void 0;
6412
+ const ms = timestamp < 3250368e4 ? timestamp * 1e3 : timestamp;
6413
+ const date = new Date(ms);
6414
+ if (date.getFullYear() < 2e3) return void 0;
6415
+ return date.toLocaleDateString("en-US", { month: "short", year: "numeric" });
6416
+ }
6417
+ function useRebalancingData(chainOverride) {
6418
+ const { address } = useEmbeddableWallet();
6419
+ const { chainId: contextChainId } = useChain();
6420
+ const chainId = chainOverride || contextChainId;
6421
+ const positionsQuery = reactQuery.useQuery({
6422
+ queryKey: ["rebalancing", "portfolio", chainId, address],
6423
+ queryFn: async () => {
6424
+ if (!address) return [];
6425
+ const params = new URLSearchParams({ chain: chainId, owner: address });
6426
+ const response = await fetch(`/api/compass/positions?${params}`);
6427
+ if (!response.ok) throw new Error("Failed to fetch positions");
6428
+ const data = await response.json();
6429
+ const positions2 = [];
6430
+ for (const p of data.positions || []) {
6431
+ const usdValue = parseFloat(p.balanceUsd || p.balance || "0");
6432
+ if (usdValue <= 0) continue;
6433
+ let venueType;
6434
+ let venueAddress = "";
6435
+ if (p.protocol === "vaults") {
6436
+ venueType = "vault";
6437
+ venueAddress = p.vaultAddress || "";
6438
+ } else if (p.protocol === "aave") {
6439
+ venueType = "aave";
6440
+ venueAddress = p.symbol || "";
6441
+ } else if (p.protocol === "pendle") {
6442
+ venueType = "pendle_pt";
6443
+ venueAddress = p.marketAddress || "";
6444
+ } else {
6445
+ continue;
6446
+ }
6447
+ positions2.push({
6448
+ id: `${p.protocol}-${venueAddress}`,
6449
+ venueType,
6450
+ venueName: p.name || `${p.symbol} on ${p.protocol}`,
6451
+ venueAddress,
6452
+ token: p.symbol || "UNKNOWN",
6453
+ balance: parseFloat(p.balance || "0"),
6454
+ usdValue,
6455
+ apy: p.apy || 0,
6456
+ allocationPercent: 0
6457
+ // computed below
6458
+ });
6459
+ }
6460
+ return positions2;
6461
+ },
6462
+ enabled: !!address,
6463
+ staleTime: 3e4
6464
+ });
6465
+ const balancesQuery = reactQuery.useQuery({
6466
+ queryKey: ["rebalancing", "balances", chainId, address],
6467
+ queryFn: async () => {
6468
+ if (!address) return [];
6469
+ const params = new URLSearchParams({ chain: chainId, owner: address });
6470
+ const response = await fetch(`/api/compass/earn-account/balances?${params}`);
6471
+ if (!response.ok) throw new Error("Failed to fetch balances");
6472
+ const data = await response.json();
6473
+ const balances = [];
6474
+ for (const [symbol, tokenData] of Object.entries(data.balances || {})) {
6475
+ const td = tokenData;
6476
+ const usdValue = parseFloat(td.usdValue || "0");
6477
+ if (usdValue <= 0) continue;
6478
+ balances.push({
6479
+ token: symbol,
6480
+ balance: parseFloat(td.balance || "0"),
6481
+ usdValue
6482
+ });
6483
+ }
6484
+ return balances;
6485
+ },
6486
+ enabled: !!address,
6487
+ staleTime: 3e4
6488
+ });
6489
+ const venuesQuery = reactQuery.useQuery({
6490
+ queryKey: ["rebalancing", "venues", chainId],
6491
+ queryFn: async () => {
6492
+ const [vaultsRes, aaveRes, pendleRes] = await Promise.all([
6493
+ fetch(`/api/compass/vaults?chain=${chainId}&orderBy=apy_7d&direction=desc&limit=200`),
6494
+ fetch(`/api/compass/aave/markets?chain=${chainId}`),
6495
+ fetch(`/api/compass/pendle/markets?chain=${chainId}&orderBy=implied_apy&direction=desc&limit=200`)
6496
+ ]);
6497
+ const venues = [];
6498
+ const earnMarkets = [];
6499
+ if (vaultsRes.ok) {
6500
+ const data = await vaultsRes.json();
6501
+ const vaultsList = data.vaults || [];
6502
+ for (const v of vaultsList) {
6503
+ const vaultAddress = v.vaultAddress || v.address || "";
6504
+ const name = v.name || `${v.curatorName || "Morpho"} ${v.assetSymbol || "Vault"}`;
6505
+ const token = (v.assetSymbol || "UNKNOWN").toUpperCase();
6506
+ const apy = parseFloat(v.apy7d || v.apy || "0");
6507
+ venues.push({
6508
+ venueType: "vault",
6509
+ venueAddress: vaultAddress,
6510
+ venueName: name,
6511
+ token,
6512
+ apy
6513
+ });
6514
+ earnMarkets.push({
6515
+ id: `vault-${vaultAddress}`,
6516
+ name,
6517
+ apy,
6518
+ tvl: parseFloat(v.tvlUsd || v.tvl || "0"),
6519
+ type: "vaults",
6520
+ underlyingToken: token,
6521
+ curator: v.curatorName || v.curator,
6522
+ vaultAddress
6523
+ });
6524
+ }
6525
+ }
6526
+ if (aaveRes.ok) {
6527
+ const data = await aaveRes.json();
6528
+ const marketsDict = data.markets || {};
6529
+ for (const [symbol, marketData] of Object.entries(marketsDict)) {
6530
+ const chainData = marketData.chains?.[chainId];
6531
+ if (chainData) {
6532
+ const upperSymbol = symbol.toUpperCase();
6533
+ const apy = parseFloat(chainData.supplyApy || "0");
6534
+ venues.push({
6535
+ venueType: "aave",
6536
+ venueAddress: upperSymbol,
6537
+ venueName: `${upperSymbol} on Aave`,
6538
+ token: upperSymbol,
6539
+ apy
6540
+ });
6541
+ earnMarkets.push({
6542
+ id: `aave-${symbol.toLowerCase()}`,
6543
+ name: upperSymbol,
6544
+ apy,
6545
+ tvl: parseFloat(chainData.totalSupplyUsd || "0"),
6546
+ type: "aave",
6547
+ underlyingToken: upperSymbol
6548
+ });
6549
+ }
6550
+ }
6551
+ }
6552
+ if (pendleRes.ok) {
6553
+ const data = await pendleRes.json();
6554
+ const marketsList = data.markets || [];
6555
+ for (const m of marketsList) {
6556
+ const symbol = (m.underlyingSymbol || "PT").toUpperCase();
6557
+ const marketAddress = m.marketAddress || m.address || "";
6558
+ const expiryStr = parseExpiry(m.expiry);
6559
+ const name = m.name || `PT-${symbol}${expiryStr ? ` ${expiryStr}` : ""}`;
6560
+ const apy = parseFloat(m.impliedApy || m.apy || "0");
6561
+ venues.push({
6562
+ venueType: "pendle_pt",
6563
+ venueAddress: marketAddress,
6564
+ venueName: name,
6565
+ token: symbol,
6566
+ apy
6567
+ });
6568
+ earnMarkets.push({
6569
+ id: `pendle-${marketAddress}`,
6570
+ name,
6571
+ apy,
6572
+ tvl: parseFloat(m.tvlUsd || m.tvl || "0"),
6573
+ type: "pendle",
6574
+ underlyingToken: symbol,
6575
+ expiry: expiryStr,
6576
+ marketAddress
6577
+ });
6578
+ }
6579
+ }
6580
+ return { venues, earnMarkets };
6581
+ },
6582
+ staleTime: 6e4
6583
+ });
6584
+ const positions = positionsQuery.data || [];
6585
+ const idleBalances = balancesQuery.data || [];
6586
+ const totalPositionUsd = positions.reduce((sum, p) => sum + p.usdValue, 0);
6587
+ const totalIdleUsd = idleBalances.reduce((sum, b) => sum + b.usdValue, 0);
6588
+ const totalUsd = totalPositionUsd + totalIdleUsd;
6589
+ const positionsWithAllocation = positions.map((p) => ({
6590
+ ...p,
6591
+ allocationPercent: totalUsd > 0 ? p.usdValue / totalUsd * 100 : 0
6592
+ }));
6593
+ const portfolio = address ? {
6594
+ positions: positionsWithAllocation,
6595
+ idleBalances,
6596
+ totalUsd,
6597
+ totalIdleUsd
6598
+ } : null;
6599
+ return {
6600
+ portfolio,
6601
+ availableVenues: venuesQuery.data?.venues || [],
6602
+ earnAccountMarkets: venuesQuery.data?.earnMarkets || [],
6603
+ isMarketsLoading: venuesQuery.isLoading,
6604
+ isLoading: positionsQuery.isLoading || balancesQuery.isLoading || venuesQuery.isLoading,
6605
+ isError: positionsQuery.isError || balancesQuery.isError || venuesQuery.isError,
6606
+ error: positionsQuery.error || balancesQuery.error || venuesQuery.error,
6607
+ refetch: () => {
6608
+ positionsQuery.refetch();
6609
+ balancesQuery.refetch();
6610
+ venuesQuery.refetch();
6611
+ }
6612
+ };
6613
+ }
6614
+
6615
+ // src/components/RebalancingWidget/rebalancingEngine.ts
6616
+ var DEFAULT_OPTIONS = {
6617
+ slippagePercent: 0.5,
6618
+ minThresholdUsd: 0.01
6619
+ };
6620
+ function computeRebalancePlan(portfolio, targets, options = {}) {
6621
+ const opts = { ...DEFAULT_OPTIONS, ...options };
6622
+ const warnings = [];
6623
+ const actions = [];
6624
+ const totalUsd = portfolio.totalUsd;
6625
+ if (totalUsd <= 0) {
6626
+ return { actions: [], totalActions: 0, estimatedGasSavings: "$0", warnings: ["No portfolio value to rebalance"] };
6627
+ }
6628
+ const deltas = [];
6629
+ for (const target of targets) {
6630
+ const targetUsd = totalUsd * (target.targetPercent / 100);
6631
+ const position = portfolio.positions.find(
6632
+ (p) => p.venueType === target.venueType && p.venueAddress.toLowerCase() === target.venueAddress.toLowerCase()
6633
+ );
6634
+ const currentUsd = position?.usdValue || 0;
6635
+ const deltaUsd = targetUsd - currentUsd;
6636
+ if (Math.abs(deltaUsd) < opts.minThresholdUsd) continue;
6637
+ deltas.push({
6638
+ venueType: target.venueType,
6639
+ venueAddress: target.venueAddress,
6640
+ venueName: target.venueName,
6641
+ token: target.token,
6642
+ currentUsd,
6643
+ targetUsd,
6644
+ deltaUsd
6645
+ });
6646
+ }
6647
+ for (const position of portfolio.positions) {
6648
+ const hasTarget = targets.some(
6649
+ (t) => t.venueType === position.venueType && t.venueAddress.toLowerCase() === position.venueAddress.toLowerCase()
6650
+ );
6651
+ if (!hasTarget && position.usdValue >= opts.minThresholdUsd) {
6652
+ deltas.push({
6653
+ venueType: position.venueType,
6654
+ venueAddress: position.venueAddress,
6655
+ venueName: position.venueName,
6656
+ token: position.token,
6657
+ currentUsd: position.usdValue,
6658
+ targetUsd: 0,
6659
+ deltaUsd: -position.usdValue
6660
+ });
6661
+ }
6662
+ }
6663
+ const overAllocated = deltas.filter((d) => d.deltaUsd < 0);
6664
+ const underAllocated = deltas.filter((d) => d.deltaUsd > 0);
6665
+ for (const delta of overAllocated) {
6666
+ const withdrawUsd = Math.abs(delta.deltaUsd);
6667
+ const withdrawAmount = delta.currentUsd > 0 ? withdrawUsd / delta.currentUsd * (portfolio.positions.find(
6668
+ (p) => p.venueType === delta.venueType && p.venueAddress.toLowerCase() === delta.venueAddress.toLowerCase()
6669
+ )?.balance || 0) : 0;
6670
+ actions.push({
6671
+ type: "withdraw",
6672
+ venueType: delta.venueType,
6673
+ venueAddress: delta.venueAddress,
6674
+ token: delta.token,
6675
+ amount: withdrawAmount,
6676
+ usdValue: withdrawUsd
6677
+ });
6678
+ if (delta.venueType === "pendle_pt") {
6679
+ warnings.push(`Withdrawing from Pendle PT position (${delta.venueName}) - check maturity date before proceeding`);
6680
+ }
6681
+ }
6682
+ const withdrawnTokenAmounts = {};
6683
+ for (const action of actions) {
6684
+ if (action.type === "withdraw") {
6685
+ const key = action.token.toUpperCase();
6686
+ withdrawnTokenAmounts[key] = (withdrawnTokenAmounts[key] || 0) + action.usdValue;
6687
+ }
6688
+ }
6689
+ for (const idle of portfolio.idleBalances) {
6690
+ const key = idle.token.toUpperCase();
6691
+ withdrawnTokenAmounts[key] = (withdrawnTokenAmounts[key] || 0) + idle.usdValue;
6692
+ }
6693
+ const depositTokenNeeds = {};
6694
+ for (const delta of underAllocated) {
6695
+ const key = delta.token.toUpperCase();
6696
+ depositTokenNeeds[key] = (depositTokenNeeds[key] || 0) + delta.deltaUsd;
6697
+ }
6698
+ for (const [depositToken, neededUsd] of Object.entries(depositTokenNeeds)) {
6699
+ const availableUsd = withdrawnTokenAmounts[depositToken] || 0;
6700
+ const shortfallUsd = neededUsd - availableUsd;
6701
+ if (shortfallUsd > opts.minThresholdUsd) {
6702
+ for (const [withdrawToken, excessUsd] of Object.entries(withdrawnTokenAmounts)) {
6703
+ if (withdrawToken === depositToken) continue;
6704
+ if (excessUsd <= opts.minThresholdUsd) continue;
6705
+ const swapUsd = Math.min(shortfallUsd, excessUsd);
6706
+ if (swapUsd < opts.minThresholdUsd) continue;
6707
+ const slippageFactor = 1 - opts.slippagePercent / 100;
6708
+ actions.push({
6709
+ type: "swap",
6710
+ token: withdrawToken,
6711
+ amount: swapUsd,
6712
+ // USD-denominated for now
6713
+ usdValue: swapUsd,
6714
+ tokenOut: depositToken,
6715
+ estimatedAmountOut: swapUsd * slippageFactor
6716
+ // Approximate
6717
+ });
6718
+ withdrawnTokenAmounts[withdrawToken] -= swapUsd;
6719
+ withdrawnTokenAmounts[depositToken] = (withdrawnTokenAmounts[depositToken] || 0) + swapUsd * slippageFactor;
6720
+ warnings.push(`Swap ${withdrawToken} to ${depositToken} involves slippage risk`);
6721
+ break;
6722
+ }
6723
+ }
6724
+ }
6725
+ for (const delta of underAllocated) {
6726
+ const depositUsd = delta.deltaUsd;
6727
+ const depositAmount = depositUsd;
6728
+ actions.push({
6729
+ type: "deposit",
6730
+ venueType: delta.venueType,
6731
+ venueAddress: delta.venueAddress,
6732
+ token: delta.token,
6733
+ amount: depositAmount,
6734
+ usdValue: depositUsd
6735
+ });
6736
+ }
6737
+ const totalRebalanceUsd = actions.filter((a) => a.type === "withdraw" || a.type === "deposit").reduce((sum, a) => sum + a.usdValue, 0);
6738
+ if (totalRebalanceUsd > totalUsd * 0.5) {
6739
+ warnings.push("This rebalance involves more than 50% of your portfolio");
6740
+ }
6741
+ if (actions.some((a) => a.type === "swap")) {
6742
+ warnings.push("Swap amounts are estimates - actual amounts may vary due to slippage");
6743
+ }
6744
+ const sortOrder = { withdraw: 0, swap: 1, deposit: 2 };
6745
+ actions.sort((a, b) => sortOrder[a.type] - sortOrder[b.type]);
6746
+ const individualTxCount = actions.length;
6747
+ const bundleTxCount = 1;
6748
+ const savedTxs = Math.max(0, individualTxCount - bundleTxCount);
6749
+ const estimatedGasSavings = `~$${(savedTxs * 2).toFixed(0)}`;
6750
+ return {
6751
+ actions,
6752
+ totalActions: actions.length,
6753
+ estimatedGasSavings,
6754
+ warnings
6755
+ };
6756
+ }
6757
+ function PortfolioBalanceCard({
6758
+ totalUsd,
6759
+ totalIdleUsd,
6760
+ idleBalances,
6761
+ earnAccountAddress,
6762
+ isPositionsExpanded,
6763
+ onTogglePositions,
6764
+ positionCount,
6765
+ showTopUp = true,
6766
+ onTopUp
6767
+ }) {
6768
+ const [showBalancesModal, setShowBalancesModal] = react.useState(false);
6769
+ const tokenBalances = idleBalances.map((b) => ({
6770
+ symbol: b.token,
6771
+ balance: b.balance.toString(),
6772
+ usdValue: b.usdValue.toString()
6773
+ }));
6774
+ const earningInterestUsd = totalUsd - totalIdleUsd;
6775
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6776
+ /* @__PURE__ */ jsxRuntime.jsxs(
6777
+ "div",
6778
+ {
6779
+ style: {
6780
+ backgroundColor: "var(--compass-color-surface)",
6781
+ borderRadius: "var(--compass-border-radius-xl)",
6782
+ border: "1px solid var(--compass-color-border)",
6783
+ fontFamily: "var(--compass-font-family)",
6784
+ padding: "var(--compass-spacing-card)"
6785
+ },
6786
+ children: [
6787
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
6788
+ /* @__PURE__ */ jsxRuntime.jsxs(
6789
+ "button",
6790
+ {
6791
+ onClick: () => setShowBalancesModal(true),
6792
+ className: "flex items-center text-left transition-opacity hover:opacity-80",
6793
+ style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" },
6794
+ children: [
6795
+ /* @__PURE__ */ jsxRuntime.jsx(
6796
+ "span",
6797
+ {
6798
+ className: "text-xs font-medium uppercase tracking-wide",
6799
+ style: { color: "var(--compass-color-text-tertiary)" },
6800
+ children: "Total Balance"
6801
+ }
6802
+ ),
6803
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 12, style: { color: "var(--compass-color-text-tertiary)" } })
6804
+ ]
6805
+ }
6806
+ ),
6807
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
6808
+ /* @__PURE__ */ jsxRuntime.jsx(
6809
+ "button",
6810
+ {
6811
+ onClick: () => setShowBalancesModal(true),
6812
+ className: "transition-opacity hover:opacity-80",
6813
+ children: /* @__PURE__ */ jsxRuntime.jsx(
6814
+ "span",
6815
+ {
6816
+ className: "font-bold",
6817
+ style: {
6818
+ color: "var(--compass-color-text)",
6819
+ fontSize: "2rem",
6820
+ lineHeight: "1"
6821
+ },
6822
+ children: formatUSD(totalUsd)
6823
+ }
6824
+ )
6825
+ }
6826
+ ),
6827
+ showTopUp && onTopUp && /* @__PURE__ */ jsxRuntime.jsxs(
6828
+ "button",
6829
+ {
6830
+ onClick: onTopUp,
6831
+ className: "flex items-center font-medium transition-all hover:opacity-80",
6832
+ style: {
6833
+ backgroundColor: "var(--compass-color-surface-elevated, var(--compass-color-surface))",
6834
+ border: "1px solid var(--compass-color-border)",
6835
+ color: "var(--compass-color-text-secondary)",
6836
+ borderRadius: "var(--compass-border-radius-md)",
6837
+ padding: "6px 10px",
6838
+ gap: "4px",
6839
+ fontSize: "12px"
6840
+ },
6841
+ children: [
6842
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 14 }),
6843
+ "Top Up"
6844
+ ]
6845
+ }
6846
+ )
6847
+ ] })
6848
+ ] }),
6849
+ /* @__PURE__ */ jsxRuntime.jsx(
6850
+ "div",
6851
+ {
6852
+ style: {
6853
+ height: "1px",
6854
+ backgroundColor: "var(--compass-color-border)",
6855
+ margin: "var(--compass-spacing-card) 0"
6856
+ }
6857
+ }
6858
+ ),
6859
+ /* @__PURE__ */ jsxRuntime.jsx(
6860
+ "button",
6861
+ {
6862
+ onClick: onTogglePositions,
6863
+ className: "flex items-center justify-between w-full text-left transition-opacity hover:opacity-80",
6864
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.25)" }, children: [
6865
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
6866
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm", style: { color: "var(--compass-color-text-tertiary)" }, children: [
6867
+ "Earning interest",
6868
+ positionCount > 0 ? ` \xB7 ${positionCount} ${positionCount === 1 ? "position" : "positions"}` : ""
6869
+ ] }),
6870
+ positionCount > 0 && (isPositionsExpanded ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }))
6871
+ ] }),
6872
+ /* @__PURE__ */ jsxRuntime.jsx(
6873
+ "span",
6874
+ {
6875
+ className: "font-semibold",
6876
+ style: {
6877
+ color: "var(--compass-color-text)",
6878
+ fontSize: "1rem"
6879
+ },
6880
+ children: formatUSD(earningInterestUsd)
6881
+ }
6882
+ )
6883
+ ] })
6884
+ }
6885
+ )
6886
+ ]
6887
+ }
6888
+ ),
6889
+ /* @__PURE__ */ jsxRuntime.jsx(
6890
+ AccountBalancesModal,
6891
+ {
6892
+ isOpen: showBalancesModal,
6893
+ onClose: () => setShowBalancesModal(false),
6894
+ balances: tokenBalances,
6895
+ totalUsdValue: totalIdleUsd.toString(),
6896
+ earnAccountAddress
6897
+ }
6898
+ )
6899
+ ] });
6900
+ }
6901
+
6902
+ // src/components/RebalancingWidget/constants.ts
6903
+ var VENUE_COLORS = {
6904
+ vault: "var(--compass-color-primary)",
6905
+ aave: "#9333ea",
6906
+ pendle_pt: "#22c55e"
6907
+ };
6908
+ var VENUE_LABELS = {
6909
+ vault: "Vault",
6910
+ aave: "Aave",
6911
+ pendle_pt: "Pendle PT"
6912
+ };
6913
+ function AllocationEditor({
6914
+ portfolio,
6915
+ targets,
6916
+ targetSum,
6917
+ hasChanges,
6918
+ highlightedVenueAddress,
6919
+ onUpdatePercent,
6920
+ onRemoveVenue,
6921
+ onResetToCurrent,
6922
+ onEqualSplit
6923
+ }) {
6924
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
6925
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end mb-2", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
6926
+ /* @__PURE__ */ jsxRuntime.jsxs(
6927
+ "button",
6928
+ {
6929
+ onClick: onResetToCurrent,
6930
+ className: "text-xs px-2 py-1 rounded-md transition-colors",
6931
+ style: {
6932
+ backgroundColor: "var(--compass-color-background)",
6933
+ color: "var(--compass-color-text-secondary)"
6934
+ },
6935
+ title: "Reset to current",
6936
+ children: [
6937
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { size: 12, className: "inline mr-1" }),
6938
+ "Reset"
6939
+ ]
6940
+ }
6941
+ ),
6942
+ targets.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs(
6943
+ "button",
6944
+ {
6945
+ onClick: onEqualSplit,
6946
+ className: "text-xs px-2 py-1 rounded-md transition-colors",
6947
+ style: {
6948
+ backgroundColor: "var(--compass-color-background)",
6949
+ color: "var(--compass-color-text-secondary)"
6950
+ },
6951
+ title: "Split equally",
6952
+ children: [
6953
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Equal, { size: 12, className: "inline mr-1" }),
6954
+ "Equal"
6955
+ ]
6956
+ }
6957
+ )
6958
+ ] }),
6959
+ /* @__PURE__ */ jsxRuntime.jsxs(
6960
+ "div",
6961
+ {
6962
+ className: "flex flex-col",
6963
+ style: { gap: "6px" },
6964
+ children: [
6965
+ targets.map((target, index) => {
6966
+ const currentPos = portfolio.positions.find(
6967
+ (p) => p.venueType === target.venueType && p.venueAddress === target.venueAddress
6968
+ );
6969
+ const currentPercent = currentPos?.allocationPercent ?? 0;
6970
+ const diff = target.targetPercent - currentPercent;
6971
+ const hasDiff = Math.abs(diff) > 0.1;
6972
+ const isHighlighted = highlightedVenueAddress === target.venueAddress;
6973
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6974
+ "div",
6975
+ {
6976
+ className: "p-2.5 rounded-lg",
6977
+ style: {
6978
+ backgroundColor: "var(--compass-color-background)",
6979
+ borderLeft: isHighlighted ? "3px solid var(--compass-color-primary)" : "3px solid transparent"
6980
+ },
6981
+ children: [
6982
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
6983
+ /* @__PURE__ */ jsxRuntime.jsx(
6984
+ "div",
6985
+ {
6986
+ className: "w-1.5 rounded-full flex-shrink-0",
6987
+ style: {
6988
+ backgroundColor: VENUE_COLORS[target.venueType],
6989
+ alignSelf: "stretch"
6990
+ }
6991
+ }
6992
+ ),
6993
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
6994
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium truncate block", style: { color: "var(--compass-color-text)" }, children: target.venueName }),
6995
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
6996
+ /* @__PURE__ */ jsxRuntime.jsx(
6997
+ "span",
6998
+ {
6999
+ className: "text-xs px-1 py-0.5 rounded",
7000
+ style: {
7001
+ backgroundColor: VENUE_COLORS[target.venueType] + "20",
7002
+ color: VENUE_COLORS[target.venueType],
7003
+ fontSize: "10px"
7004
+ },
7005
+ children: VENUE_LABELS[target.venueType]
7006
+ }
7007
+ ),
7008
+ currentPos && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7009
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono", style: { color: "var(--compass-color-text-secondary)", fontSize: "10px" }, children: formatUSD(currentPos.usdValue) }),
7010
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "var(--compass-color-success)", fontSize: "10px" }, children: [
7011
+ currentPos.apy.toFixed(1),
7012
+ "%"
7013
+ ] })
7014
+ ] }),
7015
+ !currentPos && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-tertiary)", fontSize: "10px" }, children: "New" })
7016
+ ] })
7017
+ ] }),
7018
+ /* @__PURE__ */ jsxRuntime.jsx(
7019
+ "button",
7020
+ {
7021
+ onClick: () => onRemoveVenue(index),
7022
+ className: "w-5 h-5 flex items-center justify-center rounded flex-shrink-0 opacity-40 hover:opacity-100 transition-opacity",
7023
+ style: { color: "var(--compass-color-text-secondary)" },
7024
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 11 })
7025
+ }
7026
+ )
7027
+ ] }),
7028
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
7029
+ /* @__PURE__ */ jsxRuntime.jsx(
7030
+ "div",
7031
+ {
7032
+ className: "flex-1 h-1.5 rounded-full overflow-hidden",
7033
+ style: { backgroundColor: "var(--compass-color-border)" },
7034
+ children: /* @__PURE__ */ jsxRuntime.jsx(
7035
+ "div",
7036
+ {
7037
+ className: "h-full rounded-full transition-all",
7038
+ style: {
7039
+ width: `${Math.min(target.targetPercent, 100)}%`,
7040
+ backgroundColor: VENUE_COLORS[target.venueType],
7041
+ opacity: hasDiff ? 0.85 : 1
7042
+ }
7043
+ }
7044
+ )
7045
+ }
7046
+ ),
7047
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 flex-shrink-0", children: [
7048
+ /* @__PURE__ */ jsxRuntime.jsx(
7049
+ "input",
7050
+ {
7051
+ type: "number",
7052
+ value: target.targetPercent.toFixed(3),
7053
+ onChange: (e) => onUpdatePercent(index, parseFloat(e.target.value) || 0),
7054
+ className: "w-16 text-center text-xs font-mono rounded bg-transparent outline-none",
7055
+ style: {
7056
+ color: "var(--compass-color-text)",
7057
+ border: "1px solid var(--compass-color-border)",
7058
+ padding: "2px 0"
7059
+ },
7060
+ min: 0,
7061
+ max: 100,
7062
+ step: 1e-3
7063
+ }
7064
+ ),
7065
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)", width: "12px" }, children: "%" })
7066
+ ] })
7067
+ ] }),
7068
+ hasDiff && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end mt-0.5", children: /* @__PURE__ */ jsxRuntime.jsxs(
7069
+ "span",
7070
+ {
7071
+ className: "text-xs font-mono",
7072
+ style: { color: diff > 0 ? "var(--compass-color-success)" : "var(--compass-color-error)", fontSize: "10px" },
7073
+ children: [
7074
+ currentPercent.toFixed(3),
7075
+ "% \u2192 ",
7076
+ target.targetPercent.toFixed(3),
7077
+ "%"
7078
+ ]
7079
+ }
7080
+ ) })
7081
+ ]
7082
+ },
7083
+ `${target.venueType}-${target.venueAddress}`
7084
+ );
7085
+ }),
7086
+ portfolio.totalIdleUsd > 0 && /* @__PURE__ */ jsxRuntime.jsx(
7087
+ "div",
7088
+ {
7089
+ className: "p-2.5 rounded-lg",
7090
+ style: { backgroundColor: "var(--compass-color-background)" },
7091
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
7092
+ /* @__PURE__ */ jsxRuntime.jsx(
7093
+ "div",
7094
+ {
7095
+ className: "w-1.5 rounded-full flex-shrink-0",
7096
+ style: {
7097
+ backgroundColor: "var(--compass-color-text-tertiary)",
7098
+ alignSelf: "stretch",
7099
+ minHeight: "20px"
7100
+ }
7101
+ }
7102
+ ),
7103
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-secondary)" }, children: "Idle (undeployed)" }),
7104
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono ml-auto", style: { color: "var(--compass-color-text-secondary)" }, children: formatUSD(portfolio.totalIdleUsd) })
7105
+ ] })
7106
+ }
7107
+ )
7108
+ ]
7109
+ }
7110
+ ),
7111
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end mt-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
7112
+ "span",
7113
+ {
7114
+ className: "text-xs font-mono font-medium",
7115
+ style: {
7116
+ color: "var(--compass-color-text-secondary)"
7117
+ },
7118
+ children: [
7119
+ targetSum.toFixed(3),
7120
+ "% allocated"
7121
+ ]
7122
+ }
7123
+ ) })
7124
+ ] });
7125
+ }
7126
+ var SUPPORTED_TOKENS2 = ["USDC", "USDT", "DAI", "WETH", "SBC", "AUSD"];
7127
+ var EVM_CHAIN_IDS2 = {
7128
+ ethereum: 1,
7129
+ base: 8453,
7130
+ arbitrum: 42161
7131
+ };
7132
+ function formatAmount3(value) {
7133
+ const num = typeof value === "string" ? parseFloat(value) : value;
7134
+ if (isNaN(num)) return "0";
7135
+ return num.toFixed(6).replace(/\.?0+$/, "");
7136
+ }
7137
+ function RebalancingWidget({
7138
+ showChainSwitcher = true,
7139
+ showWalletStatus = true,
7140
+ minRebalanceThresholdUsd = 0.01,
7141
+ defaultSlippage = 0.5,
7142
+ chain,
7143
+ onRebalance,
7144
+ onError,
7145
+ title = "Portfolio Manager",
7146
+ venues,
7147
+ showTopUp = true,
7148
+ height = "600px"
7149
+ }) {
7150
+ const { chainId: contextChainId, setChainId } = useChain();
7151
+ const CHAIN_ID = chain || contextChainId;
7152
+ const { address, signTypedData, isConnected, login, switchChain, walletChainId } = useEmbeddableWallet();
7153
+ const queryClient = reactQuery.useQueryClient();
7154
+ const { portfolio, earnAccountMarkets, isMarketsLoading, isLoading, isError, error, refetch } = useRebalancingData(chain);
7155
+ const allowedVariableMarketIds = react.useMemo(() => {
7156
+ if (!venues) return void 0;
7157
+ if (!venues.aave && !venues.vaults) return void 0;
7158
+ const ids = [];
7159
+ if (venues.aave) {
7160
+ for (const symbol of venues.aave) ids.push(`aave-${symbol.toLowerCase()}`);
7161
+ }
7162
+ if (venues.vaults) {
7163
+ for (const addr of venues.vaults) ids.push(`vault-${addr}`);
7164
+ }
7165
+ return ids;
7166
+ }, [venues]);
7167
+ const allowedFixedMarketIds = react.useMemo(() => {
7168
+ if (!venues) return void 0;
7169
+ if (!venues.pendle) return void 0;
7170
+ const ids = [];
7171
+ for (const addr of venues.pendle) ids.push(`pendle-${addr}`);
7172
+ return ids;
7173
+ }, [venues]);
7174
+ react.useEffect(() => {
7175
+ if (chain && chain !== contextChainId) {
7176
+ setChainId(chain);
7177
+ }
7178
+ }, [chain, contextChainId, setChainId]);
7179
+ const [targets, setTargets] = react.useState([]);
7180
+ const [previewPlan, setPreviewPlan] = react.useState(null);
7181
+ const [serverPreview, setServerPreview] = react.useState(null);
7182
+ const [widgetState, setWidgetState] = react.useState("editing");
7183
+ const [errorMessage, setErrorMessage] = react.useState(null);
7184
+ const [txHash, setTxHash] = react.useState(null);
7185
+ const [hasInitializedTargets, setHasInitializedTargets] = react.useState(false);
7186
+ const [isPositionsExpanded, setIsPositionsExpanded] = react.useState(false);
7187
+ const [marketTab, setMarketTab] = react.useState("variable");
7188
+ const [selectedMarket, setSelectedMarket] = react.useState(null);
7189
+ const [selectedToken, setSelectedToken] = react.useState("USDC");
7190
+ const [depositAmount, setDepositAmount] = react.useState("");
7191
+ const [isTokenDropdownOpen, setIsTokenDropdownOpen] = react.useState(false);
7192
+ const [isProcessing, setIsProcessing] = react.useState(false);
7193
+ const [depositError, setDepositError] = react.useState(null);
7194
+ const [depositStatus, setDepositStatus] = react.useState("");
7195
+ const earnBalanceRef = react.useRef(null);
7196
+ react.useEffect(() => {
7197
+ setTargets([]);
7198
+ setPreviewPlan(null);
7199
+ setServerPreview(null);
7200
+ setWidgetState("editing");
7201
+ setErrorMessage(null);
7202
+ setTxHash(null);
7203
+ setHasInitializedTargets(false);
7204
+ setIsPositionsExpanded(false);
7205
+ setSelectedMarket(null);
7206
+ setDepositAmount("");
7207
+ setDepositError(null);
7208
+ setDepositStatus("");
7209
+ }, [CHAIN_ID]);
7210
+ react.useEffect(() => {
7211
+ if (portfolio && portfolio.positions.length > 0 && !hasInitializedTargets) {
7212
+ setTargets(
7213
+ portfolio.positions.map((p) => ({
7214
+ venueType: p.venueType,
7215
+ venueAddress: p.venueAddress,
7216
+ venueName: p.venueName,
7217
+ token: p.token,
7218
+ targetPercent: p.allocationPercent
7219
+ }))
7220
+ );
7221
+ setHasInitializedTargets(true);
7222
+ }
7223
+ }, [portfolio, hasInitializedTargets]);
7224
+ const state = react.useMemo(() => {
7225
+ if (isLoading) return "loading";
7226
+ if (!portfolio || portfolio.positions.length === 0) return "empty";
7227
+ return widgetState;
7228
+ }, [isLoading, portfolio, widgetState]);
7229
+ const targetSum = react.useMemo(() => targets.reduce((s, t) => s + t.targetPercent, 0), [targets]);
7230
+ const hasChanges = react.useMemo(() => {
7231
+ if (!portfolio || targets.length === 0) return false;
7232
+ const hasDiff = targets.some((t) => {
7233
+ const pos = portfolio.positions.find(
7234
+ (p) => p.venueType === t.venueType && p.venueAddress === t.venueAddress
7235
+ );
7236
+ return Math.abs(t.targetPercent - (pos?.allocationPercent ?? 0)) > 0.1;
7237
+ });
7238
+ const hasRemovedPositions = portfolio.positions.some(
7239
+ (p) => p.usdValue > 1 && !targets.some((t) => t.venueType === p.venueType && t.venueAddress === p.venueAddress)
7240
+ );
7241
+ return hasDiff || hasRemovedPositions;
7242
+ }, [portfolio, targets]);
7243
+ const clientPreview = react.useMemo(() => {
7244
+ if (!portfolio || !hasChanges) return null;
7245
+ return computeRebalancePlan(portfolio, targets, {
7246
+ slippagePercent: defaultSlippage,
7247
+ minThresholdUsd: minRebalanceThresholdUsd
7248
+ });
7249
+ }, [portfolio, targets, hasChanges, defaultSlippage, minRebalanceThresholdUsd]);
7250
+ const handleResetToCurrent = react.useCallback(() => {
7251
+ if (!portfolio) return;
7252
+ setTargets(
7253
+ portfolio.positions.map((p) => ({
7254
+ venueType: p.venueType,
7255
+ venueAddress: p.venueAddress,
7256
+ venueName: p.venueName,
7257
+ token: p.token,
7258
+ targetPercent: p.allocationPercent
7259
+ }))
7260
+ );
7261
+ setPreviewPlan(null);
7262
+ setServerPreview(null);
7263
+ setWidgetState("editing");
7264
+ }, [portfolio]);
7265
+ const handleEqualSplit = react.useCallback(() => {
7266
+ if (targets.length === 0) return;
7267
+ const equalPercent = 100 / targets.length;
7268
+ setTargets((prev) => prev.map((t) => ({ ...t, targetPercent: parseFloat(equalPercent.toFixed(2)) })));
7269
+ }, [targets.length]);
7270
+ const handleRemoveVenue = react.useCallback((index) => {
7271
+ setTargets((prev) => prev.filter((_, i) => i !== index));
7272
+ }, []);
7273
+ const handleUpdatePercent = react.useCallback((index, value) => {
7274
+ setTargets((prev) => prev.map((t, i) => i === index ? { ...t, targetPercent: Math.max(0, Math.min(100, value)) } : t));
7275
+ }, []);
7276
+ const handlePreview = react.useCallback(async () => {
7277
+ if (!portfolio || !hasChanges || !address) return;
7278
+ setWidgetState("previewing");
7279
+ setErrorMessage(null);
7280
+ try {
7281
+ const response = await fetch("/api/compass/rebalance/preview", {
7282
+ method: "POST",
7283
+ headers: { "Content-Type": "application/json" },
7284
+ body: JSON.stringify({
7285
+ owner: address,
7286
+ chain: CHAIN_ID,
7287
+ targets: targets.map((t) => ({
7288
+ venueType: t.venueType === "vault" ? "VAULT" : t.venueType === "aave" ? "AAVE" : "PENDLE_PT",
7289
+ venueAddress: t.venueAddress,
7290
+ targetPercent: t.targetPercent,
7291
+ token: t.token
7292
+ })),
7293
+ slippage: defaultSlippage
7294
+ })
7295
+ });
7296
+ if (!response.ok) {
7297
+ const err = await response.json();
7298
+ throw new Error(err.error || "Failed to compute rebalance preview");
7299
+ }
7300
+ const data = await response.json();
7301
+ setServerPreview(data);
7302
+ setPreviewPlan(clientPreview);
7303
+ setWidgetState("ready");
7304
+ } catch (err) {
7305
+ setErrorMessage(err instanceof Error ? err.message : "Preview failed");
7306
+ setWidgetState("error");
7307
+ onError?.(err instanceof Error ? err : new Error("Preview failed"));
7308
+ }
7309
+ }, [portfolio, hasChanges, address, CHAIN_ID, targets, defaultSlippage, clientPreview, onError]);
7310
+ const handleExecute = react.useCallback(async () => {
7311
+ if (!serverPreview?.eip712 || !address) return;
7312
+ setWidgetState("signing");
7313
+ setErrorMessage(null);
7314
+ try {
7315
+ const signature = await signTypedData({
7316
+ domain: serverPreview.domain,
7317
+ types: serverPreview.normalizedTypes,
7318
+ primaryType: "SafeTx",
7319
+ message: serverPreview.message
7320
+ });
7321
+ setWidgetState("executing");
7322
+ const response = await fetch("/api/compass/bundle/execute", {
7323
+ method: "POST",
7324
+ headers: { "Content-Type": "application/json" },
7325
+ body: JSON.stringify({
7326
+ owner: address,
7327
+ chain: CHAIN_ID,
7328
+ eip712: serverPreview.eip712,
7329
+ signature
7330
+ })
7331
+ });
7332
+ if (!response.ok) {
7333
+ const err = await response.json();
7334
+ throw new Error(err.error || "Transaction execution failed");
7335
+ }
7336
+ const data = await response.json();
7337
+ setTxHash(data.txHash);
7338
+ setWidgetState("success");
7339
+ setTimeout(() => refetch(), 5e3);
7340
+ setTimeout(() => refetch(), 15e3);
7341
+ setTimeout(() => refetch(), 3e4);
7342
+ if (previewPlan) {
7343
+ onRebalance?.(previewPlan, data.txHash);
7344
+ }
7345
+ } catch (err) {
7346
+ setErrorMessage(err instanceof Error ? err.message : "Execution failed");
7347
+ setWidgetState("error");
7348
+ onError?.(err instanceof Error ? err : new Error("Execution failed"));
7349
+ }
7350
+ }, [serverPreview, address, signTypedData, CHAIN_ID, refetch, previewPlan, onRebalance, onError]);
7351
+ const earnBalancesQuery = react.useMemo(() => {
7352
+ const idleBalance = portfolio?.idleBalances.find((b) => b.token === selectedToken);
7353
+ return idleBalance?.balance ?? 0;
7354
+ }, [portfolio, selectedToken]);
7355
+ const needsSwap = selectedMarket ? selectedToken !== selectedMarket.underlyingToken : false;
7356
+ const ensureCorrectChain = react.useCallback(async () => {
7357
+ const targetChainId = EVM_CHAIN_IDS2[CHAIN_ID];
7358
+ if (!targetChainId) return;
7359
+ if (walletChainId !== void 0 && walletChainId !== targetChainId) {
7360
+ if (!switchChain) {
7361
+ throw new Error(`Please switch your wallet to ${CHAIN_ID} (chain ${targetChainId})`);
7362
+ }
7363
+ await switchChain(targetChainId);
7364
+ }
7365
+ }, [walletChainId, switchChain, CHAIN_ID]);
7366
+ const buildVenueParamsFromMarket = (market) => {
7367
+ switch (market.type) {
7368
+ case "aave":
7369
+ return { venueType: "AAVE", token: market.underlyingToken };
7370
+ case "vaults":
7371
+ return { venueType: "VAULT", token: market.underlyingToken, vaultAddress: market.vaultAddress };
7372
+ case "pendle":
7373
+ return { venueType: "PENDLE_PT", token: market.underlyingToken, marketAddress: market.marketAddress, maxSlippagePercent: 1 };
7374
+ }
7375
+ };
7376
+ const buildVenueFromMarket = (market) => {
7377
+ switch (market.type) {
7378
+ case "aave":
7379
+ return { type: "AAVE", token: market.underlyingToken };
7380
+ case "vaults":
7381
+ return { type: "VAULT", vaultAddress: market.vaultAddress };
7382
+ case "pendle":
7383
+ return { type: "PENDLE_PT", marketAddress: market.marketAddress, token: market.underlyingToken, maxSlippagePercent: 1 };
7384
+ }
7385
+ };
7386
+ const handleDeposit = react.useCallback(async () => {
7387
+ if (!selectedMarket || !depositAmount || parseFloat(depositAmount) <= 0 || !address || !signTypedData) return;
7388
+ setIsProcessing(true);
7389
+ setDepositError(null);
7390
+ const underlyingToken = selectedMarket.underlyingToken;
7391
+ try {
7392
+ let resultTxHash;
7393
+ if (needsSwap) {
7394
+ setDepositStatus("Getting swap quote...");
7395
+ const quoteResponse = await fetch(
7396
+ `/api/compass/swap/quote?owner=${address}&chain=${CHAIN_ID}&tokenIn=${selectedToken}&tokenOut=${underlyingToken}&amountIn=${depositAmount}`
7397
+ );
7398
+ if (!quoteResponse.ok) {
7399
+ const errorData = await quoteResponse.json();
7400
+ throw new Error(errorData.error || "Failed to get swap quote");
7401
+ }
7402
+ const quoteData = await quoteResponse.json();
7403
+ const estimatedOutput = quoteData.estimatedAmountOut;
7404
+ if (!estimatedOutput || parseFloat(estimatedOutput) <= 0) {
7405
+ throw new Error("Invalid swap quote - no output amount");
7406
+ }
7407
+ const actualDepositAmount = (parseFloat(estimatedOutput) * 0.99999).toString();
7408
+ setDepositStatus("Preparing swap and deposit...");
7409
+ const bundleActions = [
7410
+ {
7411
+ body: {
7412
+ actionType: "V2_SWAP",
7413
+ tokenIn: selectedToken,
7414
+ tokenOut: underlyingToken,
7415
+ amountIn: depositAmount,
7416
+ maxSlippagePercent: 1
7417
+ }
7418
+ },
7419
+ {
7420
+ body: {
7421
+ actionType: "V2_MANAGE",
7422
+ action: "DEPOSIT",
7423
+ venue: buildVenueFromMarket(selectedMarket),
7424
+ amount: actualDepositAmount
7425
+ }
7426
+ }
7427
+ ];
7428
+ const prepareResponse = await fetch("/api/compass/bundle/prepare", {
7429
+ method: "POST",
7430
+ headers: { "Content-Type": "application/json" },
7431
+ body: JSON.stringify({
7432
+ owner: address,
7433
+ chain: CHAIN_ID,
7434
+ actions: bundleActions
7435
+ })
7436
+ });
7437
+ if (!prepareResponse.ok) {
7438
+ const errorData = await prepareResponse.json();
7439
+ throw new Error(errorData.error || "Failed to prepare bundle");
7440
+ }
7441
+ const { eip712, normalizedTypes, domain, message } = await prepareResponse.json();
7442
+ setDepositStatus("Please sign the transaction...");
7443
+ await ensureCorrectChain();
7444
+ const signature = await signTypedData({
7445
+ domain,
7446
+ types: normalizedTypes,
7447
+ primaryType: "SafeTx",
7448
+ message
7449
+ });
7450
+ setDepositStatus("Executing swap and deposit...");
7451
+ const executeResponse = await fetch("/api/compass/bundle/execute", {
7452
+ method: "POST",
7453
+ headers: { "Content-Type": "application/json" },
7454
+ body: JSON.stringify({
7455
+ owner: address,
7456
+ eip712,
7457
+ signature,
7458
+ chain: CHAIN_ID
7459
+ })
7460
+ });
7461
+ if (!executeResponse.ok) {
7462
+ const errorData = await executeResponse.json();
7463
+ throw new Error(errorData.error || "Failed to execute bundle");
7464
+ }
7465
+ const result = await executeResponse.json();
7466
+ resultTxHash = result.txHash;
7467
+ } else {
7468
+ setDepositStatus("Preparing deposit...");
7469
+ const venueParams = buildVenueParamsFromMarket(selectedMarket);
7470
+ const prepareResponse = await fetch("/api/compass/deposit/prepare", {
7471
+ method: "POST",
7472
+ headers: { "Content-Type": "application/json" },
7473
+ body: JSON.stringify({
7474
+ owner: address,
7475
+ chain: CHAIN_ID,
7476
+ ...venueParams,
7477
+ amount: depositAmount
7478
+ })
7479
+ });
7480
+ if (!prepareResponse.ok) {
7481
+ const errData = await prepareResponse.json();
7482
+ throw new Error(errData.error || "Failed to prepare deposit");
7483
+ }
7484
+ const prepareData = await prepareResponse.json();
7485
+ setDepositStatus("Please sign the transaction...");
7486
+ await ensureCorrectChain();
7487
+ const signature = await signTypedData({
7488
+ domain: prepareData.domain,
7489
+ types: prepareData.normalizedTypes,
7490
+ primaryType: "SafeTx",
7491
+ message: prepareData.message
7492
+ });
7493
+ setDepositStatus("Executing deposit...");
7494
+ const executeResponse = await fetch("/api/compass/deposit/execute", {
7495
+ method: "POST",
7496
+ headers: { "Content-Type": "application/json" },
7497
+ body: JSON.stringify({
7498
+ owner: address,
7499
+ chain: CHAIN_ID,
7500
+ eip712: prepareData.eip712,
7501
+ signature
7502
+ })
7503
+ });
7504
+ if (!executeResponse.ok) {
7505
+ const errData = await executeResponse.json();
7506
+ throw new Error(errData.error || "Transaction failed");
7507
+ }
7508
+ const result = await executeResponse.json();
7509
+ resultTxHash = result.txHash;
7510
+ }
7511
+ setDepositStatus("Deposit successful!");
7512
+ setDepositAmount("");
7513
+ queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
7514
+ queryClient.invalidateQueries({ queryKey: ["rebalancing"] });
7515
+ refetch();
7516
+ const delays = [5e3, 15e3, 3e4];
7517
+ for (const delay of delays) {
7518
+ setTimeout(() => {
7519
+ queryClient.invalidateQueries({ queryKey: ["rebalancing"] });
7520
+ refetch();
7521
+ }, delay);
7522
+ }
7523
+ setTimeout(() => setDepositStatus(""), 3e3);
7524
+ } catch (err) {
7525
+ setDepositError(err instanceof Error ? err.message : "Deposit failed");
7526
+ setDepositStatus("");
7527
+ onError?.(err instanceof Error ? err : new Error("Deposit failed"));
7528
+ } finally {
7529
+ setIsProcessing(false);
7530
+ }
7531
+ }, [selectedMarket, depositAmount, address, signTypedData, selectedToken, needsSwap, CHAIN_ID, ensureCorrectChain, queryClient, refetch, onError]);
7532
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7533
+ "div",
7534
+ {
7535
+ className: "flex flex-col w-full",
7536
+ style: {
7537
+ gap: "calc(var(--compass-spacing-unit) * 0.75)",
7538
+ fontFamily: "var(--compass-font-family)",
7539
+ height,
7540
+ overflow: "hidden"
7541
+ },
7542
+ children: [
7543
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
7544
+ /* @__PURE__ */ jsxRuntime.jsx(
7545
+ "h2",
7546
+ {
7547
+ className: "font-semibold",
7548
+ style: {
7549
+ color: "var(--compass-color-text)",
7550
+ fontSize: "var(--compass-font-size-subheading, 1.25rem)"
7551
+ },
7552
+ children: title
7553
+ }
7554
+ ),
7555
+ /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
7556
+ ] }),
7557
+ showChainSwitcher && !chain && /* @__PURE__ */ jsxRuntime.jsx(ChainSwitcher, {}),
7558
+ /* @__PURE__ */ jsxRuntime.jsx(EarnAccountGuard, { children: /* @__PURE__ */ jsxRuntime.jsxs(
7559
+ "div",
7560
+ {
7561
+ style: {
7562
+ flex: 1,
7563
+ minHeight: 0,
7564
+ overflowY: "auto",
7565
+ scrollbarWidth: "none",
7566
+ display: "flex",
7567
+ flexDirection: "column",
7568
+ gap: "calc(var(--compass-spacing-unit) * 0.75)"
7569
+ },
7570
+ children: [
7571
+ state === "loading" && /* @__PURE__ */ jsxRuntime.jsxs(
7572
+ "div",
7573
+ {
7574
+ className: "p-8 text-center",
7575
+ style: {
7576
+ backgroundColor: "var(--compass-color-surface)",
7577
+ borderRadius: "var(--compass-border-radius-xl)"
7578
+ },
7404
7579
  children: [
7405
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
7406
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
7407
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Duration" })
7408
- ] }),
7409
7580
  /* @__PURE__ */ jsxRuntime.jsx(
7410
- "p",
7581
+ lucideReact.Loader2,
7411
7582
  {
7412
- className: "font-semibold",
7413
- style: { color: "var(--compass-color-text)" },
7414
- children: duration
7583
+ size: 24,
7584
+ className: "animate-spin mx-auto mb-3",
7585
+ style: { color: "var(--compass-color-primary)" }
7415
7586
  }
7416
- )
7587
+ ),
7588
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm", style: { color: "var(--compass-color-text-secondary)" }, children: "Loading portfolio..." })
7417
7589
  ]
7418
7590
  }
7419
7591
  ),
7420
- isPendle && expiryDate && /* @__PURE__ */ jsxRuntime.jsxs(
7592
+ state === "empty" && /* @__PURE__ */ jsxRuntime.jsxs(
7421
7593
  "div",
7422
7594
  {
7423
- className: "p-3 rounded-lg",
7424
- style: { backgroundColor: "var(--compass-color-surface)" },
7595
+ className: "p-8 text-center",
7596
+ style: {
7597
+ backgroundColor: "var(--compass-color-surface)",
7598
+ borderRadius: "var(--compass-border-radius-xl)"
7599
+ },
7425
7600
  children: [
7426
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
7427
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Calendar, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
7428
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Expiry (UTC)" })
7429
- ] }),
7430
7601
  /* @__PURE__ */ jsxRuntime.jsx(
7431
7602
  "p",
7432
7603
  {
7433
- className: "font-semibold",
7604
+ className: "text-lg font-semibold mb-2",
7434
7605
  style: { color: "var(--compass-color-text)" },
7435
- children: expiryDate
7606
+ children: "No positions found"
7436
7607
  }
7437
- )
7608
+ ),
7609
+ /* @__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." })
7438
7610
  ]
7439
7611
  }
7440
7612
  ),
7441
- position.pnl && /* @__PURE__ */ jsxRuntime.jsxs(
7442
- "div",
7443
- {
7444
- className: "p-3 rounded-lg",
7445
- style: { backgroundColor: "var(--compass-color-surface)" },
7446
- children: [
7447
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
7448
- 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)" } }),
7449
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "Total P&L" })
7450
- ] }),
7613
+ state !== "loading" && state !== "empty" && portfolio && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.75)" }, children: [
7614
+ /* @__PURE__ */ jsxRuntime.jsx(
7615
+ PortfolioBalanceCard,
7616
+ {
7617
+ totalUsd: portfolio.totalUsd,
7618
+ totalIdleUsd: portfolio.totalIdleUsd,
7619
+ idleBalances: portfolio.idleBalances,
7620
+ isPositionsExpanded,
7621
+ onTogglePositions: () => setIsPositionsExpanded((prev) => !prev),
7622
+ positionCount: portfolio.positions.length,
7623
+ showTopUp,
7624
+ onTopUp: () => earnBalanceRef.current?.openTransferModal()
7625
+ }
7626
+ ),
7627
+ isPositionsExpanded && /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
7628
+ AllocationEditor,
7629
+ {
7630
+ portfolio,
7631
+ targets,
7632
+ targetSum,
7633
+ hasChanges,
7634
+ highlightedVenueAddress: void 0,
7635
+ onUpdatePercent: handleUpdatePercent,
7636
+ onRemoveVenue: handleRemoveVenue,
7637
+ onResetToCurrent: handleResetToCurrent,
7638
+ onEqualSplit: handleEqualSplit
7639
+ }
7640
+ ) }),
7641
+ !isPositionsExpanded && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7642
+ /* @__PURE__ */ jsxRuntime.jsx(
7643
+ MarketSelector,
7644
+ {
7645
+ markets: earnAccountMarkets,
7646
+ selectedMarket,
7647
+ onMarketSelect: (market) => {
7648
+ setSelectedMarket(market);
7649
+ setDepositError(null);
7650
+ },
7651
+ marketTab,
7652
+ onMarketTabChange: (tab) => {
7653
+ setMarketTab(tab);
7654
+ setSelectedMarket(null);
7655
+ setDepositError(null);
7656
+ },
7657
+ isLoading: isMarketsLoading,
7658
+ allowedMarketIds: marketTab === "variable" ? allowedVariableMarketIds : allowedFixedMarketIds
7659
+ }
7660
+ ),
7661
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.75)" }, children: [
7451
7662
  /* @__PURE__ */ jsxRuntime.jsxs(
7452
- "p",
7663
+ "div",
7453
7664
  {
7454
- className: "font-semibold",
7665
+ className: "flex items-center flex-wrap",
7455
7666
  style: {
7456
- color: isPnlPositive ? "var(--compass-color-success)" : "var(--compass-color-error)"
7667
+ backgroundColor: "var(--compass-color-surface)",
7668
+ border: "1px solid var(--compass-color-border)",
7669
+ borderRadius: "var(--compass-border-radius-lg)",
7670
+ padding: "calc(var(--compass-spacing-unit) * 0.75)",
7671
+ gap: "calc(var(--compass-spacing-unit) * 0.5)"
7457
7672
  },
7458
7673
  children: [
7459
- isPnlPositive ? "+" : "",
7460
- formatUSD(position.pnl.totalPnl)
7461
- ]
7462
- }
7463
- )
7464
- ]
7465
- }
7466
- )
7467
- ] }),
7468
- (position.deposits.length > 0 || position.withdrawals.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
7469
- /* @__PURE__ */ jsxRuntime.jsx(
7470
- "h3",
7471
- {
7472
- className: "font-semibold mb-3",
7473
- style: { color: "var(--compass-color-text)" },
7474
- children: "Transaction History"
7475
- }
7476
- ),
7477
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
7478
- position.deposits.map((tx, i) => {
7479
- const date = formatDate(tx.timestamp);
7480
- return /* @__PURE__ */ jsxRuntime.jsxs(
7481
- "div",
7482
- {
7483
- className: "flex items-center justify-between p-3 rounded-lg",
7484
- style: { backgroundColor: "var(--compass-color-surface)" },
7485
- children: [
7486
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
7487
- /* @__PURE__ */ jsxRuntime.jsxs(
7488
- "p",
7674
+ /* @__PURE__ */ jsxRuntime.jsx(
7675
+ "input",
7489
7676
  {
7490
- className: "font-medium",
7491
- style: { color: "var(--compass-color-success)" },
7492
- children: [
7493
- "+",
7494
- formatAmount(tx.amount),
7495
- " ",
7496
- position.assetSymbol
7497
- ]
7677
+ type: "number",
7678
+ value: depositAmount,
7679
+ onChange: (e) => {
7680
+ setDepositAmount(e.target.value);
7681
+ setDepositError(null);
7682
+ },
7683
+ placeholder: "0.00",
7684
+ disabled: isProcessing,
7685
+ className: "flex-1 font-medium bg-transparent outline-none",
7686
+ style: { color: "var(--compass-color-text)", fontSize: "1.25rem", minWidth: 0 }
7498
7687
  }
7499
7688
  ),
7689
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", style: { flexShrink: 0 }, children: [
7690
+ /* @__PURE__ */ jsxRuntime.jsxs(
7691
+ "button",
7692
+ {
7693
+ onClick: () => setIsTokenDropdownOpen(!isTokenDropdownOpen),
7694
+ disabled: isProcessing,
7695
+ className: "flex items-center font-medium",
7696
+ style: {
7697
+ backgroundColor: "var(--compass-color-surface-elevated, var(--compass-color-background))",
7698
+ border: "1px solid var(--compass-color-border)",
7699
+ color: "var(--compass-color-text)",
7700
+ borderRadius: "var(--compass-border-radius-md)",
7701
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 0.75)",
7702
+ gap: "calc(var(--compass-spacing-unit) * 0.25)",
7703
+ fontSize: "0.875rem"
7704
+ },
7705
+ children: [
7706
+ selectedToken,
7707
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
7708
+ ]
7709
+ }
7710
+ ),
7711
+ isTokenDropdownOpen && /* @__PURE__ */ jsxRuntime.jsx(
7712
+ "div",
7713
+ {
7714
+ className: "absolute right-0 mt-1 z-10",
7715
+ style: {
7716
+ backgroundColor: "var(--compass-color-surface)",
7717
+ border: "1px solid var(--compass-color-border)",
7718
+ borderRadius: "var(--compass-border-radius-lg)",
7719
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
7720
+ minWidth: "100px"
7721
+ },
7722
+ children: SUPPORTED_TOKENS2.map((token) => /* @__PURE__ */ jsxRuntime.jsx(
7723
+ "button",
7724
+ {
7725
+ onClick: () => {
7726
+ setSelectedToken(token);
7727
+ setIsTokenDropdownOpen(false);
7728
+ setDepositError(null);
7729
+ },
7730
+ className: "w-full text-left font-medium transition-colors",
7731
+ style: {
7732
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 0.75)",
7733
+ color: token === selectedToken ? "var(--compass-color-primary)" : "var(--compass-color-text)",
7734
+ backgroundColor: token === selectedToken ? "var(--compass-color-primary-muted, rgba(99, 102, 241, 0.1))" : "transparent",
7735
+ fontSize: "0.875rem"
7736
+ },
7737
+ children: token
7738
+ },
7739
+ token
7740
+ ))
7741
+ }
7742
+ )
7743
+ ] }),
7500
7744
  /* @__PURE__ */ jsxRuntime.jsx(
7501
- "p",
7745
+ "button",
7502
7746
  {
7503
- className: "text-xs",
7504
- style: { color: "var(--compass-color-text-tertiary)" },
7505
- children: date ? `Deposit - ${date}` : "Deposit"
7747
+ onClick: () => setDepositAmount(earnBalancesQuery.toString()),
7748
+ disabled: isProcessing,
7749
+ className: "text-xs font-medium uppercase tracking-wide",
7750
+ style: {
7751
+ backgroundColor: "var(--compass-color-surface-elevated, var(--compass-color-background))",
7752
+ border: "1px solid var(--compass-color-border)",
7753
+ color: "var(--compass-color-text-secondary)",
7754
+ borderRadius: "var(--compass-border-radius-md)",
7755
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 0.75)",
7756
+ flexShrink: 0
7757
+ },
7758
+ children: "MAX"
7506
7759
  }
7507
7760
  )
7508
- ] }),
7509
- tx.txHash && /* @__PURE__ */ jsxRuntime.jsx(
7510
- "a",
7511
- {
7512
- href: `https://etherscan.io/tx/${tx.txHash}`,
7513
- target: "_blank",
7514
- rel: "noopener noreferrer",
7515
- className: "p-2 rounded-lg hover:opacity-80 transition-opacity",
7516
- style: { backgroundColor: "var(--compass-color-background)" },
7517
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
7518
- }
7519
- )
7520
- ]
7521
- },
7522
- `deposit-${i}`
7523
- );
7524
- }),
7525
- position.withdrawals.map((tx, i) => {
7526
- const date = formatDate(tx.timestamp);
7527
- return /* @__PURE__ */ jsxRuntime.jsxs(
7761
+ ]
7762
+ }
7763
+ ),
7764
+ needsSwap && /* @__PURE__ */ jsxRuntime.jsxs(
7765
+ "div",
7766
+ {
7767
+ className: "flex items-center text-sm",
7768
+ style: {
7769
+ backgroundColor: "var(--compass-color-primary-muted, rgba(99, 102, 241, 0.1))",
7770
+ color: "var(--compass-color-primary)",
7771
+ borderRadius: "var(--compass-border-radius-lg)",
7772
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 0.75)",
7773
+ gap: "calc(var(--compass-spacing-unit) * 0.5)"
7774
+ },
7775
+ children: [
7776
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { size: 14 }),
7777
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
7778
+ "Swaps ",
7779
+ selectedToken,
7780
+ " to ",
7781
+ selectedMarket?.underlyingToken,
7782
+ ", then deposits"
7783
+ ] })
7784
+ ]
7785
+ }
7786
+ ),
7787
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", style: { padding: "0 calc(var(--compass-spacing-unit) * 0.25)" }, children: [
7788
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm", style: { color: "var(--compass-color-text-tertiary)" }, children: [
7789
+ "Available ",
7790
+ selectedToken
7791
+ ] }),
7792
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium", style: { color: "var(--compass-color-text-secondary)" }, children: [
7793
+ formatAmount3(earnBalancesQuery),
7794
+ " ",
7795
+ selectedToken
7796
+ ] })
7797
+ ] })
7798
+ ] }),
7799
+ depositError && /* @__PURE__ */ jsxRuntime.jsx(
7528
7800
  "div",
7529
7801
  {
7530
- className: "flex items-center justify-between p-3 rounded-lg",
7531
- style: { backgroundColor: "var(--compass-color-surface)" },
7802
+ className: "text-sm",
7803
+ style: {
7804
+ backgroundColor: "var(--compass-color-error-muted)",
7805
+ color: "var(--compass-color-error)",
7806
+ borderRadius: "var(--compass-border-radius-lg)",
7807
+ padding: "calc(var(--compass-spacing-unit) * 0.75)"
7808
+ },
7809
+ children: depositError
7810
+ }
7811
+ ),
7812
+ /* @__PURE__ */ jsxRuntime.jsx(
7813
+ "button",
7814
+ {
7815
+ onClick: !isConnected && login ? login : handleDeposit,
7816
+ disabled: isConnected && (isProcessing || !selectedMarket || !depositAmount || parseFloat(depositAmount) <= 0 || parseFloat(depositAmount) > earnBalancesQuery),
7817
+ className: "w-full font-semibold transition-all disabled:opacity-50 disabled:cursor-not-allowed",
7818
+ style: {
7819
+ backgroundColor: "var(--compass-color-primary)",
7820
+ color: "var(--compass-color-primary-text, white)",
7821
+ borderRadius: "var(--compass-border-radius-lg)",
7822
+ padding: "calc(var(--compass-spacing-unit) * 0.75)",
7823
+ fontSize: "0.875rem",
7824
+ transition: "var(--compass-transition-normal)"
7825
+ },
7826
+ children: !isConnected ? "Connect Wallet" : isProcessing ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center justify-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
7827
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 16, className: "animate-spin" }),
7828
+ "Processing..."
7829
+ ] }) : !selectedMarket ? "Select a market" : needsSwap ? "Swap & Deposit" : "Deposit"
7830
+ }
7831
+ ),
7832
+ depositStatus && !depositError && /* @__PURE__ */ jsxRuntime.jsx(
7833
+ "div",
7834
+ {
7835
+ className: "text-sm text-center",
7836
+ style: {
7837
+ backgroundColor: depositStatus.includes("successful") ? "var(--compass-color-success-muted)" : "var(--compass-color-primary-muted, rgba(99, 102, 241, 0.1))",
7838
+ color: depositStatus.includes("successful") ? "var(--compass-color-success)" : "var(--compass-color-primary)",
7839
+ borderRadius: "var(--compass-border-radius-lg)",
7840
+ padding: "calc(var(--compass-spacing-unit) * 0.75)"
7841
+ },
7842
+ children: depositStatus
7843
+ }
7844
+ )
7845
+ ] }),
7846
+ clientPreview && clientPreview.actions.length > 0 && state === "editing" && /* @__PURE__ */ jsxRuntime.jsxs(
7847
+ "div",
7848
+ {
7849
+ className: "p-4",
7850
+ style: {
7851
+ backgroundColor: "var(--compass-color-surface)",
7852
+ borderRadius: "var(--compass-border-radius-xl)",
7853
+ border: "1px solid var(--compass-color-border)"
7854
+ },
7855
+ children: [
7856
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold mb-3", style: { color: "var(--compass-color-text)" }, children: "Preview" }),
7857
+ /* @__PURE__ */ jsxRuntime.jsx(ActionList, { actions: clientPreview.actions }),
7858
+ 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: [
7859
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { size: 12, className: "flex-shrink-0 mt-0.5" }),
7860
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: w })
7861
+ ] }, i)) }),
7862
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs mt-2", style: { color: "var(--compass-color-text-tertiary)" }, children: [
7863
+ "Estimated gas savings: ",
7864
+ clientPreview.estimatedGasSavings
7865
+ ] })
7866
+ ]
7867
+ }
7868
+ ),
7869
+ state === "ready" && serverPreview && /* @__PURE__ */ jsxRuntime.jsxs(
7870
+ "div",
7871
+ {
7872
+ className: "p-4",
7873
+ style: {
7874
+ backgroundColor: "var(--compass-color-surface)",
7875
+ borderRadius: "var(--compass-border-radius-xl)",
7876
+ border: "1px solid var(--compass-color-primary)"
7877
+ },
7878
+ children: [
7879
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold mb-3", style: { color: "var(--compass-color-text)" }, children: "Ready to Execute" }),
7880
+ /* @__PURE__ */ jsxRuntime.jsx(ActionList, { actions: serverPreview.actions || [] }),
7881
+ (serverPreview.warnings || []).length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 flex flex-col gap-1", children: serverPreview.warnings.map((w, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 text-xs", style: { color: "var(--compass-color-warning, #eab308)" }, children: [
7882
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { size: 12, className: "flex-shrink-0 mt-0.5" }),
7883
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: w })
7884
+ ] }, i)) }),
7885
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs mt-2", style: { color: "var(--compass-color-text-tertiary)" }, children: [
7886
+ serverPreview.actionsCount,
7887
+ " actions in 1 atomic transaction"
7888
+ ] })
7889
+ ]
7890
+ }
7891
+ ),
7892
+ state === "success" && txHash && /* @__PURE__ */ jsxRuntime.jsxs(
7893
+ "div",
7894
+ {
7895
+ className: "p-4 text-center",
7896
+ style: {
7897
+ backgroundColor: "var(--compass-color-success-muted, rgba(34, 197, 94, 0.1))",
7898
+ borderRadius: "var(--compass-border-radius-xl)"
7899
+ },
7900
+ children: [
7901
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: 24, className: "mx-auto mb-2", style: { color: "var(--compass-color-success)" } }),
7902
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold mb-1", style: { color: "var(--compass-color-success)" }, children: "Rebalance Complete" }),
7903
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-mono break-all", style: { color: "var(--compass-color-text-secondary)" }, children: txHash })
7904
+ ]
7905
+ }
7906
+ ),
7907
+ state === "error" && errorMessage && /* @__PURE__ */ jsxRuntime.jsxs(
7908
+ "div",
7909
+ {
7910
+ className: "p-4",
7911
+ style: {
7912
+ backgroundColor: "var(--compass-color-error-muted, rgba(239, 68, 68, 0.1))",
7913
+ borderRadius: "var(--compass-border-radius-xl)"
7914
+ },
7915
+ children: [
7916
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium mb-1", style: { color: "var(--compass-color-error)" }, children: errorMessage }),
7917
+ /* @__PURE__ */ jsxRuntime.jsx(
7918
+ "button",
7919
+ {
7920
+ onClick: () => {
7921
+ setWidgetState("editing");
7922
+ setErrorMessage(null);
7923
+ },
7924
+ className: "text-xs underline",
7925
+ style: { color: "var(--compass-color-error)" },
7926
+ children: "Try again"
7927
+ }
7928
+ )
7929
+ ]
7930
+ }
7931
+ ),
7932
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
7933
+ state === "editing" && hasChanges && /* @__PURE__ */ jsxRuntime.jsx(
7934
+ "button",
7935
+ {
7936
+ onClick: handlePreview,
7937
+ disabled: !address,
7938
+ className: "w-full py-3 rounded-xl font-semibold text-base transition-all disabled:opacity-50 disabled:cursor-not-allowed",
7939
+ style: {
7940
+ backgroundColor: "var(--compass-color-primary)",
7941
+ color: "white"
7942
+ },
7943
+ children: "Preview Rebalance"
7944
+ }
7945
+ ),
7946
+ state === "previewing" && /* @__PURE__ */ jsxRuntime.jsxs(
7947
+ "button",
7948
+ {
7949
+ disabled: true,
7950
+ className: "w-full py-3 rounded-xl font-semibold text-base flex items-center justify-center gap-2",
7951
+ style: {
7952
+ backgroundColor: "var(--compass-color-surface-hover, var(--compass-color-surface))",
7953
+ color: "var(--compass-color-text-secondary)"
7954
+ },
7955
+ children: [
7956
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 18, className: "animate-spin" }),
7957
+ "Computing exact amounts..."
7958
+ ]
7959
+ }
7960
+ ),
7961
+ state === "ready" && /* @__PURE__ */ jsxRuntime.jsx(
7962
+ "button",
7963
+ {
7964
+ onClick: handleExecute,
7965
+ className: "w-full py-3 rounded-xl font-semibold text-base transition-all",
7966
+ style: {
7967
+ backgroundColor: "var(--compass-color-primary)",
7968
+ color: "white"
7969
+ },
7970
+ children: "Confirm & Sign"
7971
+ }
7972
+ ),
7973
+ state === "signing" && /* @__PURE__ */ jsxRuntime.jsxs(
7974
+ "button",
7975
+ {
7976
+ disabled: true,
7977
+ className: "w-full py-3 rounded-xl font-semibold text-base flex items-center justify-center gap-2",
7978
+ style: {
7979
+ backgroundColor: "var(--compass-color-surface-hover, var(--compass-color-surface))",
7980
+ color: "var(--compass-color-text-secondary)"
7981
+ },
7982
+ children: [
7983
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 18, className: "animate-spin" }),
7984
+ "Waiting for signature..."
7985
+ ]
7986
+ }
7987
+ ),
7988
+ state === "executing" && /* @__PURE__ */ jsxRuntime.jsxs(
7989
+ "button",
7990
+ {
7991
+ disabled: true,
7992
+ className: "w-full py-3 rounded-xl font-semibold text-base flex items-center justify-center gap-2",
7993
+ style: {
7994
+ backgroundColor: "var(--compass-color-surface-hover, var(--compass-color-surface))",
7995
+ color: "var(--compass-color-text-secondary)"
7996
+ },
7532
7997
  children: [
7533
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
7534
- /* @__PURE__ */ jsxRuntime.jsxs(
7535
- "p",
7536
- {
7537
- className: "font-medium",
7538
- style: { color: "var(--compass-color-error)" },
7539
- children: [
7540
- "-",
7541
- formatAmount(tx.amount),
7542
- " ",
7543
- position.assetSymbol
7544
- ]
7545
- }
7546
- ),
7547
- /* @__PURE__ */ jsxRuntime.jsx(
7548
- "p",
7549
- {
7550
- className: "text-xs",
7551
- style: { color: "var(--compass-color-text-tertiary)" },
7552
- children: date ? `Withdrawal - ${date}` : "Withdrawal"
7553
- }
7554
- )
7555
- ] }),
7556
- tx.txHash && /* @__PURE__ */ jsxRuntime.jsx(
7557
- "a",
7558
- {
7559
- href: `https://etherscan.io/tx/${tx.txHash}`,
7560
- target: "_blank",
7561
- rel: "noopener noreferrer",
7562
- className: "p-2 rounded-lg hover:opacity-80 transition-opacity",
7563
- style: { backgroundColor: "var(--compass-color-background)" },
7564
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
7565
- }
7566
- )
7998
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 18, className: "animate-spin" }),
7999
+ "Executing on-chain..."
7567
8000
  ]
8001
+ }
8002
+ ),
8003
+ state === "success" && /* @__PURE__ */ jsxRuntime.jsx(
8004
+ "button",
8005
+ {
8006
+ onClick: () => {
8007
+ setWidgetState("editing");
8008
+ setTxHash(null);
8009
+ setServerPreview(null);
8010
+ setPreviewPlan(null);
8011
+ },
8012
+ className: "w-full py-3 rounded-xl font-semibold text-base transition-all",
8013
+ style: {
8014
+ backgroundColor: "var(--compass-color-primary)",
8015
+ color: "white"
8016
+ },
8017
+ children: "Rebalance Again"
8018
+ }
8019
+ ),
8020
+ (state === "ready" || state === "previewing") && /* @__PURE__ */ jsxRuntime.jsx(
8021
+ "button",
8022
+ {
8023
+ onClick: () => {
8024
+ setWidgetState("editing");
8025
+ setServerPreview(null);
8026
+ },
8027
+ className: "w-full py-2 rounded-xl text-sm transition-all",
8028
+ style: {
8029
+ backgroundColor: "transparent",
8030
+ color: "var(--compass-color-text-secondary)"
8031
+ },
8032
+ children: "Back to editing"
8033
+ }
8034
+ )
8035
+ ] }),
8036
+ /* @__PURE__ */ jsxRuntime.jsxs(
8037
+ "div",
8038
+ {
8039
+ className: "flex items-center justify-center",
8040
+ style: {
8041
+ gap: "calc(var(--compass-spacing-unit) * 1)",
8042
+ paddingTop: "calc(var(--compass-spacing-unit) * 0.5)"
7568
8043
  },
7569
- `withdraw-${i}`
7570
- );
7571
- })
8044
+ children: [
8045
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
8046
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Shield, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
8047
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", style: { color: "var(--compass-color-text-tertiary)" }, children: "Non-custodial" })
8048
+ ] }),
8049
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-border)" }, children: "|" }),
8050
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
8051
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } }),
8052
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", style: { color: "var(--compass-color-text-tertiary)" }, children: "Audited protocols" })
8053
+ ] })
8054
+ ]
8055
+ }
8056
+ )
7572
8057
  ] })
7573
- ] })
7574
- ]
7575
- }
7576
- )
8058
+ ]
8059
+ }
8060
+ ) }),
8061
+ /* @__PURE__ */ jsxRuntime.jsx(EarnAccountBalance, { ref: earnBalanceRef, compact: true, hideVisual: true, onTransferComplete: () => refetch() })
8062
+ ]
7577
8063
  }
7578
8064
  );
7579
8065
  }
7580
- function EarnPositionsList({
7581
- showApy = true,
7582
- showPnL = true,
7583
- defaultCollapsed = [],
7584
- onPositionSelect
7585
- }) {
7586
- const { isConnected } = useEmbeddableWallet();
7587
- const { venuePositions, totalPositions, totalBalanceUsd, isLoading, isError } = usePositionsData();
7588
- const [selectedPosition, setSelectedPosition] = react.useState(null);
7589
- const handlePositionClick = (position) => {
7590
- setSelectedPosition(position);
7591
- onPositionSelect?.(position);
8066
+ function ActionList({ actions }) {
8067
+ const actionIcons = {
8068
+ withdraw: "Withdraw",
8069
+ swap: "Swap",
8070
+ deposit: "Deposit"
7592
8071
  };
7593
- const renderContent = () => {
7594
- if (!isConnected) {
7595
- return /* @__PURE__ */ jsxRuntime.jsxs(
7596
- "div",
7597
- {
7598
- className: "flex flex-col items-center justify-center py-12 text-center",
7599
- style: { color: "var(--compass-color-text-secondary)" },
7600
- children: [
7601
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Inbox, { size: 48, className: "mb-4 opacity-50" }),
7602
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "Connect your wallet" }),
7603
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mt-1", children: "to view your earn positions" })
7604
- ]
7605
- }
7606
- );
7607
- }
7608
- if (isLoading) {
7609
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(
7610
- lucideReact.Loader2,
7611
- {
7612
- size: 32,
7613
- className: "animate-spin",
7614
- style: { color: "var(--compass-color-primary)" }
7615
- }
7616
- ) });
7617
- }
7618
- if (isError) {
7619
- return /* @__PURE__ */ jsxRuntime.jsxs(
7620
- "div",
7621
- {
7622
- className: "flex flex-col items-center justify-center py-12 text-center",
7623
- style: { color: "var(--compass-color-error)" },
7624
- children: [
7625
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "Failed to load positions" }),
7626
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mt-1", children: "Please try again later" })
7627
- ]
7628
- }
7629
- );
7630
- }
7631
- if (totalPositions === 0) {
7632
- return /* @__PURE__ */ jsxRuntime.jsxs(
7633
- "div",
7634
- {
7635
- className: "flex flex-col items-center justify-center py-12 text-center",
7636
- style: { color: "var(--compass-color-text-secondary)" },
7637
- children: [
7638
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Inbox, { size: 48, className: "mb-4 opacity-50" }),
7639
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "No positions yet" }),
7640
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mt-1", children: "Deposit to a vault or market to get started" })
7641
- ]
7642
- }
7643
- );
7644
- }
7645
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7646
- /* @__PURE__ */ jsxRuntime.jsxs(
7647
- "div",
7648
- {
7649
- className: "flex items-center justify-between p-4 rounded-xl",
7650
- style: { backgroundColor: "var(--compass-color-surface)" },
7651
- children: [
7652
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
7653
- /* @__PURE__ */ jsxRuntime.jsx(
7654
- "h2",
7655
- {
7656
- className: "font-bold text-lg",
7657
- style: { color: "var(--compass-color-text)" },
7658
- children: "Your Positions"
7659
- }
7660
- ),
7661
- /* @__PURE__ */ jsxRuntime.jsxs(
7662
- "p",
7663
- {
7664
- className: "text-sm",
7665
- style: { color: "var(--compass-color-text-secondary)" },
7666
- children: [
7667
- totalPositions,
7668
- " position",
7669
- totalPositions !== 1 ? "s" : "",
7670
- " across ",
7671
- venuePositions.length,
7672
- " venue",
7673
- venuePositions.length !== 1 ? "s" : ""
7674
- ]
7675
- }
7676
- )
7677
- ] }),
7678
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
7679
- /* @__PURE__ */ jsxRuntime.jsx(
7680
- "p",
7681
- {
7682
- className: "text-sm",
7683
- style: { color: "var(--compass-color-text-secondary)" },
7684
- children: "Total Value"
7685
- }
7686
- ),
7687
- /* @__PURE__ */ jsxRuntime.jsx(
7688
- "p",
7689
- {
7690
- className: "font-bold text-xl",
7691
- style: { color: "var(--compass-color-text)" },
7692
- children: formatUSD(totalBalanceUsd)
7693
- }
7694
- )
7695
- ] })
7696
- ]
7697
- }
7698
- ),
7699
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: venuePositions.map((vp) => /* @__PURE__ */ jsxRuntime.jsx(
7700
- VenueSection,
7701
- {
7702
- venuePositions: vp,
7703
- defaultCollapsed: defaultCollapsed.includes(vp.venue),
7704
- showApy,
7705
- showPnL,
7706
- onPositionSelect: handlePositionClick
7707
- },
7708
- vp.venue
7709
- )) })
7710
- ] });
8072
+ const actionColors = {
8073
+ withdraw: "var(--compass-color-error)",
8074
+ swap: "var(--compass-color-warning, #eab308)",
8075
+ deposit: "var(--compass-color-success)"
7711
8076
  };
7712
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7713
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
7714
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
7715
- /* @__PURE__ */ jsxRuntime.jsx(ChainSwitcher, {}),
8077
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1.5", children: actions.map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
8078
+ "div",
8079
+ {
8080
+ className: "flex items-center justify-between p-2 rounded-lg text-sm",
8081
+ style: { backgroundColor: "var(--compass-color-background)" },
8082
+ children: [
7716
8083
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
7717
- /* @__PURE__ */ jsxRuntime.jsx(EarnAccountBalance, { compact: true }),
7718
- /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
7719
- ] })
7720
- ] }),
7721
- renderContent()
7722
- ] }),
7723
- selectedPosition && /* @__PURE__ */ jsxRuntime.jsx(
7724
- PositionDetailModal,
7725
- {
7726
- position: selectedPosition,
7727
- onClose: () => setSelectedPosition(null)
7728
- }
7729
- )
7730
- ] });
8084
+ /* @__PURE__ */ jsxRuntime.jsx(
8085
+ "span",
8086
+ {
8087
+ className: "text-xs font-medium px-1.5 py-0.5 rounded",
8088
+ style: {
8089
+ backgroundColor: actionColors[action.type] + "20",
8090
+ color: actionColors[action.type]
8091
+ },
8092
+ children: actionIcons[action.type] || action.type
8093
+ }
8094
+ ),
8095
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "var(--compass-color-text)" }, children: [
8096
+ action.token,
8097
+ action.tokenOut && ` \u2192 ${action.tokenOut}`
8098
+ ] })
8099
+ ] }),
8100
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-xs", style: { color: "var(--compass-color-text-secondary)" }, children: formatUSD(action.usdValue || 0) })
8101
+ ]
8102
+ },
8103
+ i
8104
+ )) });
7731
8105
  }
7732
8106
 
7733
8107
  // src/components/CompassEarnWidget/presets.ts
7734
8108
  var allTabs = [
7735
- { id: "vaults", label: "Vaults", enabled: true },
7736
- { id: "aave", label: "Aave", enabled: true },
7737
- { id: "pendle", label: "Pendle", enabled: true },
7738
8109
  { id: "swap", label: "Swap", enabled: true },
8110
+ { id: "rebalance", label: "Rebalance", enabled: true },
7739
8111
  // TODO: Positions tab temporarily disabled - needs more work on API response handling
7740
8112
  { id: "positions", label: "Positions", enabled: false }
7741
8113
  ];
@@ -7743,59 +8115,39 @@ function getTabsForPreset(preset) {
7743
8115
  switch (preset) {
7744
8116
  case "full":
7745
8117
  return allTabs;
7746
- case "earn-only":
7747
- return allTabs.map((tab) => ({
7748
- ...tab,
7749
- enabled: tab.id !== "swap"
7750
- }));
7751
8118
  case "swap-only":
7752
8119
  return allTabs.map((tab) => ({
7753
8120
  ...tab,
7754
8121
  enabled: tab.id === "swap"
7755
8122
  }));
7756
- case "vaults-only":
7757
- return allTabs.map((tab) => ({
7758
- ...tab,
7759
- enabled: tab.id === "vaults"
7760
- }));
7761
8123
  default:
7762
8124
  return allTabs;
7763
8125
  }
7764
8126
  }
7765
8127
  function getDefaultTab(tabs) {
7766
8128
  const enabledTab = tabs.find((t) => t.enabled);
7767
- return enabledTab?.id || "vaults";
8129
+ return enabledTab?.id || "swap";
7768
8130
  }
7769
8131
  function CompassEarnWidget({
7770
8132
  preset = "full",
7771
- enableVaults,
7772
- enableAave,
7773
- enablePendle,
7774
8133
  enableSwap,
7775
8134
  enablePositions,
8135
+ enableRebalance,
7776
8136
  defaultTab,
7777
8137
  showHeader = true,
7778
- showApy = true,
7779
- showTvl = true,
7780
- showExpiry = true,
7781
- showUserPosition = true,
7782
8138
  showPnL = true,
7783
- showSearch = true,
7784
- showSort = true,
7785
8139
  onTabChange
7786
8140
  }) {
7787
8141
  const tabs = react.useMemo(() => {
7788
8142
  const baseTabs = getTabsForPreset(preset);
7789
8143
  return baseTabs.map((tab) => {
7790
8144
  let enabled = tab.enabled;
7791
- if (tab.id === "vaults" && enableVaults !== void 0) enabled = enableVaults;
7792
- if (tab.id === "aave" && enableAave !== void 0) enabled = enableAave;
7793
- if (tab.id === "pendle" && enablePendle !== void 0) enabled = enablePendle;
7794
8145
  if (tab.id === "swap" && enableSwap !== void 0) enabled = enableSwap;
7795
8146
  if (tab.id === "positions" && enablePositions !== void 0) enabled = enablePositions;
8147
+ if (tab.id === "rebalance" && enableRebalance !== void 0) enabled = enableRebalance;
7796
8148
  return { ...tab, enabled };
7797
8149
  });
7798
- }, [preset, enableVaults, enableAave, enablePendle, enableSwap, enablePositions]);
8150
+ }, [preset, enableSwap, enablePositions, enableRebalance]);
7799
8151
  const enabledTabs = tabs.filter((t) => t.enabled);
7800
8152
  const initialTab = defaultTab && tabs.find((t) => t.id === defaultTab)?.enabled ? defaultTab : getDefaultTab(tabs);
7801
8153
  const [activeTab, setActiveTab] = react.useState(initialTab);
@@ -7836,35 +8188,8 @@ function CompassEarnWidget({
7836
8188
  }
7837
8189
  ),
7838
8190
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
7839
- activeTab === "vaults" && /* @__PURE__ */ jsxRuntime.jsx(
7840
- VaultsList,
7841
- {
7842
- showApy,
7843
- showTvl,
7844
- showUserPosition,
7845
- showSearch,
7846
- showSort
7847
- }
7848
- ),
7849
- activeTab === "aave" && /* @__PURE__ */ jsxRuntime.jsx(
7850
- AaveMarketsList,
7851
- {
7852
- showApy,
7853
- showUserPosition,
7854
- showSearch
7855
- }
7856
- ),
7857
- activeTab === "pendle" && /* @__PURE__ */ jsxRuntime.jsx(
7858
- PendleMarketsList,
7859
- {
7860
- showApy,
7861
- showExpiry,
7862
- showUserPosition,
7863
- showSearch,
7864
- showSort
7865
- }
7866
- ),
7867
8191
  activeTab === "swap" && /* @__PURE__ */ jsxRuntime.jsx(SwapWidget, {}),
8192
+ activeTab === "rebalance" && /* @__PURE__ */ jsxRuntime.jsx(RebalancingWidget, {}),
7868
8193
  activeTab === "positions" && /* @__PURE__ */ jsxRuntime.jsx(
7869
8194
  EarnPositionsList,
7870
8195
  {
@@ -7895,7 +8220,6 @@ var CHAINS = {
7895
8220
  }
7896
8221
  };
7897
8222
 
7898
- exports.AaveMarketsList = AaveMarketsList;
7899
8223
  exports.AccountBalancesModal = AccountBalancesModal;
7900
8224
  exports.ActionModal = ActionModal;
7901
8225
  exports.ApiProvider = ApiProvider;
@@ -7908,15 +8232,13 @@ exports.DepositWithdrawForm = DepositWithdrawForm;
7908
8232
  exports.EarnAccount = EarnAccount;
7909
8233
  exports.EarnAccountBalance = EarnAccountBalance;
7910
8234
  exports.EarnAccountGuard = EarnAccountGuard;
7911
- exports.PendleMarketsList = PendleMarketsList;
7912
8235
  exports.PnLSummary = PnLSummary;
8236
+ exports.RebalancingWidget = RebalancingWidget;
7913
8237
  exports.SwapWidget = SwapWidget;
7914
8238
  exports.ThemeProvider = ThemeProvider;
7915
8239
  exports.TransactionHistory = TransactionHistory;
7916
- exports.VaultsList = VaultsList;
7917
8240
  exports.WalletStatus = WalletStatus;
7918
8241
  exports.themePresets = themePresets;
7919
- exports.useAaveData = useAaveData;
7920
8242
  exports.useChain = useChain;
7921
8243
  exports.useCompassApi = useCompassApi;
7922
8244
  exports.useCompassChain = useCompassChain;
@@ -7924,9 +8246,8 @@ exports.useCompassWallet = useCompassWallet;
7924
8246
  exports.useEarnAccount = useEarnAccount;
7925
8247
  exports.useEmbeddableApi = useEmbeddableApi;
7926
8248
  exports.useEmbeddableWallet = useEmbeddableWallet;
7927
- exports.usePendleData = usePendleData;
8249
+ exports.useRebalancingData = useRebalancingData;
7928
8250
  exports.useSwapQuote = useSwapQuote;
7929
8251
  exports.useTheme = useTheme;
7930
- exports.useVaultsData = useVaultsData;
7931
8252
  //# sourceMappingURL=index.js.map
7932
8253
  //# sourceMappingURL=index.js.map