@compass-labs/widgets 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -932,7 +932,7 @@ function CompassProvider({
932
932
  }),
933
933
  []
934
934
  );
935
- return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsxRuntime.jsx(ThemeProvider, { theme, children: /* @__PURE__ */ jsxRuntime.jsx(ApiProvider, { apiKey, children: /* @__PURE__ */ jsxRuntime.jsx(ChainProvider, { defaultChain, children: /* @__PURE__ */ jsxRuntime.jsx(WalletProvider, { wallet, children: /* @__PURE__ */ jsxRuntime.jsx(EarnAccountProvider, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "compass-widget", children }) }) }) }) }) }) });
935
+ return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsxRuntime.jsx(ThemeProvider, { theme, children: /* @__PURE__ */ jsxRuntime.jsx(ApiProvider, { apiKey, children: /* @__PURE__ */ jsxRuntime.jsx(ChainProvider, { defaultChain, children: /* @__PURE__ */ jsxRuntime.jsx(WalletProvider, { wallet, children: /* @__PURE__ */ jsxRuntime.jsx(EarnAccountProvider, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "compass-widget", style: { width: "100%", height: "100%" }, children }) }) }) }) }) }) });
936
936
  }
937
937
  var SUPPORTED_CHAINS = ["ethereum", "base", "arbitrum"];
938
938
  function ChainSwitcher() {
@@ -2341,6 +2341,48 @@ function AccountBalancesModal({
2341
2341
  children: "No token balances found"
2342
2342
  }
2343
2343
  ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
2344
+ /* @__PURE__ */ jsxRuntime.jsxs(
2345
+ "div",
2346
+ {
2347
+ className: "flex items-center justify-between border",
2348
+ style: {
2349
+ borderColor: "var(--compass-color-border)",
2350
+ backgroundColor: "var(--compass-color-surface)",
2351
+ borderRadius: "var(--compass-border-radius-lg)",
2352
+ padding: "calc(var(--compass-spacing-unit) * 1)",
2353
+ marginBottom: "calc(var(--compass-spacing-unit) * 0.5)"
2354
+ },
2355
+ children: [
2356
+ /* @__PURE__ */ jsxRuntime.jsx(
2357
+ "span",
2358
+ {
2359
+ className: "font-medium",
2360
+ style: { color: "var(--compass-color-text-secondary)" },
2361
+ children: "Total Balance"
2362
+ }
2363
+ ),
2364
+ /* @__PURE__ */ jsxRuntime.jsx(
2365
+ "span",
2366
+ {
2367
+ className: "font-bold text-xl",
2368
+ style: { color: "var(--compass-color-text)" },
2369
+ children: formatUSD(totalUsdValue)
2370
+ }
2371
+ )
2372
+ ]
2373
+ }
2374
+ ),
2375
+ /* @__PURE__ */ jsxRuntime.jsx(
2376
+ "div",
2377
+ {
2378
+ className: "text-xs font-medium uppercase tracking-wide",
2379
+ style: {
2380
+ color: "var(--compass-color-text-tertiary)",
2381
+ padding: "calc(var(--compass-spacing-unit) * 0.5) 0"
2382
+ },
2383
+ children: "Token Breakdown"
2384
+ }
2385
+ ),
2344
2386
  balances.map((token) => /* @__PURE__ */ jsxRuntime.jsxs(
2345
2387
  "div",
2346
2388
  {
@@ -2394,38 +2436,7 @@ function AccountBalancesModal({
2394
2436
  ]
2395
2437
  },
2396
2438
  token.symbol
2397
- )),
2398
- /* @__PURE__ */ jsxRuntime.jsxs(
2399
- "div",
2400
- {
2401
- className: "flex items-center justify-between border",
2402
- style: {
2403
- borderColor: "var(--compass-color-border)",
2404
- backgroundColor: "var(--compass-color-surface)",
2405
- borderRadius: "var(--compass-border-radius-lg)",
2406
- padding: "calc(var(--compass-spacing-unit) * 0.75)",
2407
- marginTop: "calc(var(--compass-spacing-unit) * 0.5)"
2408
- },
2409
- children: [
2410
- /* @__PURE__ */ jsxRuntime.jsx(
2411
- "span",
2412
- {
2413
- className: "font-medium",
2414
- style: { color: "var(--compass-color-text-secondary)" },
2415
- children: "Total Balance"
2416
- }
2417
- ),
2418
- /* @__PURE__ */ jsxRuntime.jsx(
2419
- "span",
2420
- {
2421
- className: "font-bold text-lg",
2422
- style: { color: "var(--compass-color-text)" },
2423
- children: formatUSD(totalUsdValue)
2424
- }
2425
- )
2426
- ]
2427
- }
2428
- )
2439
+ ))
2429
2440
  ] }),
2430
2441
  earnAccountAddress && /* @__PURE__ */ jsxRuntime.jsx(
2431
2442
  "div",
@@ -3788,202 +3799,949 @@ function AaveMarketsList({
3788
3799
  )
3789
3800
  ] });
3790
3801
  }
