@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.d.mts +122 -329
- package/dist/index.d.ts +122 -329
- package/dist/index.js +2447 -2126
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2450 -2125
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.js +304 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +304 -0
- package/dist/server/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
"
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
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
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
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
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
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
|
-
"
|
|
3003
|
+
"div",
|
|
2987
3004
|
{
|
|
2988
|
-
onClick,
|
|
2989
|
-
className: "w-full border text-left hover:scale-[1.01]",
|
|
2990
3005
|
style: {
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
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", {
|
|
3000
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
{
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
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
|
-
|
|
3028
|
-
style: {
|
|
3029
|
-
|
|
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
|
-
"
|
|
3071
|
+
"button",
|
|
3035
3072
|
{
|
|
3036
|
-
|
|
3037
|
-
|
|
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
|
-
|
|
3040
|
-
|
|
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
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
"
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
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
|
-
|
|
3087
|
-
|
|
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
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
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
|
|
3104
|
-
const
|
|
3105
|
-
const
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
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(
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
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
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
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
|
-
"
|
|
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
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
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
|
-
|
|
3298
|
-
"
|
|
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
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
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.
|
|
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
|
-
"
|
|
3288
|
+
"div",
|
|
3678
3289
|
{
|
|
3679
|
-
className: "font-semibold",
|
|
3680
3290
|
style: {
|
|
3681
|
-
fontSize: "var(--compass-font-size-
|
|
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:
|
|
3295
|
+
children: "Amount"
|
|
3685
3296
|
}
|
|
3686
3297
|
),
|
|
3687
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
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
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
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
|
-
"
|
|
3311
|
+
"div",
|
|
3720
3312
|
{
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
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.
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
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
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
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:
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
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
|
-
|
|
3996
|
-
|
|
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
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
6022
|
-
const { address } = useEmbeddableWallet();
|
|
5195
|
+
function useSwapQuote({ fromToken, toToken, amount, enabled = true }) {
|
|
6023
5196
|
const { chainId } = useChain();
|
|
6024
|
-
const {
|
|
6025
|
-
const
|
|
6026
|
-
queryKey: ["
|
|
5197
|
+
const { address } = useCompassWallet();
|
|
5198
|
+
const query = reactQuery.useQuery({
|
|
5199
|
+
queryKey: ["swapQuote", chainId, fromToken, toToken, amount, address],
|
|
6027
5200
|
queryFn: async () => {
|
|
6028
|
-
|
|
6029
|
-
|
|
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
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
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:
|
|
6111
|
-
|
|
6112
|
-
|
|
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
|
-
|
|
6120
|
-
isLoading:
|
|
6121
|
-
isError:
|
|
6122
|
-
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
|
-
|
|
7581
|
+
lucideReact.Loader2,
|
|
7411
7582
|
{
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
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
|
-
|
|
7592
|
+
state === "empty" && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7421
7593
|
"div",
|
|
7422
7594
|
{
|
|
7423
|
-
className: "p-
|
|
7424
|
-
style: {
|
|
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:
|
|
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
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
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
|
-
"
|
|
7663
|
+
"div",
|
|
7453
7664
|
{
|
|
7454
|
-
className: "
|
|
7665
|
+
className: "flex items-center flex-wrap",
|
|
7455
7666
|
style: {
|
|
7456
|
-
|
|
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
|
-
|
|
7460
|
-
|
|
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
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
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
|
-
"
|
|
7745
|
+
"button",
|
|
7502
7746
|
{
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
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
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
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: "
|
|
7531
|
-
style: {
|
|
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.
|
|
7534
|
-
|
|
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
|
-
|
|
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
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
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
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
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.
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
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(
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
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 || "
|
|
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,
|
|
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.
|
|
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
|