3791
- var CHAIN_ID = "ethereum";
3792
- function formatCurrency(amount) {
3793
- if (!amount) return "$0.00";
3794
- const num = typeof amount === "string" ? parseFloat(amount) : amount;
3795
- if (isNaN(num)) return "$0.00";
3796
- return new Intl.NumberFormat("en-US", {
3797
- style: "currency",
3798
- currency: "USD",
3799
- minimumFractionDigits: 2,
3800
- maximumFractionDigits: 2
3801
- }).format(num);
3802
- }
3803
- function formatAPY3(apy) {
3804
- if (!apy) return "0.00";
3805
- const num = parseFloat(apy);
3806
- return num.toFixed(2);
3807
- }
3808
- function formatAmount2(value) {
3809
- const num = typeof value === "string" ? parseFloat(value) : value;
3810
- if (isNaN(num)) return "0";
3811
- return num.toFixed(6).replace(/\.?0+$/, "");
3812
- }
3813
- function EarnAccount({
3814
- showHeader = true,
3815
- showInterestRate = true,
3816
- showPnL = true,
3817
- showFundButton = true,
3818
- showTrustBadge = true,
3819
- compact = false,
3820
- title = "Savings Account",
3821
- onSupply,
3822
- onWithdraw
3802
+ var MARKET_TYPES = [
3803
+ { value: "aave", label: "AAVE" },
3804
+ { value: "pendle", label: "PENDLE" },
3805
+ { value: "vaults", label: "VAULTS" }
3806
+ ];
3807
+ function MarketSelector({
3808
+ markets,
3809
+ selectedMarket,
3810
+ onMarketSelect,
3811
+ marketType,
3812
+ onMarketTypeChange,
3813
+ minTvl,
3814
+ onMinTvlChange,
3815
+ isLoading
3823
3816
  }) {
3824
- const { address, signTypedData } = useEmbeddableWallet();
3825
- const { isDeployed } = useEarnAccount();
3826
- const queryClient = reactQuery.useQueryClient();
3827
- const [activeTab, setActiveTab] = react.useState("deposit");
3828
- const [amount, setAmount] = react.useState("");
3829
- const [isProcessing, setIsProcessing] = react.useState(false);
3830
- const [error, setError] = react.useState(null);
3831
- const [statusMessage, setStatusMessage] = react.useState("");
3832
- const [isFundModalOpen, setIsFundModalOpen] = react.useState(false);
3833
- const [fundAmount, setFundAmount] = react.useState("");
3834
- const [fundToken, setFundToken] = react.useState("USDC");
3835
- const [isFunding, setIsFunding] = react.useState(false);
3836
- const [fundError, setFundError] = react.useState(null);
3837
- const [fundStatus, setFundStatus] = react.useState("");
3838
- const FUND_TOKENS = ["USDC", "DAI"];
3839
- const walletBalanceQuery = reactQuery.useQuery({
3840
- queryKey: ["walletBalance", CHAIN_ID, address, fundToken],
3841
- queryFn: async () => {
3842
- if (!address) return null;
3843
- try {
3844
- const response = await fetch(
3845
- `/api/compass/token/balance?address=${address}&chain=${CHAIN_ID}&token=${fundToken}`
3846
- );
3847
- if (!response.ok) return null;
3848
- const data = await response.json();
3849
- return data;
3850
- } catch {
3851
- return null;
3852
- }
3853
- },
3854
- enabled: !!address && isFundModalOpen,
3855
- staleTime: 15 * 1e3
3856
- });
3857
- const walletBalance = walletBalanceQuery.data?.balance || "0";
3858
- const balancesQuery = reactQuery.useQuery({
3859
- queryKey: ["earnAccountBalances", CHAIN_ID, address],
3860
- queryFn: async () => {
3861
- if (!address) return null;
3862
- const response = await fetch(
3863
- `/api/compass/earn-account/balances?owner=${address}&chain=${CHAIN_ID}`
3864
- );
3865
- if (!response.ok) {
3866
- throw new Error("Failed to fetch balances");
3867
- }
3868
- return response.json();
3869
- },
3870
- enabled: !!address && isDeployed,
3871
- staleTime: 30 * 1e3
3872
- });
3873
- const availableBalance = balancesQuery.data?.balances?.["USDC"]?.balance || "0";
3874
- const marketQuery = reactQuery.useQuery({
3875
- queryKey: ["earnAccountUSDC", CHAIN_ID],
3876
- queryFn: async () => {
3877
- const response = await fetch(`/api/compass/aave/markets?chain=${CHAIN_ID}`);
3878
- if (!response.ok) return null;
3879
- const data = await response.json();
3880
- const marketsDict = data.markets || {};
3881
- const usdcData = marketsDict["USDC"];
3882
- if (!usdcData) return null;
3883
- const chainData = usdcData.chains?.[CHAIN_ID];
3884
- if (!chainData) return null;
3885
- return {
3886
- marketAddress: chainData.address || "",
3887
- supplyApy: chainData.supplyApy?.toString() ?? null
3888
- };
3889
- },
3890
- staleTime: 30 * 1e3
3891
- });
3892
- const positionQuery = reactQuery.useQuery({
3893
- queryKey: ["earnAccountUSDCPosition", CHAIN_ID, address],
3894
- queryFn: async () => {
3895
- if (!address) return null;
3896
- const response = await fetch(`/api/compass/positions?chain=${CHAIN_ID}&owner=${address}`);
3897
- if (!response.ok) return null;
3898
- const data = await response.json();
3899
- const aavePositions = data.aave || [];
3900
- const usdcPosition = aavePositions.find(
3901
- (p) => p.reserveSymbol?.toUpperCase() === "USDC"
3902
- );
3903
- if (!usdcPosition) return null;
3904
- return {
3905
- balance: usdcPosition.balance || "0",
3906
- pnl: usdcPosition.pnl ? {
3907
- unrealizedPnl: usdcPosition.pnl.unrealizedPnl || "0",
3908
- realizedPnl: usdcPosition.pnl.realizedPnl || "0",
3909
- totalPnl: usdcPosition.pnl.totalPnl || "0",
3910
- totalDeposited: usdcPosition.pnl.totalDeposited || "0"
3911
- } : void 0,
3912
- deposits: (usdcPosition.deposits || []).map((d) => ({
3913
- amount: d.inputAmount || d.amount || "0",
3914
- blockNumber: d.blockNumber || 0,
3915
- txHash: d.transactionHash || d.txHash || ""
3916
- })),
3917
- withdrawals: (usdcPosition.withdrawals || []).map((w) => ({
3918
- amount: w.outputAmount || w.amount || "0",
3919
- blockNumber: w.blockNumber || 0,
3920
- txHash: w.transactionHash || w.txHash || ""
3921
- }))
3922
- };
3923
- },
3924
- enabled: !!address,
3925
- staleTime: 30 * 1e3
3926
- });
3927
- const market = marketQuery.data;
3928
- const userPosition = positionQuery.data;
3929
- const depositedBalance = parseFloat(userPosition?.balance || "0");
3930
- const available = parseFloat(availableBalance);
3931
- const totalBalance = available + depositedBalance;
3932
- const earnings = parseFloat(userPosition?.pnl?.totalPnl || "0");
3933
- const needsSwap = fundToken !== "USDC";
3934
- const ensureTokenApproval = async (token) => {
3935
- setFundStatus(`Checking ${token} approval...`);
3936
- const approveResponse = await fetch("/api/compass/transfer/approve", {
3937
- method: "POST",
3938
- headers: { "Content-Type": "application/json" },
3939
- body: JSON.stringify({
3940
- owner: address,
3941
- chain: CHAIN_ID,
3942
- token
3943
- })
3944
- });
3945
- if (!approveResponse.ok) {
3946
- const errData = await approveResponse.json();
3947
- throw new Error(errData.error || "Failed to check approval");
3948
- }
3949
- const approvalData = await approveResponse.json();
3950
- if (approvalData.approved) {
3951
- return;
3952
- }
3953
- if (approvalData.requiresTransaction && approvalData.transaction) {
3954
- setFundStatus(`Approving ${token} (requires on-chain transaction)...`);
3955
- const executeApprovalResponse = await fetch("/api/compass/approval/execute", {
3956
- method: "POST",
3957
- headers: { "Content-Type": "application/json" },
3958
- body: JSON.stringify({
3959
- owner: address,
3960
- chain: CHAIN_ID,
3961
- transaction: approvalData.transaction
3962
- })
3963
- });
3964
- if (!executeApprovalResponse.ok) {
3965
- const errData = await executeApprovalResponse.json();
3966
- throw new Error(errData.error || "Approval transaction failed");
3967
- }
3968
- return;
3969
- }
3970
- if (approvalData.eip712) {
3971
- setFundStatus(`Please approve ${token} for transfers...`);
3972
- const approvalSignature = await signTypedData({
3973
- domain: approvalData.domain,
3974
- types: approvalData.normalizedTypes,
3975
- primaryType: "Permit",
3976
- message: approvalData.message
3977
- });
3978
- setFundStatus(`Setting up ${token} approval...`);
3979
- const executeApprovalResponse = await fetch("/api/compass/transfer/execute", {
3980
- method: "POST",
3981
- headers: { "Content-Type": "application/json" },
3982
- body: JSON.stringify({
3983
- owner: address,
3984
- chain: CHAIN_ID,
3985
- eip712: approvalData.eip712,
3986
- signature: approvalSignature
3817
+ const [isDropdownOpen, setIsDropdownOpen] = react.useState(false);
3818
+ const filteredMarkets = react.useMemo(() => {
3819
+ return markets.filter((m) => m.type === marketType && m.tvl >= minTvl);
3820
+ }, [markets, marketType, minTvl]);
3821
+ const maxTvl = react.useMemo(() => {
3822
+ const typeMarkets = markets.filter((m) => m.type === marketType);
3823
+ return Math.max(...typeMarkets.map((m) => m.tvl), 1e8);
3824
+ }, [markets, marketType]);
3825
+ const hasMarkets = filteredMarkets.length > 0;
3826
+ const formatTvl = (value) => {
3827
+ if (value >= 1e9) return `$${(value / 1e9).toFixed(1)}B`;
3828
+ if (value >= 1e6) return `$${(value / 1e6).toFixed(1)}M`;
3829
+ if (value >= 1e3) return `$${(value / 1e3).toFixed(0)}K`;
3830
+ return `$${value}`;
3831
+ };
3832
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3833
+ "div",
3834
+ {
3835
+ style: {
3836
+ display: "flex",
3837
+ flexDirection: "column",
3838
+ gap: "calc(var(--compass-spacing-unit) * 3)"
3839
+ },
3840
+ children: [
3841
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3842
+ /* @__PURE__ */ jsxRuntime.jsx(
3843
+ "label",
3844
+ {
3845
+ style: {
3846
+ display: "block",
3847
+ fontSize: "var(--compass-font-size-sm)",
3848
+ color: "var(--compass-color-text-secondary)",
3849
+ marginBottom: "var(--compass-spacing-unit)"
3850
+ },
3851
+ children: "Market Type"
3852
+ }
3853
+ ),
3854
+ /* @__PURE__ */ jsxRuntime.jsx(
3855
+ "div",
3856
+ {
3857
+ style: {
3858
+ display: "flex",
3859
+ borderRadius: "var(--compass-border-radius-lg)",
3860
+ border: "1px solid var(--compass-color-border)",
3861
+ overflow: "hidden"
3862
+ },
3863
+ children: MARKET_TYPES.map((type) => /* @__PURE__ */ jsxRuntime.jsx(
3864
+ "button",
3865
+ {
3866
+ onClick: () => onMarketTypeChange(type.value),
3867
+ style: {
3868
+ flex: 1,
3869
+ padding: "calc(var(--compass-spacing-unit) * 2)",
3870
+ backgroundColor: marketType === type.value ? "var(--compass-color-primary)" : "var(--compass-color-surface)",
3871
+ color: marketType === type.value ? "var(--compass-color-primary-foreground)" : "var(--compass-color-text)",
3872
+ border: "none",
3873
+ cursor: "pointer",
3874
+ fontSize: "var(--compass-font-size-sm)",
3875
+ fontWeight: 500,
3876
+ fontFamily: "var(--compass-font-family)",
3877
+ transition: "var(--compass-transition-normal)"
3878
+ },
3879
+ children: type.label
3880
+ },
3881
+ type.value
3882
+ ))
3883
+ }
3884
+ )
3885
+ ] }),
3886
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3887
+ /* @__PURE__ */ jsxRuntime.jsx(
3888
+ "label",
3889
+ {
3890
+ style: {
3891
+ display: "block",
3892
+ fontSize: "var(--compass-font-size-sm)",
3893
+ color: "var(--compass-color-text-secondary)",
3894
+ marginBottom: "var(--compass-spacing-unit)"
3895
+ },
3896
+ children: "Select Market"
3897
+ }
3898
+ ),
3899
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
3900
+ /* @__PURE__ */ jsxRuntime.jsxs(
3901
+ "button",
3902
+ {
3903
+ onClick: () => hasMarkets && setIsDropdownOpen(!isDropdownOpen),
3904
+ disabled: !hasMarkets || isLoading,
3905
+ style: {
3906
+ width: "100%",
3907
+ padding: "calc(var(--compass-spacing-unit) * 2.5)",
3908
+ backgroundColor: "var(--compass-color-surface)",
3909
+ border: "1px solid var(--compass-color-border)",
3910
+ borderRadius: "var(--compass-border-radius-lg)",
3911
+ display: "flex",
3912
+ justifyContent: "space-between",
3913
+ alignItems: "center",
3914
+ cursor: hasMarkets ? "pointer" : "not-allowed",
3915
+ opacity: hasMarkets ? 1 : 0.5,
3916
+ fontFamily: "var(--compass-font-family)"
3917
+ },
3918
+ children: [
3919
+ /* @__PURE__ */ jsxRuntime.jsx(
3920
+ "span",
3921
+ {
3922
+ style: {
3923
+ color: selectedMarket ? "var(--compass-color-text)" : "var(--compass-color-text-secondary)"
3924
+ },
3925
+ children: isLoading ? "Loading markets..." : !hasMarkets ? `No markets above ${formatTvl(minTvl)}` : selectedMarket ? selectedMarket.name : "Select a market"
3926
+ }
3927
+ ),
3928
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "var(--compass-spacing-unit)" }, children: [
3929
+ selectedMarket && /* @__PURE__ */ jsxRuntime.jsxs(
3930
+ "span",
3931
+ {
3932
+ style: {
3933
+ color: "var(--compass-color-success)",
3934
+ fontSize: "var(--compass-font-size-sm)"
3935
+ },
3936
+ children: [
3937
+ selectedMarket.apy.toFixed(2),
3938
+ "%"
3939
+ ]
3940
+ }
3941
+ ),
3942
+ /* @__PURE__ */ jsxRuntime.jsx(
3943
+ lucideReact.ChevronDown,
3944
+ {
3945
+ size: 16,
3946
+ style: {
3947
+ color: "var(--compass-color-text-secondary)",
3948
+ transform: isDropdownOpen ? "rotate(180deg)" : "rotate(0deg)",
3949
+ transition: "var(--compass-transition-normal)"
3950
+ }
3951
+ }
3952
+ )
3953
+ ] })
3954
+ ]
3955
+ }
3956
+ ),
3957
+ isDropdownOpen && /* @__PURE__ */ jsxRuntime.jsx(
3958
+ "div",
3959
+ {
3960
+ style: {
3961
+ position: "absolute",
3962
+ top: "100%",
3963
+ left: 0,
3964
+ right: 0,
3965
+ marginTop: "var(--compass-spacing-unit)",
3966
+ backgroundColor: "var(--compass-color-surface-elevated)",
3967
+ border: "1px solid var(--compass-color-border)",
3968
+ borderRadius: "var(--compass-border-radius-lg)",
3969
+ boxShadow: "var(--compass-shadow-lg)",
3970
+ zIndex: 50,
3971
+ maxHeight: "200px",
3972
+ overflowY: "auto"
3973
+ },
3974
+ children: filteredMarkets.map((market) => /* @__PURE__ */ jsxRuntime.jsxs(
3975
+ "button",
3976
+ {
3977
+ onClick: () => {
3978
+ onMarketSelect(market);
3979
+ setIsDropdownOpen(false);
3980
+ },
3981
+ style: {
3982
+ width: "100%",
3983
+ padding: "calc(var(--compass-spacing-unit) * 2)",
3984
+ backgroundColor: selectedMarket?.id === market.id ? "var(--compass-color-primary-muted)" : "transparent",
3985
+ border: "none",
3986
+ borderBottom: "1px solid var(--compass-color-border)",
3987
+ cursor: "pointer",
3988
+ textAlign: "left",
3989
+ fontFamily: "var(--compass-font-family)"
3990
+ },
3991
+ children: [
3992
+ /* @__PURE__ */ jsxRuntime.jsxs(
3993
+ "div",
3994
+ {
3995
+ style: {
3996
+ display: "flex",
3997
+ justifyContent: "space-between",
3998
+ alignItems: "center"
3999
+ },
4000
+ children: [
4001
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text)" }, children: market.name }),
4002
+ /* @__PURE__ */ jsxRuntime.jsxs(
4003
+ "span",
4004
+ {
4005
+ style: {
4006
+ color: "var(--compass-color-success)",
4007
+ fontSize: "var(--compass-font-size-sm)"
4008
+ },
4009
+ children: [
4010
+ market.apy.toFixed(2),
4011
+ "% APY"
4012
+ ]
4013
+ }
4014
+ )
4015
+ ]
4016
+ }
4017
+ ),
4018
+ /* @__PURE__ */ jsxRuntime.jsxs(
4019
+ "div",
4020
+ {
4021
+ style: {
4022
+ fontSize: "var(--compass-font-size-xs)",
4023
+ color: "var(--compass-color-text-tertiary)",
4024
+ marginTop: "calc(var(--compass-spacing-unit) * 0.5)"
4025
+ },
4026
+ children: [
4027
+ "TVL: ",
4028
+ formatTvl(market.tvl),
4029
+ market.expiry && ` \xB7 Expires: ${market.expiry}`
4030
+ ]
4031
+ }
4032
+ )
4033
+ ]
4034
+ },
4035
+ market.id
4036
+ ))
4037
+ }
4038
+ )
4039
+ ] })
4040
+ ] }),
4041
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4042
+ /* @__PURE__ */ jsxRuntime.jsxs(
4043
+ "div",
4044
+ {
4045
+ style: {
4046
+ display: "flex",
4047
+ justifyContent: "space-between",
4048
+ alignItems: "center",
4049
+ marginBottom: "var(--compass-spacing-unit)"
4050
+ },
4051
+ children: [
4052
+ /* @__PURE__ */ jsxRuntime.jsx(
4053
+ "label",
4054
+ {
4055
+ style: {
4056
+ fontSize: "var(--compass-font-size-sm)",
4057
+ color: "var(--compass-color-text-secondary)"
4058
+ },
4059
+ children: "Min TVL"
4060
+ }
4061
+ ),
4062
+ /* @__PURE__ */ jsxRuntime.jsx(
4063
+ "span",
4064
+ {
4065
+ style: {
4066
+ fontSize: "var(--compass-font-size-sm)",
4067
+ color: "var(--compass-color-text)"
4068
+ },
4069
+ children: formatTvl(minTvl)
4070
+ }
4071
+ )
4072
+ ]
4073
+ }
4074
+ ),
4075
+ /* @__PURE__ */ jsxRuntime.jsx(
4076
+ "input",
4077
+ {
4078
+ type: "range",
4079
+ min: 0,
4080
+ max: maxTvl,
4081
+ step: 1e5,
4082
+ value: minTvl,
4083
+ onChange: (e) => onMinTvlChange(Number(e.target.value)),
4084
+ style: {
4085
+ width: "100%",
4086
+ accentColor: "var(--compass-color-primary)"
4087
+ }
4088
+ }
4089
+ )
4090
+ ] })
4091
+ ]
4092
+ }
4093
+ );
4094
+ }
4095
+ function PositionCard({ position }) {
4096
+ const [isHistoryExpanded, setIsHistoryExpanded] = react.useState(false);
4097
+ const formatDate2 = (dateStr) => {
4098
+ const date = new Date(dateStr);
4099
+ return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
4100
+ };
4101
+ const formatShortDate = (dateStr) => {
4102
+ const date = new Date(dateStr);
4103
+ return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
4104
+ };
4105
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4106
+ "div",
4107
+ {
4108
+ style: {
4109
+ backgroundColor: "var(--compass-color-surface)",
4110
+ border: "1px solid var(--compass-color-border)",
4111
+ borderRadius: "var(--compass-border-radius-lg)",
4112
+ padding: "var(--compass-spacing-card)"
4113
+ },
4114
+ children: [
4115
+ /* @__PURE__ */ jsxRuntime.jsxs(
4116
+ "div",
4117
+ {
4118
+ style: {
4119
+ display: "flex",
4120
+ justifyContent: "space-between",
4121
+ alignItems: "flex-start",
4122
+ marginBottom: "calc(var(--compass-spacing-unit) * 2)"
4123
+ },
4124
+ children: [
4125
+ /* @__PURE__ */ jsxRuntime.jsx(
4126
+ "span",
4127
+ {
4128
+ style: {
4129
+ fontWeight: 600,
4130
+ color: "var(--compass-color-text)",
4131
+ fontSize: "var(--compass-font-size-base)"
4132
+ },
4133
+ children: position.marketName
4134
+ }
4135
+ ),
4136
+ /* @__PURE__ */ jsxRuntime.jsxs(
4137
+ "span",
4138
+ {
4139
+ style: {
4140
+ color: "var(--compass-color-success)",
4141
+ fontSize: "var(--compass-font-size-sm)",
4142
+ backgroundColor: "var(--compass-color-success-muted)",
4143
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 1.5)",
4144
+ borderRadius: "var(--compass-border-radius-full)"
4145
+ },
4146
+ children: [
4147
+ "APY: ",
4148
+ position.apy.toFixed(2),
4149
+ "%"
4150
+ ]
4151
+ }
4152
+ )
4153
+ ]
4154
+ }
4155
+ ),
4156
+ /* @__PURE__ */ jsxRuntime.jsxs(
4157
+ "div",
4158
+ {
4159
+ style: {
4160
+ display: "grid",
4161
+ gridTemplateColumns: "1fr 1fr",
4162
+ gap: "calc(var(--compass-spacing-unit) * 2)",
4163
+ marginBottom: "calc(var(--compass-spacing-unit) * 2)"
4164
+ },
4165
+ children: [
4166
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4167
+ /* @__PURE__ */ jsxRuntime.jsx(
4168
+ "div",
4169
+ {
4170
+ style: {
4171
+ fontSize: "var(--compass-font-size-xs)",
4172
+ color: "var(--compass-color-text-tertiary)",
4173
+ marginBottom: "calc(var(--compass-spacing-unit) * 0.5)"
4174
+ },
4175
+ children: "Amount"
4176
+ }
4177
+ ),
4178
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "var(--compass-color-text)", fontWeight: 500 }, children: [
4179
+ position.amount.toLocaleString(),
4180
+ " ",
4181
+ position.token
4182
+ ] })
4183
+ ] }),
4184
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4185
+ /* @__PURE__ */ jsxRuntime.jsx(
4186
+ "div",
4187
+ {
4188
+ style: {
4189
+ fontSize: "var(--compass-font-size-xs)",
4190
+ color: "var(--compass-color-text-tertiary)",
4191
+ marginBottom: "calc(var(--compass-spacing-unit) * 0.5)"
4192
+ },
4193
+ children: "Entered"
4194
+ }
4195
+ ),
4196
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--compass-color-text)" }, children: formatDate2(position.entryDate) })
4197
+ ] })
4198
+ ]
4199
+ }
4200
+ ),
4201
+ /* @__PURE__ */ jsxRuntime.jsxs(
4202
+ "div",
4203
+ {
4204
+ style: {
4205
+ display: "flex",
4206
+ justifyContent: "space-between",
4207
+ alignItems: "center",
4208
+ paddingTop: "calc(var(--compass-spacing-unit) * 2)",
4209
+ borderTop: "1px solid var(--compass-color-border)"
4210
+ },
4211
+ children: [
4212
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4213
+ /* @__PURE__ */ jsxRuntime.jsxs(
4214
+ "span",
4215
+ {
4216
+ style: {
4217
+ fontSize: "var(--compass-font-size-sm)",
4218
+ color: "var(--compass-color-text-secondary)"
4219
+ },
4220
+ children: [
4221
+ "Performance:",
4222
+ " "
4223
+ ]
4224
+ }
4225
+ ),
4226
+ /* @__PURE__ */ jsxRuntime.jsxs(
4227
+ "span",
4228
+ {
4229
+ style: {
4230
+ fontWeight: 600,
4231
+ color: position.performance >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)"
4232
+ },
4233
+ children: [
4234
+ position.performance >= 0 ? "+" : "",
4235
+ "$",
4236
+ position.performance.toFixed(2)
4237
+ ]
4238
+ }
4239
+ )
4240
+ ] }),
4241
+ /* @__PURE__ */ jsxRuntime.jsxs(
4242
+ "button",
4243
+ {
4244
+ onClick: () => setIsHistoryExpanded(!isHistoryExpanded),
4245
+ style: {
4246
+ display: "flex",
4247
+ alignItems: "center",
4248
+ gap: "calc(var(--compass-spacing-unit) * 0.5)",
4249
+ padding: "calc(var(--compass-spacing-unit) * 1) calc(var(--compass-spacing-unit) * 2)",
4250
+ backgroundColor: "transparent",
4251
+ border: "1px solid var(--compass-color-border)",
4252
+ borderRadius: "var(--compass-border-radius-md)",
4253
+ cursor: "pointer",
4254
+ fontSize: "var(--compass-font-size-xs)",
4255
+ color: "var(--compass-color-text-secondary)",
4256
+ fontFamily: "var(--compass-font-family)"
4257
+ },
4258
+ children: [
4259
+ "tx history",
4260
+ isHistoryExpanded ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { size: 12 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 12 })
4261
+ ]
4262
+ }
4263
+ )
4264
+ ]
4265
+ }
4266
+ ),
4267
+ isHistoryExpanded && position.transactions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
4268
+ "div",
4269
+ {
4270
+ style: {
4271
+ marginTop: "calc(var(--compass-spacing-unit) * 2)",
4272
+ backgroundColor: "var(--compass-color-background)",
4273
+ borderRadius: "var(--compass-border-radius-md)",
4274
+ padding: "calc(var(--compass-spacing-unit) * 2)"
4275
+ },
4276
+ children: position.transactions.map((tx) => /* @__PURE__ */ jsxRuntime.jsxs(
4277
+ "div",
4278
+ {
4279
+ style: {
4280
+ display: "flex",
4281
+ justifyContent: "space-between",
4282
+ alignItems: "center",
4283
+ padding: "calc(var(--compass-spacing-unit) * 1.5) 0",
4284
+ borderBottom: "1px solid var(--compass-color-border)"
4285
+ },
4286
+ children: [
4287
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "calc(var(--compass-spacing-unit) * 2)" }, children: [
4288
+ /* @__PURE__ */ jsxRuntime.jsx(
4289
+ "span",
4290
+ {
4291
+ style: {
4292
+ fontSize: "var(--compass-font-size-xs)",
4293
+ color: "var(--compass-color-text-tertiary)",
4294
+ minWidth: "50px"
4295
+ },
4296
+ children: formatShortDate(tx.date)
4297
+ }
4298
+ ),
4299
+ /* @__PURE__ */ jsxRuntime.jsx(
4300
+ "span",
4301
+ {
4302
+ style: {
4303
+ fontSize: "var(--compass-font-size-sm)",
4304
+ color: "var(--compass-color-text-secondary)",
4305
+ textTransform: "capitalize"
4306
+ },
4307
+ children: tx.type
4308
+ }
4309
+ )
4310
+ ] }),
4311
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "calc(var(--compass-spacing-unit) * 2)" }, children: [
4312
+ /* @__PURE__ */ jsxRuntime.jsxs(
4313
+ "span",
4314
+ {
4315
+ style: {
4316
+ fontWeight: 500,
4317
+ color: tx.type === "deposit" ? "var(--compass-color-success)" : "var(--compass-color-error)"
4318
+ },
4319
+ children: [
4320
+ tx.type === "deposit" ? "+" : "-",
4321
+ tx.amount,
4322
+ " ",
4323
+ tx.token
4324
+ ]
4325
+ }
4326
+ ),
4327
+ /* @__PURE__ */ jsxRuntime.jsx(
4328
+ "a",
4329
+ {
4330
+ href: `https://etherscan.io/tx/${tx.txHash}`,
4331
+ target: "_blank",
4332
+ rel: "noopener noreferrer",
4333
+ style: { color: "var(--compass-color-text-tertiary)" },
4334
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 12 })
4335
+ }
4336
+ )
4337
+ ] })
4338
+ ]
4339
+ },
4340
+ tx.id
4341
+ ))
4342
+ }
4343
+ )
4344
+ ]
4345
+ }
4346
+ );
4347
+ }
4348
+ function EarningsModal({ isOpen, onClose, positions, totalEarned, isLoading }) {
4349
+ if (!isOpen) return null;
4350
+ return /* @__PURE__ */ jsxRuntime.jsx(
4351
+ "div",
4352
+ {
4353
+ style: {
4354
+ position: "fixed",
4355
+ inset: 0,
4356
+ backgroundColor: "var(--compass-color-overlay)",
4357
+ display: "flex",
4358
+ alignItems: "center",
4359
+ justifyContent: "center",
4360
+ zIndex: 50
4361
+ },
4362
+ onClick: onClose,
4363
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
4364
+ "div",
4365
+ {
4366
+ style: {
4367
+ backgroundColor: "var(--compass-color-surface-elevated)",
4368
+ borderRadius: "var(--compass-border-radius-xl)",
4369
+ boxShadow: "var(--compass-shadow-lg)",
4370
+ width: "100%",
4371
+ maxWidth: "480px",
4372
+ maxHeight: "80vh",
4373
+ overflow: "hidden",
4374
+ display: "flex",
4375
+ flexDirection: "column"
4376
+ },
4377
+ onClick: (e) => e.stopPropagation(),
4378
+ children: [
4379
+ /* @__PURE__ */ jsxRuntime.jsxs(
4380
+ "div",
4381
+ {
4382
+ style: {
4383
+ display: "flex",
4384
+ justifyContent: "space-between",
4385
+ alignItems: "center",
4386
+ padding: "var(--compass-spacing-card)",
4387
+ borderBottom: "1px solid var(--compass-color-border)"
4388
+ },
4389
+ children: [
4390
+ /* @__PURE__ */ jsxRuntime.jsx(
4391
+ "h2",
4392
+ {
4393
+ style: {
4394
+ fontWeight: 600,
4395
+ fontSize: "var(--compass-font-size-lg)",
4396
+ color: "var(--compass-color-text)",
4397
+ margin: 0
4398
+ },
4399
+ children: "Your Positions"
4400
+ }
4401
+ ),
4402
+ /* @__PURE__ */ jsxRuntime.jsx(
4403
+ "button",
4404
+ {
4405
+ onClick: onClose,
4406
+ style: {
4407
+ backgroundColor: "transparent",
4408
+ border: "none",
4409
+ cursor: "pointer",
4410
+ padding: "var(--compass-spacing-unit)",
4411
+ color: "var(--compass-color-text-secondary)"
4412
+ },
4413
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 20 })
4414
+ }
4415
+ )
4416
+ ]
4417
+ }
4418
+ ),
4419
+ /* @__PURE__ */ jsxRuntime.jsxs(
4420
+ "div",
4421
+ {
4422
+ style: {
4423
+ padding: "var(--compass-spacing-card)",
4424
+ borderBottom: "1px solid var(--compass-color-border)",
4425
+ backgroundColor: "var(--compass-color-surface)"
4426
+ },
4427
+ children: [
4428
+ /* @__PURE__ */ jsxRuntime.jsx(
4429
+ "div",
4430
+ {
4431
+ style: {
4432
+ fontSize: "var(--compass-font-size-sm)",
4433
+ color: "var(--compass-color-text-secondary)",
4434
+ marginBottom: "calc(var(--compass-spacing-unit) * 0.5)"
4435
+ },
4436
+ children: "Total Earned"
4437
+ }
4438
+ ),
4439
+ /* @__PURE__ */ jsxRuntime.jsxs(
4440
+ "div",
4441
+ {
4442
+ style: {
4443
+ fontSize: "var(--compass-font-size-xl)",
4444
+ fontWeight: 600,
4445
+ color: totalEarned >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)"
4446
+ },
4447
+ children: [
4448
+ totalEarned >= 0 ? "+" : "",
4449
+ "$",
4450
+ totalEarned.toFixed(2)
4451
+ ]
4452
+ }
4453
+ )
4454
+ ]
4455
+ }
4456
+ ),
4457
+ /* @__PURE__ */ jsxRuntime.jsx(
4458
+ "div",
4459
+ {
4460
+ style: {
4461
+ flex: 1,
4462
+ overflowY: "auto",
4463
+ padding: "var(--compass-spacing-card)",
4464
+ display: "flex",
4465
+ flexDirection: "column",
4466
+ gap: "calc(var(--compass-spacing-unit) * 3)"
4467
+ },
4468
+ children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(
4469
+ "div",
4470
+ {
4471
+ style: {
4472
+ textAlign: "center",
4473
+ padding: "calc(var(--compass-spacing-unit) * 6)",
4474
+ color: "var(--compass-color-text-secondary)"
4475
+ },
4476
+ children: "Loading positions..."
4477
+ }
4478
+ ) : positions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
4479
+ "div",
4480
+ {
4481
+ style: {
4482
+ textAlign: "center",
4483
+ padding: "calc(var(--compass-spacing-unit) * 6)",
4484
+ color: "var(--compass-color-text-secondary)"
4485
+ },
4486
+ children: [
4487
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { marginBottom: "var(--compass-spacing-unit)" }, children: "No active positions yet" }),
4488
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "var(--compass-font-size-sm)", color: "var(--compass-color-text-tertiary)" }, children: "Deposit into a market to start earning" })
4489
+ ]
4490
+ }
4491
+ ) : positions.map((position) => /* @__PURE__ */ jsxRuntime.jsx(PositionCard, { position }, position.id))
4492
+ }
4493
+ )
4494
+ ]
4495
+ }
4496
+ )
4497
+ }
4498
+ );
4499
+ }
4500
+ var CHAIN_ID = "ethereum";
4501
+ var SUPPORTED_TOKENS = ["USDC", "USDT", "DAI", "WETH", "SBC", "AUSD"];
4502
+ function formatCurrency(amount) {
4503
+ if (!amount) return "$0.00";
4504
+ const num = typeof amount === "string" ? parseFloat(amount) : amount;
4505
+ if (isNaN(num)) return "$0.00";
4506
+ return new Intl.NumberFormat("en-US", {
4507
+ style: "currency",
4508
+ currency: "USD",
4509
+ minimumFractionDigits: 2,
4510
+ maximumFractionDigits: 2
4511
+ }).format(num);
4512
+ }
4513
+ function formatAPY3(apy) {
4514
+ if (!apy) return "0.00";
4515
+ const num = parseFloat(apy);
4516
+ return num.toFixed(2);
4517
+ }
4518
+ function formatAmount2(value) {
4519
+ const num = typeof value === "string" ? parseFloat(value) : value;
4520
+ if (isNaN(num)) return "0";
4521
+ return num.toFixed(6).replace(/\.?0+$/, "");
4522
+ }
4523
+ function EarnAccount({
4524
+ showHeader = true,
4525
+ showInterestRate = true,
4526
+ showTopUpButton = true,
4527
+ showTrustBadge = true,
4528
+ compact = false,
4529
+ title = "Savings Account",
4530
+ onDeposit,
4531
+ onWithdraw,
4532
+ defaultMarketType = "aave",
4533
+ defaultMinTvl = 0
4534
+ }) {
4535
+ const { address, signTypedData } = useEmbeddableWallet();
4536
+ const { isDeployed } = useEarnAccount();
4537
+ const queryClient = reactQuery.useQueryClient();
4538
+ const [activeTab, setActiveTab] = react.useState("deposit");
4539
+ const [selectedToken, setSelectedToken] = react.useState("USDC");
4540
+ const [amount, setAmount] = react.useState("");
4541
+ const [isProcessing, setIsProcessing] = react.useState(false);
4542
+ const [error, setError] = react.useState(null);
4543
+ const [statusMessage, setStatusMessage] = react.useState("");
4544
+ const [isTokenDropdownOpen, setIsTokenDropdownOpen] = react.useState(false);
4545
+ const [isFundModalOpen, setIsFundModalOpen] = react.useState(false);
4546
+ const [fundAmount, setFundAmount] = react.useState("");
4547
+ const [fundToken, setFundToken] = react.useState("USDC");
4548
+ const [isFunding, setIsFunding] = react.useState(false);
4549
+ const [fundError, setFundError] = react.useState(null);
4550
+ const [fundStatus, setFundStatus] = react.useState("");
4551
+ const [isBalancesModalOpen, setIsBalancesModalOpen] = react.useState(false);
4552
+ const [marketType, setMarketType] = react.useState(defaultMarketType);
4553
+ const [selectedMarket, setSelectedMarket] = react.useState(null);
4554
+ const [minTvl, setMinTvl] = react.useState(defaultMinTvl);
4555
+ const [isEarningsModalOpen, setIsEarningsModalOpen] = react.useState(false);
4556
+ const FUND_TOKENS = ["USDC", "DAI"];
4557
+ const walletBalanceQuery = reactQuery.useQuery({
4558
+ queryKey: ["walletBalance", CHAIN_ID, address, fundToken],
4559
+ queryFn: async () => {
4560
+ if (!address) return null;
4561
+ try {
4562
+ const response = await fetch(
4563
+ `/api/compass/token/balance?address=${address}&chain=${CHAIN_ID}&token=${fundToken}`
4564
+ );
4565
+ if (!response.ok) return null;
4566
+ const data = await response.json();
4567
+ return data;
4568
+ } catch {
4569
+ return null;
4570
+ }
4571
+ },
4572
+ enabled: !!address && isFundModalOpen,
4573
+ staleTime: 15 * 1e3
4574
+ });
4575
+ const walletBalance = walletBalanceQuery.data?.balance || "0";
4576
+ const balancesQuery = reactQuery.useQuery({
4577
+ queryKey: ["earnAccountBalances", CHAIN_ID, address],
4578
+ queryFn: async () => {
4579
+ if (!address) return null;
4580
+ const response = await fetch(
4581
+ `/api/compass/earn-account/balances?owner=${address}&chain=${CHAIN_ID}`
4582
+ );
4583
+ if (!response.ok) {
4584
+ throw new Error("Failed to fetch balances");
4585
+ }
4586
+ return response.json();
4587
+ },
4588
+ enabled: !!address && isDeployed,
4589
+ staleTime: 30 * 1e3
4590
+ });
4591
+ const selectedTokenBalance = balancesQuery.data?.balances?.[selectedToken]?.balance || "0";
4592
+ const earnAccountTotalUsd = balancesQuery.data?.totalUsdValue || "0";
4593
+ const marketQuery = reactQuery.useQuery({
4594
+ queryKey: ["earnAccountUSDC", CHAIN_ID],
4595
+ queryFn: async () => {
4596
+ const response = await fetch(`/api/compass/aave/markets?chain=${CHAIN_ID}`);
4597
+ if (!response.ok) return null;
4598
+ const data = await response.json();
4599
+ const marketsDict = data.markets || {};
4600
+ const usdcData = marketsDict["USDC"];
4601
+ if (!usdcData) return null;
4602
+ const chainData = usdcData.chains?.[CHAIN_ID];
4603
+ if (!chainData) return null;
4604
+ return {
4605
+ marketAddress: chainData.address || "",
4606
+ supplyApy: chainData.supplyApy?.toString() ?? null
4607
+ };
4608
+ },
4609
+ staleTime: 30 * 1e3
4610
+ });
4611
+ const positionQuery = reactQuery.useQuery({
4612
+ queryKey: ["earnAccountUSDCPosition", CHAIN_ID, address],
4613
+ queryFn: async () => {
4614
+ if (!address) return null;
4615
+ const response = await fetch(`/api/compass/positions?chain=${CHAIN_ID}&owner=${address}`);
4616
+ if (!response.ok) return null;
4617
+ const data = await response.json();
4618
+ const aavePositions = data.aave || [];
4619
+ const usdcPosition = aavePositions.find(
4620
+ (p) => p.reserveSymbol?.toUpperCase() === "USDC"
4621
+ );
4622
+ if (!usdcPosition) return null;
4623
+ return {
4624
+ balance: usdcPosition.balance || "0",
4625
+ pnl: usdcPosition.pnl ? {
4626
+ unrealizedPnl: usdcPosition.pnl.unrealizedPnl || "0",
4627
+ realizedPnl: usdcPosition.pnl.realizedPnl || "0",
4628
+ totalPnl: usdcPosition.pnl.totalPnl || "0",
4629
+ totalDeposited: usdcPosition.pnl.totalDeposited || "0"
4630
+ } : void 0,
4631
+ deposits: (usdcPosition.deposits || []).map((d) => ({
4632
+ amount: d.inputAmount || d.amount || "0",
4633
+ blockNumber: d.blockNumber || 0,
4634
+ txHash: d.transactionHash || d.txHash || ""
4635
+ })),
4636
+ withdrawals: (usdcPosition.withdrawals || []).map((w) => ({
4637
+ amount: w.outputAmount || w.amount || "0",
4638
+ blockNumber: w.blockNumber || 0,
4639
+ txHash: w.transactionHash || w.txHash || ""
4640
+ }))
4641
+ };
4642
+ },
4643
+ enabled: !!address,
4644
+ staleTime: 30 * 1e3
4645
+ });
4646
+ const market = marketQuery.data;
4647
+ const userPosition = positionQuery.data;
4648
+ const depositedBalance = parseFloat(userPosition?.balance || "0");
4649
+ const earnAccountTotal = parseFloat(earnAccountTotalUsd);
4650
+ const totalBalance = earnAccountTotal + depositedBalance;
4651
+ const mockMarkets = [
4652
+ { id: "aave-usdc", name: "USDC", apy: 4.5, tvl: 5e8, type: "aave" },
4653
+ { id: "aave-usdt", name: "USDT", apy: 4.2, tvl: 35e7, type: "aave" },
4654
+ { id: "aave-dai", name: "DAI", apy: 3.8, tvl: 2e8, type: "aave" },
4655
+ { id: "pendle-usdc-mar25", name: "USDC Mar 2025", apy: 8.5, tvl: 5e7, type: "pendle", expiry: "Mar 2025" },
4656
+ { id: "pendle-usdc-jun25", name: "USDC Jun 2025", apy: 7.2, tvl: 3e7, type: "pendle", expiry: "Jun 2025" },
4657
+ { id: "vault-steakhouse", name: "Steakhouse USDC", apy: 6.5, tvl: 15e7, type: "vaults", curator: "Steakhouse" },
4658
+ { id: "vault-gauntlet", name: "Gauntlet USDC", apy: 5.8, tvl: 2e8, type: "vaults", curator: "Gauntlet" }
4659
+ ];
4660
+ const positions = userPosition?.balance && parseFloat(userPosition.balance) > 0 ? [{
4661
+ id: "pos-1",
4662
+ marketType: "aave",
4663
+ marketName: "USDC on Aave",
4664
+ marketId: "aave-usdc",
4665
+ amount: parseFloat(userPosition.balance),
4666
+ token: "USDC",
4667
+ apy: parseFloat(market?.supplyApy || "0"),
4668
+ entryDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3).toISOString(),
4669
+ performance: parseFloat(userPosition.pnl?.totalPnl || "0"),
4670
+ transactions: [
4671
+ ...(userPosition.deposits || []).map((d, i) => ({
4672
+ id: `dep-${i}`,
4673
+ date: new Date(Date.now() - (i + 1) * 7 * 24 * 60 * 60 * 1e3).toISOString(),
4674
+ type: "deposit",
4675
+ amount: parseFloat(d.amount),
4676
+ token: "USDC",
4677
+ txHash: d.txHash || ""
4678
+ })),
4679
+ ...(userPosition.withdrawals || []).map((w, i) => ({
4680
+ id: `wit-${i}`,
4681
+ date: new Date(Date.now() - i * 3 * 24 * 60 * 60 * 1e3).toISOString(),
4682
+ type: "withdraw",
4683
+ amount: parseFloat(w.amount),
4684
+ token: "USDC",
4685
+ txHash: w.txHash || ""
4686
+ }))
4687
+ ]
4688
+ }] : [];
4689
+ const totalEarned = positions.reduce((sum, p) => sum + p.performance, 0);
4690
+ const needsActionSwap = selectedToken !== "USDC";
4691
+ const needsFundSwap = fundToken !== "USDC";
4692
+ const ensureTokenApproval = async (token) => {
4693
+ setFundStatus(`Checking ${token} approval...`);
4694
+ const approveResponse = await fetch("/api/compass/transfer/approve", {
4695
+ method: "POST",
4696
+ headers: { "Content-Type": "application/json" },
4697
+ body: JSON.stringify({
4698
+ owner: address,
4699
+ chain: CHAIN_ID,
4700
+ token
4701
+ })
4702
+ });
4703
+ if (!approveResponse.ok) {
4704
+ const errData = await approveResponse.json();
4705
+ throw new Error(errData.error || "Failed to check approval");
4706
+ }
4707
+ const approvalData = await approveResponse.json();
4708
+ if (approvalData.approved) {
4709
+ return;
4710
+ }
4711
+ if (approvalData.requiresTransaction && approvalData.transaction) {
4712
+ setFundStatus(`Approving ${token} (requires on-chain transaction)...`);
4713
+ const executeApprovalResponse = await fetch("/api/compass/approval/execute", {
4714
+ method: "POST",
4715
+ headers: { "Content-Type": "application/json" },
4716
+ body: JSON.stringify({
4717
+ owner: address,
4718
+ chain: CHAIN_ID,
4719
+ transaction: approvalData.transaction
4720
+ })
4721
+ });
4722
+ if (!executeApprovalResponse.ok) {
4723
+ const errData = await executeApprovalResponse.json();
4724
+ throw new Error(errData.error || "Approval transaction failed");
4725
+ }
4726
+ return;
4727
+ }
4728
+ if (approvalData.eip712) {
4729
+ setFundStatus(`Please approve ${token} for transfers...`);
4730
+ const approvalSignature = await signTypedData({
4731
+ domain: approvalData.domain,
4732
+ types: approvalData.normalizedTypes,
4733
+ primaryType: "Permit",
4734
+ message: approvalData.message
4735
+ });
4736
+ setFundStatus(`Setting up ${token} approval...`);
4737
+ const executeApprovalResponse = await fetch("/api/compass/transfer/execute", {
4738
+ method: "POST",
4739
+ headers: { "Content-Type": "application/json" },
4740
+ body: JSON.stringify({
4741
+ owner: address,
4742
+ chain: CHAIN_ID,
4743
+ eip712: approvalData.eip712,
4744
+ signature: approvalSignature
3987
4745
  })
3988
4746
  });
3989
4747
  if (!executeApprovalResponse.ok) {
@@ -4000,7 +4758,7 @@ function EarnAccount({
4000
4758
  setFundError(null);
4001
4759
  try {
4002
4760
  await ensureTokenApproval(fundToken);
4003
- if (needsSwap) {
4761
+ if (needsFundSwap) {
4004
4762
  setFundStatus(`Preparing ${fundToken} transfer...`);
4005
4763
  const transferPrepareResponse = await fetch("/api/compass/transfer/prepare", {
4006
4764
  method: "POST",
@@ -4089,32 +4847,233 @@ function EarnAccount({
4089
4847
  throw new Error(errData.error || "Swap failed");
4090
4848
  }
4091
4849
  } else {
4092
- setFundStatus("Preparing transfer...");
4093
- const prepareResponse = await fetch("/api/compass/transfer/prepare", {
4850
+ setFundStatus("Preparing transfer...");
4851
+ const prepareResponse = await fetch("/api/compass/transfer/prepare", {
4852
+ method: "POST",
4853
+ headers: { "Content-Type": "application/json" },
4854
+ body: JSON.stringify({
4855
+ owner: address,
4856
+ chain: CHAIN_ID,
4857
+ token: "USDC",
4858
+ amount: fundAmount,
4859
+ action: "DEPOSIT"
4860
+ })
4861
+ });
4862
+ if (!prepareResponse.ok) {
4863
+ const errData = await prepareResponse.json();
4864
+ throw new Error(errData.error || "Failed to prepare transfer");
4865
+ }
4866
+ const prepareData = await prepareResponse.json();
4867
+ setFundStatus("Please sign the transfer...");
4868
+ const signature = await signTypedData({
4869
+ domain: prepareData.domain,
4870
+ types: prepareData.normalizedTypes,
4871
+ primaryType: prepareData.primaryType,
4872
+ message: prepareData.message
4873
+ });
4874
+ setFundStatus("Transferring funds...");
4875
+ const executeResponse = await fetch("/api/compass/transfer/execute", {
4876
+ method: "POST",
4877
+ headers: { "Content-Type": "application/json" },
4878
+ body: JSON.stringify({
4879
+ owner: address,
4880
+ chain: CHAIN_ID,
4881
+ eip712: prepareData.eip712,
4882
+ signature
4883
+ })
4884
+ });
4885
+ if (!executeResponse.ok) {
4886
+ const errData = await executeResponse.json();
4887
+ throw new Error(errData.error || "Transfer failed");
4888
+ }
4889
+ }
4890
+ setFundStatus("Success! Funds added to savings.");
4891
+ setFundAmount("");
4892
+ queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
4893
+ queryClient.invalidateQueries({ queryKey: ["walletBalance"] });
4894
+ balancesQuery.refetch();
4895
+ setTimeout(() => {
4896
+ setIsFundModalOpen(false);
4897
+ setFundStatus("");
4898
+ setFundToken("USDC");
4899
+ }, 2e3);
4900
+ } catch (err) {
4901
+ setFundError(err instanceof Error ? err.message : "Transfer failed");
4902
+ setFundStatus("");
4903
+ } finally {
4904
+ setIsFunding(false);
4905
+ }
4906
+ }, [address, fundAmount, fundToken, needsFundSwap, signTypedData, queryClient, balancesQuery]);
4907
+ const handleAction = async () => {
4908
+ if (!market || !amount || parseFloat(amount) <= 0 || !address || !signTypedData) return;
4909
+ setIsProcessing(true);
4910
+ setError(null);
4911
+ const isDeposit = activeTab === "deposit";
4912
+ try {
4913
+ let txHash;
4914
+ if (needsActionSwap) {
4915
+ if (isDeposit) {
4916
+ setStatusMessage(`Getting swap quote...`);
4917
+ const quoteResponse = await fetch(
4918
+ `/api/compass/swap/quote?owner=${address}&chain=${CHAIN_ID}&tokenIn=${selectedToken}&tokenOut=USDC&amountIn=${amount}`
4919
+ );
4920
+ if (!quoteResponse.ok) {
4921
+ const errorData = await quoteResponse.json();
4922
+ throw new Error(errorData.error || "Failed to get swap quote");
4923
+ }
4924
+ const quoteData = await quoteResponse.json();
4925
+ const estimatedOutput = quoteData.estimatedAmountOut;
4926
+ if (!estimatedOutput || parseFloat(estimatedOutput) <= 0) {
4927
+ throw new Error("Invalid swap quote - no output amount");
4928
+ }
4929
+ const depositAmount = (parseFloat(estimatedOutput) * 0.99).toString();
4930
+ setStatusMessage(`Preparing swap and deposit...`);
4931
+ const bundleActions = [
4932
+ {
4933
+ body: {
4934
+ actionType: "V2_SWAP",
4935
+ tokenIn: selectedToken,
4936
+ tokenOut: "USDC",
4937
+ amountIn: amount,
4938
+ maxSlippagePercent: 1
4939
+ }
4940
+ },
4941
+ {
4942
+ body: {
4943
+ actionType: "V2_MANAGE",
4944
+ action: "DEPOSIT",
4945
+ venue: { type: "AAVE", token: "USDC" },
4946
+ amount: depositAmount
4947
+ }
4948
+ }
4949
+ ];
4950
+ const prepareResponse = await fetch("/api/compass/bundle/prepare", {
4951
+ method: "POST",
4952
+ headers: { "Content-Type": "application/json" },
4953
+ body: JSON.stringify({
4954
+ owner: address,
4955
+ chain: CHAIN_ID,
4956
+ actions: bundleActions
4957
+ })
4958
+ });
4959
+ if (!prepareResponse.ok) {
4960
+ const errorData = await prepareResponse.json();
4961
+ throw new Error(errorData.error || "Failed to prepare bundle");
4962
+ }
4963
+ const { eip712, normalizedTypes, domain, message } = await prepareResponse.json();
4964
+ setStatusMessage("Please sign the transaction...");
4965
+ const signature = await signTypedData({
4966
+ domain,
4967
+ types: normalizedTypes,
4968
+ primaryType: "SafeTx",
4969
+ message
4970
+ });
4971
+ setStatusMessage("Executing swap and deposit...");
4972
+ const executeResponse = await fetch("/api/compass/bundle/execute", {
4973
+ method: "POST",
4974
+ headers: { "Content-Type": "application/json" },
4975
+ body: JSON.stringify({
4976
+ owner: address,
4977
+ eip712,
4978
+ signature,
4979
+ chain: CHAIN_ID
4980
+ })
4981
+ });
4982
+ if (!executeResponse.ok) {
4983
+ const errorData = await executeResponse.json();
4984
+ throw new Error(errorData.error || "Failed to execute bundle");
4985
+ }
4986
+ const result = await executeResponse.json();
4987
+ txHash = result.txHash;
4988
+ } else {
4989
+ setStatusMessage(`Preparing withdraw and swap...`);
4990
+ const bundleActions = [
4991
+ {
4992
+ body: {
4993
+ actionType: "V2_MANAGE",
4994
+ action: "WITHDRAW",
4995
+ venue: { type: "AAVE", token: "USDC" },
4996
+ amount
4997
+ }
4998
+ },
4999
+ {
5000
+ body: {
5001
+ actionType: "V2_SWAP",
5002
+ tokenIn: "USDC",
5003
+ tokenOut: selectedToken,
5004
+ amountIn: amount,
5005
+ maxSlippagePercent: 1
5006
+ }
5007
+ }
5008
+ ];
5009
+ const prepareResponse = await fetch("/api/compass/bundle/prepare", {
5010
+ method: "POST",
5011
+ headers: { "Content-Type": "application/json" },
5012
+ body: JSON.stringify({
5013
+ owner: address,
5014
+ chain: CHAIN_ID,
5015
+ actions: bundleActions
5016
+ })
5017
+ });
5018
+ if (!prepareResponse.ok) {
5019
+ const errorData = await prepareResponse.json();
5020
+ throw new Error(errorData.error || "Failed to prepare bundle");
5021
+ }
5022
+ const { eip712, normalizedTypes, domain, message } = await prepareResponse.json();
5023
+ setStatusMessage("Please sign the transaction...");
5024
+ const signature = await signTypedData({
5025
+ domain,
5026
+ types: normalizedTypes,
5027
+ primaryType: "SafeTx",
5028
+ message
5029
+ });
5030
+ setStatusMessage("Executing withdraw and swap...");
5031
+ const executeResponse = await fetch("/api/compass/bundle/execute", {
5032
+ method: "POST",
5033
+ headers: { "Content-Type": "application/json" },
5034
+ body: JSON.stringify({
5035
+ owner: address,
5036
+ eip712,
5037
+ signature,
5038
+ chain: CHAIN_ID
5039
+ })
5040
+ });
5041
+ if (!executeResponse.ok) {
5042
+ const errorData = await executeResponse.json();
5043
+ throw new Error(errorData.error || "Failed to execute bundle");
5044
+ }
5045
+ const result = await executeResponse.json();
5046
+ txHash = result.txHash;
5047
+ }
5048
+ } else {
5049
+ setStatusMessage(isDeposit ? "Preparing deposit..." : "Preparing withdrawal...");
5050
+ const prepareEndpoint = isDeposit ? "/api/compass/deposit/prepare" : "/api/compass/withdraw/prepare";
5051
+ const executeEndpoint = isDeposit ? "/api/compass/deposit/execute" : "/api/compass/withdraw/execute";
5052
+ const prepareResponse = await fetch(prepareEndpoint, {
4094
5053
  method: "POST",
4095
5054
  headers: { "Content-Type": "application/json" },
4096
5055
  body: JSON.stringify({
4097
5056
  owner: address,
4098
5057
  chain: CHAIN_ID,
5058
+ venueType: "AAVE",
4099
5059
  token: "USDC",
4100
- amount: fundAmount,
4101
- action: "DEPOSIT"
5060
+ amount
4102
5061
  })
4103
5062
  });
4104
5063
  if (!prepareResponse.ok) {
4105
5064
  const errData = await prepareResponse.json();
4106
- throw new Error(errData.error || "Failed to prepare transfer");
5065
+ throw new Error(errData.error || "Failed to prepare transaction");
4107
5066
  }
4108
5067
  const prepareData = await prepareResponse.json();
4109
- setFundStatus("Please sign the transfer...");
5068
+ setStatusMessage("Please sign the transaction...");
4110
5069
  const signature = await signTypedData({
4111
5070
  domain: prepareData.domain,
4112
5071
  types: prepareData.normalizedTypes,
4113
- primaryType: prepareData.primaryType,
5072
+ primaryType: "SafeTx",
4114
5073
  message: prepareData.message
4115
5074
  });
4116
- setFundStatus("Transferring funds...");
4117
- const executeResponse = await fetch("/api/compass/transfer/execute", {
5075
+ setStatusMessage("Executing transaction...");
5076
+ const executeResponse = await fetch(executeEndpoint, {
4118
5077
  method: "POST",
4119
5078
  headers: { "Content-Type": "application/json" },
4120
5079
  body: JSON.stringify({
@@ -4126,74 +5085,11 @@ function EarnAccount({
4126
5085
  });
4127
5086
  if (!executeResponse.ok) {
4128
5087
  const errData = await executeResponse.json();
4129
- throw new Error(errData.error || "Transfer failed");
5088
+ throw new Error(errData.error || "Transaction failed");
4130
5089
  }
5090
+ const result = await executeResponse.json();
5091
+ txHash = result.txHash;
4131
5092
  }
4132
- setFundStatus("Success! Funds added to savings.");
4133
- setFundAmount("");
4134
- queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
4135
- queryClient.invalidateQueries({ queryKey: ["walletBalance"] });
4136
- balancesQuery.refetch();
4137
- setTimeout(() => {
4138
- setIsFundModalOpen(false);
4139
- setFundStatus("");
4140
- setFundToken("USDC");
4141
- }, 2e3);
4142
- } catch (err) {
4143
- setFundError(err instanceof Error ? err.message : "Transfer failed");
4144
- setFundStatus("");
4145
- } finally {
4146
- setIsFunding(false);
4147
- }
4148
- }, [address, fundAmount, fundToken, needsSwap, signTypedData, queryClient, balancesQuery]);
4149
- const handleAction = async () => {
4150
- if (!market || !amount || parseFloat(amount) <= 0 || !address || !signTypedData) return;
4151
- setIsProcessing(true);
4152
- setError(null);
4153
- setStatusMessage(activeTab === "deposit" ? "Preparing deposit..." : "Preparing withdrawal...");
4154
- try {
4155
- const isDeposit = activeTab === "deposit";
4156
- const prepareEndpoint = isDeposit ? "/api/compass/deposit/prepare" : "/api/compass/withdraw/prepare";
4157
- const executeEndpoint = isDeposit ? "/api/compass/deposit/execute" : "/api/compass/withdraw/execute";
4158
- const prepareResponse = await fetch(prepareEndpoint, {
4159
- method: "POST",
4160
- headers: { "Content-Type": "application/json" },
4161
- body: JSON.stringify({
4162
- owner: address,
4163
- chain: CHAIN_ID,
4164
- venueType: "AAVE",
4165
- token: "USDC",
4166
- amount
4167
- })
4168
- });
4169
- if (!prepareResponse.ok) {
4170
- const errData = await prepareResponse.json();
4171
- throw new Error(errData.error || "Failed to prepare transaction");
4172
- }
4173
- const prepareData = await prepareResponse.json();
4174
- setStatusMessage("Please sign the transaction...");
4175
- const signature = await signTypedData({
4176
- domain: prepareData.domain,
4177
- types: prepareData.normalizedTypes,
4178
- primaryType: "SafeTx",
4179
- message: prepareData.message
4180
- });
4181
- setStatusMessage("Executing transaction...");
4182
- const executeResponse = await fetch(executeEndpoint, {
4183
- method: "POST",
4184
- headers: { "Content-Type": "application/json" },
4185
- body: JSON.stringify({
4186
- owner: address,
4187
- chain: CHAIN_ID,
4188
- eip712: prepareData.eip712,
4189
- signature
4190
- })
4191
- });
4192
- if (!executeResponse.ok) {
4193
- const errData = await executeResponse.json();
4194
- throw new Error(errData.error || "Transaction failed");
4195
- }
4196
- const { txHash } = await executeResponse.json();
4197
5093
  setStatusMessage("Transaction successful!");
4198
5094
  setAmount("");
4199
5095
  queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
@@ -4208,10 +5104,24 @@ function EarnAccount({
4208
5104
  borrowApy: null,
4209
5105
  tvlUsd: null
4210
5106
  };
4211
- if (activeTab === "deposit") {
4212
- onSupply?.(marketData, amount, txHash || "");
5107
+ if (isDeposit) {
5108
+ const depositMarket = {
5109
+ id: "aave-usdc",
5110
+ name: "USDC",
5111
+ apy: parseFloat(market.supplyApy || "0"),
5112
+ tvl: 0,
5113
+ type: "aave"
5114
+ };
5115
+ onDeposit?.(depositMarket, parseFloat(amount), txHash || "");
4213
5116
  } else {
4214
- onWithdraw?.(marketData, amount, txHash || "");
5117
+ const withdrawMarket = {
5118
+ id: "aave-usdc",
5119
+ name: "USDC",
5120
+ apy: parseFloat(market.supplyApy || "0"),
5121
+ tvl: 0,
5122
+ type: "aave"
5123
+ };
5124
+ onWithdraw?.(withdrawMarket, parseFloat(amount), txHash || "");
4215
5125
  }
4216
5126
  setTimeout(() => setStatusMessage(""), 3e3);
4217
5127
  } catch (err) {
@@ -4221,7 +5131,7 @@ function EarnAccount({
4221
5131
  setIsProcessing(false);
4222
5132
  }
4223
5133
  };
4224
- const maxAmount = activeTab === "deposit" ? available : depositedBalance;
5134
+ const maxAmount = activeTab === "deposit" ? parseFloat(selectedTokenBalance) : depositedBalance;
4225
5135
  if (marketQuery.isLoading) {
4226
5136
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-16", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 32, className: "animate-spin", style: { color: "var(--compass-color-primary)" } }) });
4227
5137
  }
@@ -4240,7 +5150,7 @@ function EarnAccount({
4240
5150
  }
4241
5151
  const spacing = compact ? "calc(var(--compass-spacing-unit) * 0.75)" : "var(--compass-spacing-card)";
4242
5152
  const gapSize = compact ? "calc(var(--compass-spacing-unit) * 0.75)" : "calc(var(--compass-spacing-unit) * 1.5)";
4243
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: gapSize, fontFamily: "var(--compass-font-family)" }, children: [
5153
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col w-full", style: { gap: gapSize, fontFamily: "var(--compass-font-family)" }, children: [
4244
5154
  showHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
4245
5155
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
4246
5156
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -4276,28 +5186,39 @@ function EarnAccount({
4276
5186
  padding: compact ? "var(--compass-spacing-card)" : "calc(var(--compass-spacing-card) * 1.25)"
4277
5187
  },
4278
5188
  children: [
4279
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
4280
- /* @__PURE__ */ jsxRuntime.jsx(
4281
- "span",
4282
- {
4283
- className: "text-xs font-medium uppercase tracking-wide",
4284
- style: { color: "var(--compass-color-text-tertiary)" },
4285
- children: "Total Balance"
4286
- }
4287
- ),
4288
- /* @__PURE__ */ jsxRuntime.jsx(
4289
- "span",
4290
- {
4291
- className: "font-bold",
4292
- style: {
4293
- color: "var(--compass-color-text)",
4294
- fontSize: compact ? "2rem" : "2.5rem",
4295
- lineHeight: "1"
4296
- },
4297
- children: formatCurrency(totalBalance)
4298
- }
4299
- )
4300
- ] }),
5189
+ /* @__PURE__ */ jsxRuntime.jsxs(
5190
+ "button",
5191
+ {
5192
+ onClick: () => setIsBalancesModalOpen(true),
5193
+ className: "flex flex-col text-left w-full transition-opacity hover:opacity-80",
5194
+ style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" },
5195
+ children: [
5196
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
5197
+ /* @__PURE__ */ jsxRuntime.jsx(
5198
+ "span",
5199
+ {
5200
+ className: "text-xs font-medium uppercase tracking-wide",
5201
+ style: { color: "var(--compass-color-text-tertiary)" },
5202
+ children: "Total Balance"
5203
+ }
5204
+ ),
5205
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 12, style: { color: "var(--compass-color-text-tertiary)" } })
5206
+ ] }),
5207
+ /* @__PURE__ */ jsxRuntime.jsx(
5208
+ "span",
5209
+ {
5210
+ className: "font-bold",
5211
+ style: {
5212
+ color: "var(--compass-color-text)",
5213
+ fontSize: compact ? "2rem" : "2.5rem",
5214
+ lineHeight: "1"
5215
+ },
5216
+ children: formatCurrency(totalBalance)
5217
+ }
5218
+ )
5219
+ ]
5220
+ }
5221
+ ),
4301
5222
  /* @__PURE__ */ jsxRuntime.jsx(
4302
5223
  "div",
4303
5224
  {
@@ -4354,48 +5275,61 @@ function EarnAccount({
4354
5275
  ]
4355
5276
  }
4356
5277
  ),
4357
- showPnL && earnings > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
4358
- "div",
5278
+ positions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
5279
+ "button",
4359
5280
  {
4360
- className: "flex items-center justify-between",
5281
+ onClick: () => setIsEarningsModalOpen(true),
5282
+ className: "w-full flex items-center justify-between transition-opacity hover:opacity-80",
4361
5283
  style: {
4362
5284
  backgroundColor: "var(--compass-color-surface)",
4363
5285
  border: "1px solid var(--compass-color-border)",
4364
5286
  borderRadius: "var(--compass-border-radius-lg)",
4365
- padding: spacing
5287
+ padding: compact ? "calc(var(--compass-spacing-unit) * 0.75)" : "var(--compass-spacing-card)"
4366
5288
  },
4367
5289
  children: [
4368
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-secondary)" }, children: "Total Earnings" }),
4369
- /* @__PURE__ */ jsxRuntime.jsxs(
4370
- "span",
4371
- {
4372
- className: "font-semibold",
4373
- style: { color: "var(--compass-color-success)" },
4374
- children: [
4375
- "+",
4376
- formatCurrency(earnings)
4377
- ]
4378
- }
4379
- )
5290
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
5291
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "var(--compass-color-text)" }, children: [
5292
+ positions.length,
5293
+ " ",
5294
+ positions.length === 1 ? "position" : "positions"
5295
+ ] }),
5296
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-tertiary)" }, children: "|" }),
5297
+ /* @__PURE__ */ jsxRuntime.jsxs(
5298
+ "span",
5299
+ {
5300
+ style: {
5301
+ color: totalEarned >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)",
5302
+ fontWeight: 500
5303
+ },
5304
+ children: [
5305
+ totalEarned >= 0 ? "+" : "",
5306
+ "$",
5307
+ totalEarned.toFixed(2),
5308
+ " earned"
5309
+ ]
5310
+ }
5311
+ )
5312
+ ] }),
5313
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 16, style: { color: "var(--compass-color-text-tertiary)" } })
4380
5314
  ]
4381
5315
  }
4382
5316
  ),
4383
- userPosition?.pnl && /* @__PURE__ */ jsxRuntime.jsx(
4384
- PnLSummary,
5317
+ /* @__PURE__ */ jsxRuntime.jsx(
5318
+ MarketSelector,
4385
5319
  {
4386
- pnl: userPosition.pnl,
4387
- tokenSymbol: "USDC",
4388
- tokenPrice: 1
5320
+ markets: mockMarkets,
5321
+ selectedMarket,
5322
+ onMarketSelect: setSelectedMarket,
5323
+ marketType,
5324
+ onMarketTypeChange: (type) => {
5325
+ setMarketType(type);
5326
+ setSelectedMarket(null);
5327
+ },
5328
+ minTvl,
5329
+ onMinTvlChange: setMinTvl,
5330
+ isLoading: marketQuery.isLoading
4389
5331
  }
4390
5332
  ),
4391
- userPosition?.deposits?.length || userPosition?.withdrawals?.length ? /* @__PURE__ */ jsxRuntime.jsx(
4392
- TransactionHistory,
4393
- {
4394
- deposits: userPosition.deposits,
4395
- withdrawals: userPosition.withdrawals,
4396
- tokenSymbol: "USDC"
4397
- }
4398
- ) : null,
4399
5333
  /* @__PURE__ */ jsxRuntime.jsxs(
4400
5334
  "div",
4401
5335
  {
@@ -4412,6 +5346,7 @@ function EarnAccount({
4412
5346
  {
4413
5347
  onClick: () => {
4414
5348
  setActiveTab("deposit");
5349
+ setSelectedToken("USDC");
4415
5350
  setError(null);
4416
5351
  },
4417
5352
  className: "flex-1 flex items-center justify-center font-medium transition-all",
@@ -4435,6 +5370,7 @@ function EarnAccount({
4435
5370
  {
4436
5371
  onClick: () => {
4437
5372
  setActiveTab("withdraw");
5373
+ setSelectedToken("USDC");
4438
5374
  setError(null);
4439
5375
  },
4440
5376
  className: "flex-1 flex items-center justify-center font-medium transition-all",
@@ -4460,22 +5396,15 @@ function EarnAccount({
4460
5396
  /* @__PURE__ */ jsxRuntime.jsxs(
4461
5397
  "div",
4462
5398
  {
4463
- className: "flex items-center",
5399
+ className: "flex items-center flex-wrap",
4464
5400
  style: {
4465
5401
  backgroundColor: "var(--compass-color-surface)",
4466
5402
  border: "1px solid var(--compass-color-border)",
4467
5403
  borderRadius: "var(--compass-border-radius-lg)",
4468
- padding: spacing
5404
+ padding: spacing,
5405
+ gap: "calc(var(--compass-spacing-unit) * 0.5)"
4469
5406
  },
4470
5407
  children: [
4471
- /* @__PURE__ */ jsxRuntime.jsx(
4472
- "span",
4473
- {
4474
- className: "font-medium",
4475
- style: { color: "var(--compass-color-text-secondary)", fontSize: compact ? "1.25rem" : "1.5rem", marginRight: "calc(var(--compass-spacing-unit) * 0.25)" },
4476
- children: "$"
4477
- }
4478
- ),
4479
5408
  /* @__PURE__ */ jsxRuntime.jsx(
4480
5409
  "input",
4481
5410
  {
@@ -4488,9 +5417,64 @@ function EarnAccount({
4488
5417
  placeholder: "0.00",
4489
5418
  disabled: isProcessing,
4490
5419
  className: "flex-1 font-medium bg-transparent outline-none",
4491
- style: { color: "var(--compass-color-text)", fontSize: compact ? "1.25rem" : "1.5rem" }
5420
+ style: { color: "var(--compass-color-text)", fontSize: compact ? "1.25rem" : "1.5rem", minWidth: 0 }
4492
5421
  }
4493
5422
  ),
5423
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", style: { flexShrink: 0 }, children: [
5424
+ /* @__PURE__ */ jsxRuntime.jsxs(
5425
+ "button",
5426
+ {
5427
+ onClick: () => setIsTokenDropdownOpen(!isTokenDropdownOpen),
5428
+ disabled: isProcessing,
5429
+ className: "flex items-center font-medium",
5430
+ style: {
5431
+ backgroundColor: "var(--compass-color-surface-elevated, var(--compass-color-background))",
5432
+ border: "1px solid var(--compass-color-border)",
5433
+ color: "var(--compass-color-text)",
5434
+ borderRadius: "var(--compass-border-radius-md)",
5435
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 0.75)",
5436
+ gap: "calc(var(--compass-spacing-unit) * 0.25)",
5437
+ fontSize: compact ? "0.875rem" : "1rem"
5438
+ },
5439
+ children: [
5440
+ selectedToken,
5441
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: compact ? 14 : 16, style: { color: "var(--compass-color-text-tertiary)" } })
5442
+ ]
5443
+ }
5444
+ ),
5445
+ isTokenDropdownOpen && /* @__PURE__ */ jsxRuntime.jsx(
5446
+ "div",
5447
+ {
5448
+ className: "absolute right-0 mt-1 z-10",
5449
+ style: {
5450
+ backgroundColor: "var(--compass-color-surface)",
5451
+ border: "1px solid var(--compass-color-border)",
5452
+ borderRadius: "var(--compass-border-radius-lg)",
5453
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
5454
+ minWidth: "100px"
5455
+ },
5456
+ children: SUPPORTED_TOKENS.map((token) => /* @__PURE__ */ jsxRuntime.jsx(
5457
+ "button",
5458
+ {
5459
+ onClick: () => {
5460
+ setSelectedToken(token);
5461
+ setIsTokenDropdownOpen(false);
5462
+ setError(null);
5463
+ },
5464
+ className: "w-full text-left font-medium transition-colors",
5465
+ style: {
5466
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 0.75)",
5467
+ color: token === selectedToken ? "var(--compass-color-primary)" : "var(--compass-color-text)",
5468
+ backgroundColor: token === selectedToken ? "var(--compass-color-primary-muted, rgba(99, 102, 241, 0.1))" : "transparent",
5469
+ fontSize: compact ? "0.875rem" : "1rem"
5470
+ },
5471
+ children: token
5472
+ },
5473
+ token
5474
+ ))
5475
+ }
5476
+ )
5477
+ ] }),
4494
5478
  /* @__PURE__ */ jsxRuntime.jsx(
4495
5479
  "button",
4496
5480
  {
@@ -4502,7 +5486,8 @@ function EarnAccount({
4502
5486
  border: "1px solid var(--compass-color-border)",
4503
5487
  color: "var(--compass-color-text-secondary)",
4504
5488
  borderRadius: "var(--compass-border-radius-md)",
4505
- padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 0.75)"
5489
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 0.75)",
5490
+ flexShrink: 0
4506
5491
  },
4507
5492
  children: "MAX"
4508
5493
  }
@@ -4510,9 +5495,30 @@ function EarnAccount({
4510
5495
  ]
4511
5496
  }
4512
5497
  ),
5498
+ needsActionSwap && /* @__PURE__ */ jsxRuntime.jsxs(
5499
+ "div",
5500
+ {
5501
+ className: "flex items-center text-sm",
5502
+ style: {
5503
+ backgroundColor: "var(--compass-color-primary-muted, rgba(99, 102, 241, 0.1))",
5504
+ color: "var(--compass-color-primary)",
5505
+ borderRadius: "var(--compass-border-radius-lg)",
5506
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 0.75)",
5507
+ gap: "calc(var(--compass-spacing-unit) * 0.5)"
5508
+ },
5509
+ children: [
5510
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { size: 14 }),
5511
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: activeTab === "deposit" ? `Swaps ${selectedToken} to USDC, then deposits` : `Withdraws USDC, then swaps to ${selectedToken}` })
5512
+ ]
5513
+ }
5514
+ ),
4513
5515
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", style: { padding: "0 calc(var(--compass-spacing-unit) * 0.25)" }, children: [
4514
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", style: { color: "var(--compass-color-text-tertiary)" }, children: "Available balance" }),
4515
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", style: { color: "var(--compass-color-text-secondary)" }, children: formatCurrency(maxAmount) })
5516
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", style: { color: "var(--compass-color-text-tertiary)" }, children: activeTab === "deposit" ? `Available ${selectedToken}` : "Available to withdraw" }),
5517
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium", style: { color: "var(--compass-color-text-secondary)" }, children: [
5518
+ formatAmount2(maxAmount),
5519
+ " ",
5520
+ activeTab === "deposit" ? selectedToken : "USDC"
5521
+ ] })
4516
5522
  ] })
4517
5523
  ] }),
4518
5524
  error && /* @__PURE__ */ jsxRuntime.jsx(
@@ -4545,7 +5551,7 @@ function EarnAccount({
4545
5551
  children: isProcessing ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center justify-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
4546
5552
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: compact ? 16 : 20, className: "animate-spin" }),
4547
5553
  "Processing..."
4548
- ] }) : activeTab === "deposit" ? "Deposit funds" : "Withdraw funds"
5554
+ ] }) : needsActionSwap ? activeTab === "deposit" ? `Swap & Deposit` : `Withdraw & Swap` : activeTab === "deposit" ? "Deposit funds" : "Withdraw funds"
4549
5555
  }
4550
5556
  ),
4551
5557
  statusMessage && !error && /* @__PURE__ */ jsxRuntime.jsx(
@@ -4582,7 +5588,7 @@ function EarnAccount({
4582
5588
  ]
4583
5589
  }
4584
5590
  ),
4585
- showFundButton && /* @__PURE__ */ jsxRuntime.jsxs(
5591
+ showTopUpButton && /* @__PURE__ */ jsxRuntime.jsxs(
4586
5592
  "button",
4587
5593
  {
4588
5594
  onClick: () => setIsFundModalOpen(true),
@@ -4598,7 +5604,7 @@ function EarnAccount({
4598
5604
  },
4599
5605
  children: [
4600
5606
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Wallet, { size: compact ? 16 : 18 }),
4601
- "Transfer from wallet"
5607
+ "Top Up"
4602
5608
  ]
4603
5609
  }
4604
5610
  )
@@ -4666,7 +5672,7 @@ function EarnAccount({
4666
5672
  }
4667
5673
  )
4668
5674
  ] }),
4669
- needsSwap && /* @__PURE__ */ jsxRuntime.jsxs(
5675
+ needsFundSwap && /* @__PURE__ */ jsxRuntime.jsxs(
4670
5676
  "div",
4671
5677
  {
4672
5678
  className: "flex items-center gap-2 p-3 rounded-xl text-sm",
@@ -4778,7 +5784,7 @@ function EarnAccount({
4778
5784
  "Processing..."
4779
5785
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center justify-center gap-2", children: [
4780
5786
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownLeft, { size: 20 }),
4781
- needsSwap ? `Swap ${fundToken} & Transfer` : "Transfer to Savings"
5787
+ needsFundSwap ? `Swap ${fundToken} & Transfer` : "Transfer to Savings"
4782
5788
  ] })
4783
5789
  }
4784
5790
  ),
@@ -4792,6 +5798,125 @@ function EarnAccount({
4792
5798
  )
4793
5799
  ] })
4794
5800
  }
5801
+ ),
5802
+ /* @__PURE__ */ jsxRuntime.jsx(
5803
+ ActionModal,
5804
+ {
5805
+ isOpen: isBalancesModalOpen,
5806
+ onClose: () => setIsBalancesModalOpen(false),
5807
+ title: "Balance Breakdown",
5808
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
5809
+ balancesQuery.data?.balances && Object.keys(balancesQuery.data.balances).length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5810
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
5811
+ /* @__PURE__ */ jsxRuntime.jsx(
5812
+ "span",
5813
+ {
5814
+ className: "text-xs font-medium uppercase tracking-wide",
5815
+ style: { color: "var(--compass-color-text-tertiary)" },
5816
+ children: "Available in Account"
5817
+ }
5818
+ ),
5819
+ Object.entries(balancesQuery.data.balances).map(([symbol, data]) => /* @__PURE__ */ jsxRuntime.jsxs(
5820
+ "div",
5821
+ {
5822
+ className: "flex items-center justify-between p-3 rounded-lg",
5823
+ style: {
5824
+ backgroundColor: "var(--compass-color-surface)",
5825
+ border: "1px solid var(--compass-color-border)"
5826
+ },
5827
+ children: [
5828
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", style: { color: "var(--compass-color-text)" }, children: symbol }),
5829
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-end", children: [
5830
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono font-medium", style: { color: "var(--compass-color-text)" }, children: formatAmount2(data.balance) }),
5831
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: formatCurrency(data.usdValue) })
5832
+ ] })
5833
+ ]
5834
+ },
5835
+ symbol
5836
+ ))
5837
+ ] }),
5838
+ /* @__PURE__ */ jsxRuntime.jsxs(
5839
+ "div",
5840
+ {
5841
+ className: "flex items-center justify-between pt-2",
5842
+ style: { borderTop: "1px solid var(--compass-color-border)" },
5843
+ children: [
5844
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", style: { color: "var(--compass-color-text-secondary)" }, children: "Subtotal" }),
5845
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", style: { color: "var(--compass-color-text)" }, children: formatCurrency(earnAccountTotalUsd) })
5846
+ ]
5847
+ }
5848
+ )
5849
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
5850
+ "div",
5851
+ {
5852
+ className: "text-center py-4",
5853
+ style: { color: "var(--compass-color-text-tertiary)" },
5854
+ children: "No tokens in account"
5855
+ }
5856
+ ),
5857
+ depositedBalance > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 pt-2", style: { borderTop: "1px solid var(--compass-color-border)" }, children: [
5858
+ /* @__PURE__ */ jsxRuntime.jsx(
5859
+ "span",
5860
+ {
5861
+ className: "text-xs font-medium uppercase tracking-wide",
5862
+ style: { color: "var(--compass-color-text-tertiary)" },
5863
+ children: "Earning Interest (Aave)"
5864
+ }
5865
+ ),
5866
+ /* @__PURE__ */ jsxRuntime.jsxs(
5867
+ "div",
5868
+ {
5869
+ className: "flex items-center justify-between p-3 rounded-lg",
5870
+ style: {
5871
+ backgroundColor: "var(--compass-color-success-muted, rgba(34, 197, 94, 0.1))",
5872
+ border: "1px solid var(--compass-color-success)"
5873
+ },
5874
+ children: [
5875
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
5876
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { size: 16, style: { color: "var(--compass-color-success)" } }),
5877
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", style: { color: "var(--compass-color-text)" }, children: "USDC" })
5878
+ ] }),
5879
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-end", children: [
5880
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono font-medium", style: { color: "var(--compass-color-text)" }, children: formatAmount2(depositedBalance) }),
5881
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs", style: { color: "var(--compass-color-success)" }, children: [
5882
+ formatAPY3(market?.supplyApy || null),
5883
+ "% APY"
5884
+ ] })
5885
+ ] })
5886
+ ]
5887
+ }
5888
+ )
5889
+ ] }),
5890
+ /* @__PURE__ */ jsxRuntime.jsxs(
5891
+ "div",
5892
+ {
5893
+ className: "flex items-center justify-between pt-3 mt-2",
5894
+ style: { borderTop: "2px solid var(--compass-color-border)" },
5895
+ children: [
5896
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", style: { color: "var(--compass-color-text)" }, children: "Total Balance" }),
5897
+ /* @__PURE__ */ jsxRuntime.jsx(
5898
+ "span",
5899
+ {
5900
+ className: "font-bold text-xl",
5901
+ style: { color: "var(--compass-color-text)" },
5902
+ children: formatCurrency(totalBalance)
5903
+ }
5904
+ )
5905
+ ]
5906
+ }
5907
+ )
5908
+ ] })
5909
+ }
5910
+ ),
5911
+ /* @__PURE__ */ jsxRuntime.jsx(
5912
+ EarningsModal,
5913
+ {
5914
+ isOpen: isEarningsModalOpen,
5915
+ onClose: () => setIsEarningsModalOpen(false),
5916
+ positions,
5917
+ totalEarned,
5918
+ isLoading: positionQuery.isLoading
5919
+ }
4795
5920
  )
4796
5921
  ] });
4797
5922
  }
@@ -5804,7 +6929,7 @@ function usePositionsData() {
5804
6929
  refetch: positionsQuery.refetch
5805
6930
  };
5806
6931
  }
5807
- function PositionCard({
6932
+ function PositionCard2({
5808
6933
  position,
5809
6934
  showApy = true,
5810
6935
  showPnL = true,
@@ -5979,7 +7104,7 @@ function VenueSection({
5979
7104
  children: [
5980
7105
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-3" }),
5981
7106
  venuePositions.positions.map((position) => /* @__PURE__ */ jsxRuntime.jsx(
5982
- PositionCard,
7107
+ PositionCard2,
5983
7108
  {
5984
7109
  position,
5985
7110
  showApy,