@compass-labs/widgets 0.1.32 → 0.1.33

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.mjs CHANGED
@@ -3,7 +3,7 @@ import { useQueryClient, useQuery, QueryClient, QueryClientProvider } from '@tan
3
3
  import { CompassApiSDK } from '@compass-labs/api-sdk';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
  import { arbitrum, base, mainnet } from 'viem/chains';
6
- import { Wallet, Loader2, ArrowDownLeft, ArrowUpRight, Coins, X, ChevronDown, LogOut, AlertCircle, ArrowRight, ExternalLink, Plus, ChevronRight, Minus, Shield, CheckCircle, ArrowDownUp, AlertTriangle, Check, RotateCcw, Equal, ChevronUp, Inbox, Percent, Clock, Calendar, TrendingUp, TrendingDown } from 'lucide-react';
6
+ import { Wallet, Loader2, ArrowDownLeft, ArrowUpRight, X, ChevronDown, LogOut, AlertCircle, ArrowRight, ExternalLink, ArrowDownUp, ArrowLeftRight, Plus, ChevronRight, Minus, Shield, CheckCircle, AlertTriangle, Check, RotateCcw, Equal, ChevronUp, Inbox, Percent, Clock, Calendar, TrendingUp, TrendingDown } from 'lucide-react';
7
7
 
8
8
  // src/provider/CompassProvider.tsx
9
9
  var ApiContext = createContext(null);
@@ -1109,68 +1109,75 @@ function ActionModal({ isOpen, onClose, title, children }) {
1109
1109
  };
1110
1110
  }, [isOpen, onClose]);
1111
1111
  if (!isOpen) return null;
1112
- return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
1113
- /* @__PURE__ */ jsx(
1114
- "div",
1115
- {
1116
- className: "absolute inset-0",
1117
- style: { backgroundColor: "var(--compass-color-overlay)" },
1118
- onClick: onClose
1119
- }
1120
- ),
1121
- /* @__PURE__ */ jsxs(
1122
- "div",
1123
- {
1124
- className: "relative w-full max-w-md mx-4 overflow-hidden",
1125
- style: {
1126
- backgroundColor: "var(--compass-color-surface)",
1127
- boxShadow: "var(--compass-shadow-lg)",
1128
- borderRadius: "var(--compass-border-radius-xl)",
1129
- fontFamily: "var(--compass-font-family)"
1130
- },
1131
- children: [
1132
- /* @__PURE__ */ jsxs(
1133
- "div",
1134
- {
1135
- className: "flex items-center justify-between border-b",
1136
- style: {
1137
- borderColor: "var(--compass-color-border)",
1138
- padding: "calc(var(--compass-spacing-card) * 0.75) var(--compass-spacing-card)"
1139
- },
1140
- children: [
1141
- /* @__PURE__ */ jsx(
1142
- "h2",
1143
- {
1144
- className: "font-semibold",
1145
- style: {
1146
- fontSize: "var(--compass-font-size-subheading)",
1147
- color: "var(--compass-color-text)"
1148
- },
1149
- children: title
1150
- }
1151
- ),
1152
- /* @__PURE__ */ jsx(
1153
- "button",
1154
- {
1155
- onClick: onClose,
1156
- className: "hover:opacity-70",
1157
- style: {
1158
- color: "var(--compass-color-text-secondary)",
1159
- borderRadius: "var(--compass-border-radius-md)",
1160
- padding: "calc(var(--compass-spacing-unit) * 0.25)",
1161
- transition: "var(--compass-transition-normal)"
1162
- },
1163
- children: /* @__PURE__ */ jsx(X, { size: 20 })
1164
- }
1165
- )
1166
- ]
1167
- }
1168
- ),
1169
- /* @__PURE__ */ jsx("div", { style: { padding: "var(--compass-spacing-card)" }, children })
1170
- ]
1171
- }
1172
- )
1173
- ] });
1112
+ return /* @__PURE__ */ jsxs(
1113
+ "div",
1114
+ {
1115
+ className: "fixed inset-0 z-50 flex items-center justify-center p-4",
1116
+ style: { overflowY: "auto", scrollbarWidth: "none" },
1117
+ children: [
1118
+ /* @__PURE__ */ jsx(
1119
+ "div",
1120
+ {
1121
+ className: "absolute inset-0",
1122
+ style: { backgroundColor: "var(--compass-color-overlay)" },
1123
+ onClick: onClose
1124
+ }
1125
+ ),
1126
+ /* @__PURE__ */ jsxs(
1127
+ "div",
1128
+ {
1129
+ className: "relative w-full max-w-md",
1130
+ style: {
1131
+ backgroundColor: "var(--compass-color-surface)",
1132
+ boxShadow: "var(--compass-shadow-lg)",
1133
+ borderRadius: "var(--compass-border-radius-xl)",
1134
+ fontFamily: "var(--compass-font-family)"
1135
+ },
1136
+ children: [
1137
+ /* @__PURE__ */ jsxs(
1138
+ "div",
1139
+ {
1140
+ className: "flex items-center justify-between border-b",
1141
+ style: {
1142
+ borderColor: "var(--compass-color-border)",
1143
+ padding: "calc(var(--compass-spacing-card) * 0.75) var(--compass-spacing-card)"
1144
+ },
1145
+ children: [
1146
+ /* @__PURE__ */ jsx(
1147
+ "h2",
1148
+ {
1149
+ className: "font-semibold",
1150
+ style: {
1151
+ fontSize: "var(--compass-font-size-subheading)",
1152
+ color: "var(--compass-color-text)"
1153
+ },
1154
+ children: title
1155
+ }
1156
+ ),
1157
+ /* @__PURE__ */ jsx(
1158
+ "button",
1159
+ {
1160
+ onClick: onClose,
1161
+ className: "hover:opacity-70",
1162
+ style: {
1163
+ color: "var(--compass-color-text-secondary)",
1164
+ borderRadius: "var(--compass-border-radius-md)",
1165
+ padding: "calc(var(--compass-spacing-unit) * 0.25)",
1166
+ transition: "var(--compass-transition-normal)"
1167
+ },
1168
+ children: /* @__PURE__ */ jsx(X, { size: 20 })
1169
+ }
1170
+ )
1171
+ ]
1172
+ }
1173
+ ),
1174
+ /* @__PURE__ */ jsx("div", { style: { padding: "var(--compass-spacing-card)" }, children })
1175
+ ]
1176
+ }
1177
+ )
1178
+ ]
1179
+ }
1180
+ );
1174
1181
  }
1175
1182
  function PnLSummary({ pnl, tokenSymbol, tokenPrice }) {
1176
1183
  const [isExpanded, setIsExpanded] = useState(false);
@@ -2230,227 +2237,84 @@ function AccountBalancesModal({
2230
2237
  onClose,
2231
2238
  balances,
2232
2239
  totalUsdValue,
2233
- isLoading = false,
2234
- earnAccountAddress
2240
+ isLoading = false
2235
2241
  }) {
2236
- useEffect(() => {
2237
- const handleEscape = (e) => {
2238
- if (e.key === "Escape") onClose();
2239
- };
2240
- if (isOpen) {
2241
- document.addEventListener("keydown", handleEscape);
2242
- document.body.style.overflow = "hidden";
2242
+ return /* @__PURE__ */ jsx(ActionModal, { isOpen, onClose, title: "Balance Breakdown", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: isLoading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-4", children: /* @__PURE__ */ jsx(
2243
+ Loader2,
2244
+ {
2245
+ size: 24,
2246
+ className: "animate-spin",
2247
+ style: { color: "var(--compass-color-primary)" }
2243
2248
  }
2244
- return () => {
2245
- document.removeEventListener("keydown", handleEscape);
2246
- document.body.style.overflow = "";
2247
- };
2248
- }, [isOpen, onClose]);
2249
- if (!isOpen) return null;
2250
- return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
2251
- /* @__PURE__ */ jsx(
2252
- "div",
2253
- {
2254
- className: "absolute inset-0",
2255
- style: { backgroundColor: "var(--compass-color-overlay)" },
2256
- onClick: onClose
2257
- }
2258
- ),
2259
- /* @__PURE__ */ jsxs(
2260
- "div",
2261
- {
2262
- className: "relative w-full max-w-md mx-4 overflow-hidden",
2263
- style: {
2264
- backgroundColor: "var(--compass-color-surface)",
2265
- boxShadow: "var(--compass-shadow-lg)",
2266
- borderRadius: "var(--compass-border-radius-xl)",
2267
- fontFamily: "var(--compass-font-family)"
2268
- },
2269
- children: [
2270
- /* @__PURE__ */ jsxs(
2249
+ ) }) : balances.length === 0 ? /* @__PURE__ */ jsx(
2250
+ "div",
2251
+ {
2252
+ className: "text-center py-4",
2253
+ style: { color: "var(--compass-color-text-tertiary)" },
2254
+ children: "No tokens in account"
2255
+ }
2256
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
2257
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
2258
+ /* @__PURE__ */ jsx(
2259
+ "span",
2260
+ {
2261
+ className: "text-xs font-medium uppercase tracking-wide",
2262
+ style: { color: "var(--compass-color-text-tertiary)" },
2263
+ children: "Available in Account"
2264
+ }
2265
+ ),
2266
+ /* @__PURE__ */ jsx(
2267
+ "div",
2268
+ {
2269
+ className: "flex flex-col gap-2",
2270
+ style: {
2271
+ maxHeight: "50vh",
2272
+ overflowY: "auto",
2273
+ scrollbarWidth: "none"
2274
+ },
2275
+ children: balances.map((token) => /* @__PURE__ */ jsxs(
2271
2276
  "div",
2272
2277
  {
2273
- className: "flex items-center justify-between border-b",
2278
+ className: "flex items-center justify-between p-3 rounded-lg",
2274
2279
  style: {
2275
- borderColor: "var(--compass-color-border)",
2276
- padding: "calc(var(--compass-spacing-unit) * 0.75) var(--compass-spacing-card)"
2280
+ backgroundColor: "var(--compass-color-surface)",
2281
+ border: "1px solid var(--compass-color-border)",
2282
+ flexShrink: 0
2277
2283
  },
2278
2284
  children: [
2279
- /* @__PURE__ */ jsxs(
2280
- "h2",
2281
- {
2282
- className: "font-semibold flex items-center gap-2",
2283
- style: {
2284
- fontSize: "var(--compass-font-size-subheading)",
2285
- color: "var(--compass-color-text)"
2286
- },
2287
- children: [
2288
- /* @__PURE__ */ jsx(Coins, { size: 20 }),
2289
- "Account Balances"
2290
- ]
2291
- }
2292
- ),
2293
- /* @__PURE__ */ jsx(
2294
- "button",
2295
- {
2296
- onClick: onClose,
2297
- className: "hover:opacity-70",
2298
- style: {
2299
- color: "var(--compass-color-text-secondary)",
2300
- borderRadius: "var(--compass-border-radius-md)",
2301
- padding: "calc(var(--compass-spacing-unit) * 0.25)",
2302
- transition: "var(--compass-transition-normal)"
2303
- },
2304
- children: /* @__PURE__ */ jsx(X, { size: 20 })
2305
- }
2306
- )
2285
+ /* @__PURE__ */ jsx("span", { className: "font-medium", style: { color: "var(--compass-color-text)" }, children: token.symbol }),
2286
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end", children: [
2287
+ /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", style: { color: "var(--compass-color-text)" }, children: formatAmount(token.balance) }),
2288
+ /* @__PURE__ */ jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: formatUSD(token.usdValue) })
2289
+ ] })
2307
2290
  ]
2291
+ },
2292
+ token.symbol
2293
+ ))
2294
+ }
2295
+ )
2296
+ ] }),
2297
+ /* @__PURE__ */ jsxs(
2298
+ "div",
2299
+ {
2300
+ className: "flex items-center justify-between pt-3 mt-2",
2301
+ style: { borderTop: "2px solid var(--compass-color-border)" },
2302
+ children: [
2303
+ /* @__PURE__ */ jsx("span", { className: "font-semibold", style: { color: "var(--compass-color-text)" }, children: "Total" }),
2304
+ /* @__PURE__ */ jsx(
2305
+ "span",
2306
+ {
2307
+ className: "font-bold text-xl",
2308
+ style: { color: "var(--compass-color-text)" },
2309
+ children: formatUSD(totalUsdValue)
2308
2310
  }
2309
- ),
2310
- /* @__PURE__ */ jsxs("div", { style: { padding: "var(--compass-spacing-card)" }, children: [
2311
- isLoading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", style: { padding: "calc(var(--compass-spacing-unit) * 2) 0" }, children: /* @__PURE__ */ jsx(
2312
- Loader2,
2313
- {
2314
- size: 24,
2315
- className: "animate-spin",
2316
- style: { color: "var(--compass-color-primary)" }
2317
- }
2318
- ) }) : balances.length === 0 ? /* @__PURE__ */ jsx(
2319
- "div",
2320
- {
2321
- className: "text-center",
2322
- style: { color: "var(--compass-color-text-secondary)", padding: "calc(var(--compass-spacing-unit) * 2) 0" },
2323
- children: "No token balances found"
2324
- }
2325
- ) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
2326
- /* @__PURE__ */ jsxs(
2327
- "div",
2328
- {
2329
- className: "flex items-center justify-between border",
2330
- style: {
2331
- borderColor: "var(--compass-color-border)",
2332
- backgroundColor: "var(--compass-color-surface)",
2333
- borderRadius: "var(--compass-border-radius-lg)",
2334
- padding: "calc(var(--compass-spacing-unit) * 1)",
2335
- marginBottom: "calc(var(--compass-spacing-unit) * 0.5)"
2336
- },
2337
- children: [
2338
- /* @__PURE__ */ jsx(
2339
- "span",
2340
- {
2341
- className: "font-medium",
2342
- style: { color: "var(--compass-color-text-secondary)" },
2343
- children: "Total Balance"
2344
- }
2345
- ),
2346
- /* @__PURE__ */ jsx(
2347
- "span",
2348
- {
2349
- className: "font-bold text-xl",
2350
- style: { color: "var(--compass-color-text)" },
2351
- children: formatUSD(totalUsdValue)
2352
- }
2353
- )
2354
- ]
2355
- }
2356
- ),
2357
- /* @__PURE__ */ jsx(
2358
- "div",
2359
- {
2360
- className: "text-xs font-medium uppercase tracking-wide",
2361
- style: {
2362
- color: "var(--compass-color-text-tertiary)",
2363
- padding: "calc(var(--compass-spacing-unit) * 0.5) 0"
2364
- },
2365
- children: "Token Breakdown"
2366
- }
2367
- ),
2368
- balances.map((token) => /* @__PURE__ */ jsxs(
2369
- "div",
2370
- {
2371
- className: "flex items-center justify-between",
2372
- style: {
2373
- backgroundColor: "var(--compass-color-background)",
2374
- borderRadius: "var(--compass-border-radius-lg)",
2375
- padding: "calc(var(--compass-spacing-unit) * 0.75)"
2376
- },
2377
- children: [
2378
- /* @__PURE__ */ jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.75)" }, children: [
2379
- /* @__PURE__ */ jsx(
2380
- "div",
2381
- {
2382
- className: "w-8 h-8 flex items-center justify-center text-xs font-bold",
2383
- style: {
2384
- backgroundColor: "var(--compass-color-primary-muted)",
2385
- color: "var(--compass-color-primary)",
2386
- borderRadius: "var(--compass-border-radius-full)"
2387
- },
2388
- children: token.symbol.slice(0, 2)
2389
- }
2390
- ),
2391
- /* @__PURE__ */ jsxs("div", { children: [
2392
- /* @__PURE__ */ jsx(
2393
- "div",
2394
- {
2395
- className: "font-medium",
2396
- style: { color: "var(--compass-color-text)" },
2397
- children: token.symbol
2398
- }
2399
- ),
2400
- /* @__PURE__ */ jsx(
2401
- "div",
2402
- {
2403
- className: "text-sm font-mono",
2404
- style: { color: "var(--compass-color-text-secondary)" },
2405
- children: formatAmount(token.balance)
2406
- }
2407
- )
2408
- ] })
2409
- ] }),
2410
- /* @__PURE__ */ jsx(
2411
- "div",
2412
- {
2413
- className: "font-medium",
2414
- style: { color: "var(--compass-color-text)" },
2415
- children: formatUSD(token.usdValue)
2416
- }
2417
- )
2418
- ]
2419
- },
2420
- token.symbol
2421
- ))
2422
- ] }),
2423
- earnAccountAddress && /* @__PURE__ */ jsx(
2424
- "div",
2425
- {
2426
- className: "border-t text-center",
2427
- style: {
2428
- borderColor: "var(--compass-color-border)",
2429
- marginTop: "var(--compass-spacing-card)",
2430
- paddingTop: "var(--compass-spacing-card)"
2431
- },
2432
- children: /* @__PURE__ */ jsxs(
2433
- "span",
2434
- {
2435
- className: "text-xs",
2436
- style: { color: "var(--compass-color-text-tertiary)" },
2437
- children: [
2438
- "Earn Account: ",
2439
- earnAccountAddress.slice(0, 6),
2440
- "...",
2441
- earnAccountAddress.slice(-4)
2442
- ]
2443
- }
2444
- )
2445
- }
2446
- )
2447
- ] })
2311
+ )
2448
2312
  ]
2449
2313
  }
2450
2314
  )
2451
- ] });
2315
+ ] }) }) });
2452
2316
  }
2453
- var TRANSFER_TOKENS = ["USDC"];
2317
+ var TRANSFER_TOKENS = ["USDC", "SBC"];
2454
2318
  var EarnAccountBalance = forwardRef(function EarnAccountBalance2({
2455
2319
  compact = false,
2456
2320
  hideVisual = false,
@@ -2950,7 +2814,459 @@ var EarnAccountBalance = forwardRef(function EarnAccountBalance2({
2950
2814
  }
2951
2815
  )
2952
2816
  ] });
2953
- });
2817
+ });
2818
+ function useSwapQuote({ fromToken, toToken, amount, enabled = true }) {
2819
+ const { chainId } = useChain();
2820
+ const { address } = useEmbeddableWallet();
2821
+ const query = useQuery({
2822
+ queryKey: ["swapQuote", chainId, fromToken, toToken, amount, address],
2823
+ queryFn: async () => {
2824
+ if (!fromToken || !toToken || !amount || parseFloat(amount) <= 0 || !address) {
2825
+ return null;
2826
+ }
2827
+ const params = new URLSearchParams({
2828
+ owner: address,
2829
+ chain: chainId,
2830
+ tokenIn: fromToken,
2831
+ tokenOut: toToken,
2832
+ amountIn: amount
2833
+ });
2834
+ const response = await fetch(`/api/compass/swap/quote?${params}`);
2835
+ if (!response.ok) {
2836
+ const errorData = await response.json();
2837
+ const errorMessage = errorData.message || errorData.error || "Failed to get swap quote";
2838
+ throw new Error(errorMessage);
2839
+ }
2840
+ const data = await response.json();
2841
+ const outputAmount = data.estimatedAmountOut || "0";
2842
+ const inputAmountNum = parseFloat(amount);
2843
+ const outputAmountNum = parseFloat(outputAmount);
2844
+ return {
2845
+ inputAmount: amount,
2846
+ outputAmount,
2847
+ rate: inputAmountNum > 0 ? (outputAmountNum / inputAmountNum).toString() : "0"
2848
+ };
2849
+ },
2850
+ enabled: enabled && !!address && !!fromToken && !!toToken && !!amount && parseFloat(amount) > 0,
2851
+ staleTime: 10 * 1e3,
2852
+ refetchInterval: 15 * 1e3,
2853
+ retry: 1
2854
+ });
2855
+ return {
2856
+ quote: query.data,
2857
+ isLoading: query.isLoading,
2858
+ isError: query.isError,
2859
+ error: query.error,
2860
+ refetch: query.refetch
2861
+ };
2862
+ }
2863
+ var COMMON_TOKENS = [
2864
+ "USDC",
2865
+ "USDT",
2866
+ "DAI",
2867
+ "USDS",
2868
+ "USDE",
2869
+ "sUSDE",
2870
+ "sDAI",
2871
+ "FRAX",
2872
+ "LUSD",
2873
+ "GUSD",
2874
+ "PYUSD",
2875
+ "crvUSD",
2876
+ "GHO",
2877
+ "EURC",
2878
+ "EURS",
2879
+ "EURA",
2880
+ "WETH",
2881
+ "WBTC",
2882
+ "wstETH",
2883
+ "cbETH",
2884
+ "rETH"
2885
+ ];
2886
+ function SwapForm({
2887
+ availableFromTokens,
2888
+ availableToTokens,
2889
+ balances,
2890
+ defaultFromToken,
2891
+ defaultToToken,
2892
+ onSwapSuccess,
2893
+ onSwapError
2894
+ }) {
2895
+ const toTokens = Array.from(/* @__PURE__ */ new Set([
2896
+ ...availableFromTokens,
2897
+ ...COMMON_TOKENS,
2898
+ ...availableToTokens || []
2899
+ ]));
2900
+ const [fromToken, setFromToken] = useState(defaultFromToken || availableFromTokens[0] || "USDC");
2901
+ const [toToken, setToToken] = useState(defaultToToken || toTokens.find((t) => t !== (defaultFromToken || availableFromTokens[0])) || "USDC");
2902
+ const [fromAmount, setFromAmount] = useState("");
2903
+ const [isSwapping, setIsSwapping] = useState(false);
2904
+ const [swapStatus, setSwapStatus] = useState("");
2905
+ const [isFromOpen, setIsFromOpen] = useState(false);
2906
+ const [isToOpen, setIsToOpen] = useState(false);
2907
+ const { address, isConnected, signTypedData } = useEmbeddableWallet();
2908
+ const { chainId } = useChain();
2909
+ const { quote, isLoading: isQuoteLoading, error: quoteError } = useSwapQuote({
2910
+ fromToken,
2911
+ toToken,
2912
+ amount: fromAmount,
2913
+ enabled: !!fromAmount && parseFloat(fromAmount) > 0
2914
+ });
2915
+ const handleReverse = useCallback(() => {
2916
+ const oldFrom = fromToken;
2917
+ const oldTo = toToken;
2918
+ if (availableFromTokens.includes(oldTo)) {
2919
+ setFromToken(oldTo);
2920
+ setToToken(oldFrom);
2921
+ setFromAmount("");
2922
+ }
2923
+ }, [fromToken, toToken, availableFromTokens]);
2924
+ const handleSwap = useCallback(async () => {
2925
+ if (!address || !fromAmount || !quote || !signTypedData) return;
2926
+ setIsSwapping(true);
2927
+ setSwapStatus("Preparing swap...");
2928
+ try {
2929
+ const prepareResponse = await fetch("/api/compass/swap/prepare", {
2930
+ method: "POST",
2931
+ headers: { "Content-Type": "application/json" },
2932
+ body: JSON.stringify({
2933
+ owner: address,
2934
+ chain: chainId,
2935
+ tokenIn: fromToken,
2936
+ tokenOut: toToken,
2937
+ amountIn: fromAmount
2938
+ })
2939
+ });
2940
+ if (!prepareResponse.ok) {
2941
+ const error = await prepareResponse.json();
2942
+ throw new Error(error.error || "Failed to prepare swap");
2943
+ }
2944
+ const prepareData = await prepareResponse.json();
2945
+ const { eip712, normalizedTypes } = prepareData;
2946
+ if (!eip712) {
2947
+ throw new Error("No EIP-712 data returned from prepare");
2948
+ }
2949
+ setSwapStatus("Please sign the transaction...");
2950
+ const signature = await signTypedData({
2951
+ domain: eip712.domain,
2952
+ types: normalizedTypes || eip712.types,
2953
+ primaryType: "SafeTx",
2954
+ message: eip712.message
2955
+ });
2956
+ setSwapStatus("Executing swap...");
2957
+ const executeResponse = await fetch("/api/compass/swap/execute", {
2958
+ method: "POST",
2959
+ headers: { "Content-Type": "application/json" },
2960
+ body: JSON.stringify({
2961
+ owner: address,
2962
+ chain: chainId,
2963
+ eip712,
2964
+ signature
2965
+ })
2966
+ });
2967
+ if (!executeResponse.ok) {
2968
+ const error = await executeResponse.json();
2969
+ throw new Error(error.error || "Failed to execute swap");
2970
+ }
2971
+ const executeData = await executeResponse.json();
2972
+ const txHash = executeData.txHash;
2973
+ setSwapStatus("Swap successful!");
2974
+ onSwapSuccess?.(fromToken, toToken, fromAmount, quote.outputAmount, txHash);
2975
+ setFromAmount("");
2976
+ setTimeout(() => setSwapStatus(""), 3e3);
2977
+ } catch (error) {
2978
+ setSwapStatus("");
2979
+ onSwapError?.(error);
2980
+ } finally {
2981
+ setIsSwapping(false);
2982
+ }
2983
+ }, [address, fromAmount, quote, chainId, fromToken, toToken, signTypedData, onSwapSuccess, onSwapError]);
2984
+ const canReverse = availableFromTokens.includes(toToken);
2985
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.75)" }, children: [
2986
+ /* @__PURE__ */ jsxs(
2987
+ "div",
2988
+ {
2989
+ style: {
2990
+ backgroundColor: "var(--compass-color-surface)",
2991
+ border: "1px solid var(--compass-color-border)",
2992
+ borderRadius: "var(--compass-border-radius-xl)",
2993
+ padding: "calc(var(--compass-spacing-unit) * 0.75)"
2994
+ },
2995
+ children: [
2996
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", style: { marginBottom: "calc(var(--compass-spacing-unit) * 0.25)" }, children: [
2997
+ /* @__PURE__ */ jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "From" }),
2998
+ balances?.[fromToken] && /* @__PURE__ */ jsxs("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: [
2999
+ "Balance: ",
3000
+ parseFloat(balances[fromToken]).toLocaleString(void 0, { maximumFractionDigits: 6 })
3001
+ ] })
3002
+ ] }),
3003
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
3004
+ /* @__PURE__ */ jsx(
3005
+ "input",
3006
+ {
3007
+ type: "number",
3008
+ value: fromAmount,
3009
+ onChange: (e) => setFromAmount(e.target.value),
3010
+ placeholder: "0.00",
3011
+ disabled: isSwapping,
3012
+ className: "flex-1 bg-transparent outline-none font-medium min-w-0",
3013
+ style: { color: "var(--compass-color-text)", fontSize: "1.25rem" }
3014
+ }
3015
+ ),
3016
+ /* @__PURE__ */ jsxs("div", { className: "relative", style: { flexShrink: 0 }, children: [
3017
+ /* @__PURE__ */ jsxs(
3018
+ "button",
3019
+ {
3020
+ onClick: () => {
3021
+ setIsFromOpen(!isFromOpen);
3022
+ setIsToOpen(false);
3023
+ },
3024
+ disabled: isSwapping,
3025
+ className: "flex items-center font-medium",
3026
+ style: {
3027
+ backgroundColor: "var(--compass-color-surface-elevated, var(--compass-color-background))",
3028
+ border: "1px solid var(--compass-color-border)",
3029
+ color: "var(--compass-color-text)",
3030
+ borderRadius: "var(--compass-border-radius-md)",
3031
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 0.75)",
3032
+ gap: "calc(var(--compass-spacing-unit) * 0.25)",
3033
+ fontSize: "0.875rem"
3034
+ },
3035
+ children: [
3036
+ fromToken,
3037
+ /* @__PURE__ */ jsx(ChevronDown, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
3038
+ ]
3039
+ }
3040
+ ),
3041
+ isFromOpen && /* @__PURE__ */ jsx(
3042
+ "div",
3043
+ {
3044
+ className: "absolute right-0 mt-1 z-10",
3045
+ style: {
3046
+ backgroundColor: "var(--compass-color-surface)",
3047
+ border: "1px solid var(--compass-color-border)",
3048
+ borderRadius: "var(--compass-border-radius-lg)",
3049
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
3050
+ minWidth: "120px",
3051
+ maxHeight: "200px",
3052
+ overflowY: "auto",
3053
+ scrollbarWidth: "none"
3054
+ },
3055
+ children: availableFromTokens.filter((t) => t !== toToken).map((token) => /* @__PURE__ */ jsx(
3056
+ "button",
3057
+ {
3058
+ onClick: () => {
3059
+ setFromToken(token);
3060
+ setIsFromOpen(false);
3061
+ },
3062
+ className: "w-full text-left font-medium transition-colors",
3063
+ style: {
3064
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 0.75)",
3065
+ color: token === fromToken ? "var(--compass-color-primary)" : "var(--compass-color-text)",
3066
+ backgroundColor: token === fromToken ? "var(--compass-color-primary-muted, rgba(99, 102, 241, 0.1))" : "transparent",
3067
+ fontSize: "0.875rem"
3068
+ },
3069
+ children: token
3070
+ },
3071
+ token
3072
+ ))
3073
+ }
3074
+ )
3075
+ ] })
3076
+ ] })
3077
+ ]
3078
+ }
3079
+ ),
3080
+ /* @__PURE__ */ jsx("div", { className: "flex justify-center relative z-10", style: { margin: "calc(var(--compass-spacing-unit) * -0.25) 0" }, children: /* @__PURE__ */ jsx(
3081
+ "button",
3082
+ {
3083
+ onClick: handleReverse,
3084
+ disabled: !canReverse || isSwapping,
3085
+ className: "border disabled:opacity-30",
3086
+ style: {
3087
+ backgroundColor: "var(--compass-color-surface)",
3088
+ borderColor: "var(--compass-color-border)",
3089
+ borderRadius: "var(--compass-border-radius-full)",
3090
+ padding: "calc(var(--compass-spacing-unit) * 0.5)",
3091
+ transition: "var(--compass-transition-normal)"
3092
+ },
3093
+ children: /* @__PURE__ */ jsx(ArrowDownUp, { size: 16, style: { color: "var(--compass-color-text-secondary)" } })
3094
+ }
3095
+ ) }),
3096
+ /* @__PURE__ */ jsxs(
3097
+ "div",
3098
+ {
3099
+ style: {
3100
+ backgroundColor: "var(--compass-color-surface)",
3101
+ border: "1px solid var(--compass-color-border)",
3102
+ borderRadius: "var(--compass-border-radius-xl)",
3103
+ padding: "calc(var(--compass-spacing-unit) * 0.75)"
3104
+ },
3105
+ children: [
3106
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", style: { marginBottom: "calc(var(--compass-spacing-unit) * 0.25)" }, children: /* @__PURE__ */ jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "To" }) }),
3107
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
3108
+ /* @__PURE__ */ jsx(
3109
+ "div",
3110
+ {
3111
+ className: "flex-1 font-medium min-w-0",
3112
+ style: {
3113
+ color: isQuoteLoading ? "var(--compass-color-text-tertiary)" : "var(--compass-color-text)",
3114
+ fontSize: "1.25rem"
3115
+ },
3116
+ children: isQuoteLoading ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
3117
+ /* @__PURE__ */ jsx(Loader2, { size: 16, className: "animate-spin" }),
3118
+ "Loading..."
3119
+ ] }) : quote?.outputAmount ? parseFloat(quote.outputAmount).toFixed(8) : "0.00"
3120
+ }
3121
+ ),
3122
+ /* @__PURE__ */ jsxs("div", { className: "relative", style: { flexShrink: 0 }, children: [
3123
+ /* @__PURE__ */ jsxs(
3124
+ "button",
3125
+ {
3126
+ onClick: () => {
3127
+ setIsToOpen(!isToOpen);
3128
+ setIsFromOpen(false);
3129
+ },
3130
+ disabled: isSwapping,
3131
+ className: "flex items-center font-medium",
3132
+ style: {
3133
+ backgroundColor: "var(--compass-color-surface-elevated, var(--compass-color-background))",
3134
+ border: "1px solid var(--compass-color-border)",
3135
+ color: "var(--compass-color-text)",
3136
+ borderRadius: "var(--compass-border-radius-md)",
3137
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 0.75)",
3138
+ gap: "calc(var(--compass-spacing-unit) * 0.25)",
3139
+ fontSize: "0.875rem"
3140
+ },
3141
+ children: [
3142
+ toToken,
3143
+ /* @__PURE__ */ jsx(ChevronDown, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
3144
+ ]
3145
+ }
3146
+ ),
3147
+ isToOpen && /* @__PURE__ */ jsx(
3148
+ "div",
3149
+ {
3150
+ className: "absolute right-0 mt-1 z-10",
3151
+ style: {
3152
+ backgroundColor: "var(--compass-color-surface)",
3153
+ border: "1px solid var(--compass-color-border)",
3154
+ borderRadius: "var(--compass-border-radius-lg)",
3155
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
3156
+ minWidth: "140px",
3157
+ maxHeight: "280px",
3158
+ overflowY: "auto",
3159
+ scrollbarWidth: "none"
3160
+ },
3161
+ children: toTokens.filter((t) => t !== fromToken).map((token) => /* @__PURE__ */ jsx(
3162
+ "button",
3163
+ {
3164
+ onClick: () => {
3165
+ setToToken(token);
3166
+ setIsToOpen(false);
3167
+ },
3168
+ className: "w-full text-left font-medium transition-colors",
3169
+ style: {
3170
+ padding: "calc(var(--compass-spacing-unit) * 0.5) calc(var(--compass-spacing-unit) * 0.75)",
3171
+ color: token === toToken ? "var(--compass-color-primary)" : "var(--compass-color-text)",
3172
+ backgroundColor: token === toToken ? "var(--compass-color-primary-muted, rgba(99, 102, 241, 0.1))" : "transparent",
3173
+ fontSize: "0.875rem"
3174
+ },
3175
+ children: token
3176
+ },
3177
+ token
3178
+ ))
3179
+ }
3180
+ )
3181
+ ] })
3182
+ ] })
3183
+ ]
3184
+ }
3185
+ ),
3186
+ quote && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", style: { color: "var(--compass-color-text-secondary)" }, children: [
3187
+ /* @__PURE__ */ jsx("span", { children: "Rate" }),
3188
+ /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
3189
+ "1 ",
3190
+ fromToken,
3191
+ " = ",
3192
+ parseFloat(quote.rate).toFixed(6),
3193
+ " ",
3194
+ toToken
3195
+ ] })
3196
+ ] }),
3197
+ quoteError && /* @__PURE__ */ jsxs(
3198
+ "div",
3199
+ {
3200
+ className: "flex items-center text-sm",
3201
+ style: {
3202
+ backgroundColor: "var(--compass-color-error-muted)",
3203
+ color: "var(--compass-color-error)",
3204
+ borderRadius: "var(--compass-border-radius-lg)",
3205
+ padding: "calc(var(--compass-spacing-unit) * 0.75)",
3206
+ gap: "calc(var(--compass-spacing-unit) * 0.5)"
3207
+ },
3208
+ children: [
3209
+ /* @__PURE__ */ jsx(AlertCircle, { size: 16 }),
3210
+ /* @__PURE__ */ jsx("span", { children: quoteError.message })
3211
+ ]
3212
+ }
3213
+ ),
3214
+ swapStatus && /* @__PURE__ */ jsxs(
3215
+ "div",
3216
+ {
3217
+ className: "flex items-center text-sm",
3218
+ style: {
3219
+ backgroundColor: swapStatus.includes("successful") ? "var(--compass-color-success-muted)" : "var(--compass-color-surface)",
3220
+ color: swapStatus.includes("successful") ? "var(--compass-color-success)" : "var(--compass-color-text-secondary)",
3221
+ borderRadius: "var(--compass-border-radius-lg)",
3222
+ padding: "calc(var(--compass-spacing-unit) * 0.75)",
3223
+ gap: "calc(var(--compass-spacing-unit) * 0.5)"
3224
+ },
3225
+ children: [
3226
+ !swapStatus.includes("successful") && /* @__PURE__ */ jsx(Loader2, { size: 14, className: "animate-spin" }),
3227
+ swapStatus
3228
+ ]
3229
+ }
3230
+ ),
3231
+ !isConnected ? /* @__PURE__ */ jsxs(
3232
+ "div",
3233
+ {
3234
+ className: "flex flex-col items-center",
3235
+ style: {
3236
+ backgroundColor: "var(--compass-color-surface)",
3237
+ border: "1px solid var(--compass-color-border)",
3238
+ borderRadius: "var(--compass-border-radius-lg)",
3239
+ padding: "calc(var(--compass-spacing-unit) * 1) var(--compass-spacing-card)",
3240
+ gap: "calc(var(--compass-spacing-unit) * 0.5)"
3241
+ },
3242
+ children: [
3243
+ /* @__PURE__ */ jsx(AlertCircle, { size: 20, style: { color: "var(--compass-color-text-tertiary)" } }),
3244
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-center", style: { color: "var(--compass-color-text-secondary)" }, children: "Connect your wallet to swap" })
3245
+ ]
3246
+ }
3247
+ ) : /* @__PURE__ */ jsxs(
3248
+ "button",
3249
+ {
3250
+ onClick: handleSwap,
3251
+ disabled: isSwapping || !quote || !fromAmount || parseFloat(fromAmount) <= 0,
3252
+ className: "w-full font-semibold flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed",
3253
+ style: {
3254
+ backgroundColor: "var(--compass-color-primary)",
3255
+ color: "var(--compass-color-primary-text, white)",
3256
+ borderRadius: "var(--compass-border-radius-lg)",
3257
+ padding: "calc(var(--compass-spacing-unit) * 0.75)",
3258
+ gap: "calc(var(--compass-spacing-unit) * 0.5)",
3259
+ fontSize: "0.875rem",
3260
+ transition: "var(--compass-transition-normal)"
3261
+ },
3262
+ children: [
3263
+ isSwapping && /* @__PURE__ */ jsx(Loader2, { size: 16, className: "animate-spin" }),
3264
+ isSwapping ? "Swapping..." : "Swap"
3265
+ ]
3266
+ }
3267
+ )
3268
+ ] });
3269
+ }
2954
3270
  var MARKET_TABS = [
2955
3271
  { value: "variable", label: "VARIABLE" },
2956
3272
  { value: "fixed", label: "FIXED" }
@@ -3783,7 +4099,6 @@ var EVM_CHAIN_IDS = {
3783
4099
  base: 8453,
3784
4100
  arbitrum: 42161
3785
4101
  };
3786
- var SUPPORTED_TOKENS = ["USDC", "USDT", "DAI", "WETH", "SBC", "AUSD"];
3787
4102
  function formatCurrency(amount) {
3788
4103
  if (!amount) return "$0.00";
3789
4104
  const num = typeof amount === "string" ? parseFloat(amount) : amount;
@@ -3835,6 +4150,7 @@ function EarnAccount({
3835
4150
  const [isBalancesModalOpen, setIsBalancesModalOpen] = useState(false);
3836
4151
  const [marketTab, setMarketTab] = useState(defaultMarketTab);
3837
4152
  const [selectedMarket, setSelectedMarket] = useState(null);
4153
+ const [isSwapModalOpen, setIsSwapModalOpen] = useState(false);
3838
4154
  const [isEarningsModalOpen, setIsEarningsModalOpen] = useState(false);
3839
4155
  useEffect(() => {
3840
4156
  setSelectedMarket(null);
@@ -3884,6 +4200,7 @@ function EarnAccount({
3884
4200
  enabled: !!address && isDeployed,
3885
4201
  staleTime: 30 * 1e3
3886
4202
  });
4203
+ const balanceTokens = Object.keys(balancesQuery.data?.balances || {});
3887
4204
  const selectedTokenBalance = balancesQuery.data?.balances?.[selectedToken]?.balance || "0";
3888
4205
  const earnAccountTotalUsd = Object.values(balancesQuery.data?.balances || {}).reduce((sum, b) => sum + parseFloat(b.usdValue || "0"), 0).toString();
3889
4206
  const positionQuery = useQuery({
@@ -4553,39 +4870,59 @@ function EarnAccount({
4553
4870
  ]
4554
4871
  }
4555
4872
  ),
4556
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
4557
- /* @__PURE__ */ jsx(
4873
+ /* @__PURE__ */ jsx(
4874
+ "button",
4875
+ {
4876
+ onClick: () => setIsBalancesModalOpen(true),
4877
+ className: "transition-opacity hover:opacity-80",
4878
+ children: /* @__PURE__ */ jsx(
4879
+ "span",
4880
+ {
4881
+ className: "font-bold",
4882
+ style: {
4883
+ color: "var(--compass-color-text)",
4884
+ fontSize: compact ? "2rem" : "2.5rem",
4885
+ lineHeight: "1"
4886
+ },
4887
+ children: formatCurrency(earnAccountTotal)
4888
+ }
4889
+ )
4890
+ }
4891
+ ),
4892
+ (balanceTokens.length > 0 || showTopUpButton) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col", style: { gap: "6px", marginTop: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
4893
+ balanceTokens.length > 0 && /* @__PURE__ */ jsxs(
4558
4894
  "button",
4559
4895
  {
4560
- onClick: () => setIsBalancesModalOpen(true),
4561
- className: "transition-opacity hover:opacity-80",
4562
- children: /* @__PURE__ */ jsx(
4563
- "span",
4564
- {
4565
- className: "font-bold",
4566
- style: {
4567
- color: "var(--compass-color-text)",
4568
- fontSize: compact ? "2rem" : "2.5rem",
4569
- lineHeight: "1"
4570
- },
4571
- children: formatCurrency(earnAccountTotal)
4572
- }
4573
- )
4896
+ onClick: () => setIsSwapModalOpen(true),
4897
+ className: "flex items-center justify-center font-medium transition-all hover:opacity-80 w-full",
4898
+ style: {
4899
+ backgroundColor: "var(--compass-color-surface-elevated, var(--compass-color-surface))",
4900
+ border: "1px solid var(--compass-color-border)",
4901
+ color: "var(--compass-color-text-secondary)",
4902
+ borderRadius: "var(--compass-border-radius-md)",
4903
+ padding: "8px 10px",
4904
+ gap: "6px",
4905
+ fontSize: "13px"
4906
+ },
4907
+ children: [
4908
+ /* @__PURE__ */ jsx(ArrowLeftRight, { size: 14 }),
4909
+ "Swap"
4910
+ ]
4574
4911
  }
4575
4912
  ),
4576
4913
  showTopUpButton && /* @__PURE__ */ jsxs(
4577
4914
  "button",
4578
4915
  {
4579
4916
  onClick: () => setIsFundModalOpen(true),
4580
- className: "flex items-center font-medium transition-all hover:opacity-80",
4917
+ className: "flex items-center justify-center font-medium transition-all hover:opacity-80 w-full",
4581
4918
  style: {
4582
4919
  backgroundColor: "var(--compass-color-surface-elevated, var(--compass-color-surface))",
4583
4920
  border: "1px solid var(--compass-color-border)",
4584
4921
  color: "var(--compass-color-text-secondary)",
4585
4922
  borderRadius: "var(--compass-border-radius-md)",
4586
- padding: "6px 10px",
4587
- gap: "4px",
4588
- fontSize: "12px"
4923
+ padding: "8px 10px",
4924
+ gap: "6px",
4925
+ fontSize: "13px"
4589
4926
  },
4590
4927
  children: [
4591
4928
  /* @__PURE__ */ jsx(Plus, { size: 14 }),
@@ -4789,7 +5126,7 @@ function EarnAccount({
4789
5126
  boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
4790
5127
  minWidth: "100px"
4791
5128
  },
4792
- children: SUPPORTED_TOKENS.map((token) => /* @__PURE__ */ jsx(
5129
+ children: balanceTokens.map((token) => /* @__PURE__ */ jsx(
4793
5130
  "button",
4794
5131
  {
4795
5132
  onClick: () => {
@@ -5090,402 +5427,134 @@ function EarnAccount({
5090
5427
  {
5091
5428
  onClick: handleFund,
5092
5429
  disabled: isFunding || !fundAmount || parseFloat(fundAmount) <= 0,
5093
- className: "w-full py-4 rounded-xl font-semibold text-lg transition-all disabled:opacity-50 disabled:cursor-not-allowed",
5094
- style: {
5095
- backgroundColor: "var(--compass-color-primary)",
5096
- color: "white"
5097
- },
5098
- children: isFunding ? /* @__PURE__ */ jsxs("span", { className: "flex items-center justify-center gap-2", children: [
5099
- /* @__PURE__ */ jsx(Loader2, { size: 20, className: "animate-spin" }),
5100
- "Processing..."
5101
- ] }) : /* @__PURE__ */ jsxs("span", { className: "flex items-center justify-center gap-2", children: [
5102
- /* @__PURE__ */ jsx(ArrowDownLeft, { size: 20 }),
5103
- needsFundSwap ? `Swap ${fundToken} & Transfer` : "Transfer to Savings"
5104
- ] })
5105
- }
5106
- ),
5107
- /* @__PURE__ */ jsx(
5108
- "p",
5109
- {
5110
- className: "text-xs text-center",
5111
- style: { color: "var(--compass-color-text-tertiary)" },
5112
- children: "Gas fees are sponsored"
5113
- }
5114
- )
5115
- ] })
5116
- }
5117
- ),
5118
- /* @__PURE__ */ jsx(
5119
- ActionModal,
5120
- {
5121
- isOpen: isBalancesModalOpen,
5122
- onClose: () => setIsBalancesModalOpen(false),
5123
- title: "Balance Breakdown",
5124
- children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: balancesQuery.data?.balances && Object.keys(balancesQuery.data.balances).length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
5125
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
5126
- /* @__PURE__ */ jsx(
5127
- "span",
5128
- {
5129
- className: "text-xs font-medium uppercase tracking-wide",
5130
- style: { color: "var(--compass-color-text-tertiary)" },
5131
- children: "Available in Account"
5132
- }
5133
- ),
5134
- Object.entries(balancesQuery.data.balances).map(([symbol, data]) => /* @__PURE__ */ jsxs(
5135
- "div",
5136
- {
5137
- className: "flex items-center justify-between p-3 rounded-lg",
5138
- style: {
5139
- backgroundColor: "var(--compass-color-surface)",
5140
- border: "1px solid var(--compass-color-border)"
5141
- },
5142
- children: [
5143
- /* @__PURE__ */ jsx("span", { className: "font-medium", style: { color: "var(--compass-color-text)" }, children: symbol }),
5144
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end", children: [
5145
- /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", style: { color: "var(--compass-color-text)" }, children: formatAmount2(data.balance) }),
5146
- /* @__PURE__ */ jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: formatCurrency(data.usdValue) })
5147
- ] })
5148
- ]
5149
- },
5150
- symbol
5151
- ))
5152
- ] }),
5153
- /* @__PURE__ */ jsxs(
5154
- "div",
5155
- {
5156
- className: "flex items-center justify-between pt-3 mt-2",
5157
- style: { borderTop: "2px solid var(--compass-color-border)" },
5158
- children: [
5159
- /* @__PURE__ */ jsx("span", { className: "font-semibold", style: { color: "var(--compass-color-text)" }, children: "Total" }),
5160
- /* @__PURE__ */ jsx(
5161
- "span",
5162
- {
5163
- className: "font-bold text-xl",
5164
- style: { color: "var(--compass-color-text)" },
5165
- children: formatCurrency(earnAccountTotalUsd)
5166
- }
5167
- )
5168
- ]
5169
- }
5170
- )
5171
- ] }) : /* @__PURE__ */ jsx(
5172
- "div",
5173
- {
5174
- className: "text-center py-4",
5175
- style: { color: "var(--compass-color-text-tertiary)" },
5176
- children: "No tokens in account"
5177
- }
5178
- ) })
5179
- }
5180
- ),
5181
- /* @__PURE__ */ jsx(
5182
- EarningsModal,
5183
- {
5184
- isOpen: isEarningsModalOpen,
5185
- onClose: () => setIsEarningsModalOpen(false),
5186
- positions,
5187
- totalEarned,
5188
- isLoading: positionQuery.isLoading
5189
- }
5190
- )
5191
- ] });
5192
- }
5193
- function useSwapQuote({ fromToken, toToken, amount, enabled = true }) {
5194
- const { chainId } = useChain();
5195
- const { address } = useCompassWallet();
5196
- const query = useQuery({
5197
- queryKey: ["swapQuote", chainId, fromToken, toToken, amount, address],
5198
- queryFn: async () => {
5199
- if (!fromToken || !toToken || !amount || parseFloat(amount) <= 0 || !address) {
5200
- return null;
5201
- }
5202
- try {
5203
- const params = new URLSearchParams({
5204
- owner: address,
5205
- chain: chainId,
5206
- tokenIn: fromToken,
5207
- tokenOut: toToken,
5208
- amountIn: amount
5209
- });
5210
- const response = await fetch(`/api/compass/swap/quote?${params}`);
5211
- if (!response.ok) {
5212
- const errorData = await response.json();
5213
- const errorMessage = errorData.message || errorData.error || "Failed to get swap quote";
5214
- throw new Error(errorMessage);
5215
- }
5216
- const data = await response.json();
5217
- const outputAmount = data.estimatedAmountOut || "0";
5218
- const inputAmountNum = parseFloat(amount);
5219
- const outputAmountNum = parseFloat(outputAmount);
5220
- return {
5221
- inputAmount: amount,
5222
- outputAmount,
5223
- rate: inputAmountNum > 0 ? (outputAmountNum / inputAmountNum).toString() : "0"
5224
- };
5225
- } catch (error) {
5226
- throw error;
5227
- }
5228
- },
5229
- enabled: enabled && !!address && !!fromToken && !!toToken && !!amount && parseFloat(amount) > 0,
5230
- staleTime: 10 * 1e3,
5231
- refetchInterval: 15 * 1e3,
5232
- retry: 1
5233
- });
5234
- return {
5235
- quote: query.data,
5236
- isLoading: query.isLoading,
5237
- isError: query.isError,
5238
- error: query.error,
5239
- refetch: query.refetch
5240
- };
5241
- }
5242
-
5243
- // src/components/SwapWidget/types.ts
5244
- var DEFAULT_SWAP_TOKENS = ["USDC", "ETH", "WETH", "WBTC", "DAI", "USDT", "AUSD", "SBC"];
5245
- function SwapWidget({
5246
- layout = "full",
5247
- defaultFromToken = "ETH",
5248
- defaultToToken = "USDC",
5249
- allowedTokens = [...DEFAULT_SWAP_TOKENS],
5250
- showReverseButton = true,
5251
- showSettings = false,
5252
- onSwapSuccess,
5253
- onSwapError
5254
- }) {
5255
- const [fromToken, setFromToken] = useState(defaultFromToken);
5256
- const [toToken, setToToken] = useState(defaultToToken);
5257
- const [fromAmount, setFromAmount] = useState("");
5258
- const [isSwapping, setIsSwapping] = useState(false);
5259
- const [swapStatus, setSwapStatus] = useState("");
5260
- const { address, isConnected, signTypedData } = useCompassWallet();
5261
- const { chainId } = useChain();
5262
- const { quote, isLoading: isQuoteLoading, error: quoteError } = useSwapQuote({
5263
- fromToken,
5264
- toToken,
5265
- amount: fromAmount,
5266
- enabled: !!fromAmount && parseFloat(fromAmount) > 0
5267
- });
5268
- const handleReverse = useCallback(() => {
5269
- setFromToken(toToken);
5270
- setToToken(fromToken);
5271
- setFromAmount("");
5272
- }, [fromToken, toToken]);
5273
- const handleSwap = useCallback(async () => {
5274
- if (!address || !fromAmount || !quote) return;
5275
- setIsSwapping(true);
5276
- setSwapStatus("Preparing swap...");
5277
- try {
5278
- const prepareResponse = await fetch("/api/compass/swap/prepare", {
5279
- method: "POST",
5280
- headers: { "Content-Type": "application/json" },
5281
- body: JSON.stringify({
5282
- owner: address,
5283
- chain: chainId,
5284
- tokenIn: fromToken,
5285
- tokenOut: toToken,
5286
- amountIn: fromAmount
5287
- })
5288
- });
5289
- if (!prepareResponse.ok) {
5290
- const error = await prepareResponse.json();
5291
- throw new Error(error.error || "Failed to prepare swap");
5292
- }
5293
- const prepareData = await prepareResponse.json();
5294
- const { eip712, normalizedTypes } = prepareData;
5295
- if (!eip712) {
5296
- throw new Error("No EIP-712 data returned from prepare");
5297
- }
5298
- setSwapStatus("Please sign the transaction...");
5299
- const signature = await signTypedData({
5300
- domain: eip712.domain,
5301
- types: normalizedTypes || eip712.types,
5302
- primaryType: "SafeTx",
5303
- message: eip712.message
5304
- });
5305
- setSwapStatus("Executing swap...");
5306
- const executeResponse = await fetch("/api/compass/swap/execute", {
5307
- method: "POST",
5308
- headers: { "Content-Type": "application/json" },
5309
- body: JSON.stringify({
5310
- owner: address,
5311
- chain: chainId,
5312
- eip712,
5313
- signature
5314
- })
5315
- });
5316
- if (!executeResponse.ok) {
5317
- const error = await executeResponse.json();
5318
- throw new Error(error.error || "Failed to execute swap");
5430
+ className: "w-full py-4 rounded-xl font-semibold text-lg transition-all disabled:opacity-50 disabled:cursor-not-allowed",
5431
+ style: {
5432
+ backgroundColor: "var(--compass-color-primary)",
5433
+ color: "white"
5434
+ },
5435
+ children: isFunding ? /* @__PURE__ */ jsxs("span", { className: "flex items-center justify-center gap-2", children: [
5436
+ /* @__PURE__ */ jsx(Loader2, { size: 20, className: "animate-spin" }),
5437
+ "Processing..."
5438
+ ] }) : /* @__PURE__ */ jsxs("span", { className: "flex items-center justify-center gap-2", children: [
5439
+ /* @__PURE__ */ jsx(ArrowDownLeft, { size: 20 }),
5440
+ needsFundSwap ? `Swap ${fundToken} & Transfer` : "Transfer to Savings"
5441
+ ] })
5442
+ }
5443
+ ),
5444
+ /* @__PURE__ */ jsx(
5445
+ "p",
5446
+ {
5447
+ className: "text-xs text-center",
5448
+ style: { color: "var(--compass-color-text-tertiary)" },
5449
+ children: "Gas fees are sponsored"
5450
+ }
5451
+ )
5452
+ ] })
5319
5453
  }
5320
- const executeData = await executeResponse.json();
5321
- const txHash = executeData.txHash;
5322
- setSwapStatus("Swap successful!");
5323
- onSwapSuccess?.(fromToken, toToken, fromAmount, quote.outputAmount, txHash);
5324
- setFromAmount("");
5325
- setTimeout(() => setSwapStatus(""), 3e3);
5326
- } catch (error) {
5327
- setSwapStatus("");
5328
- onSwapError?.(error);
5329
- } finally {
5330
- setIsSwapping(false);
5331
- }
5332
- }, [address, fromAmount, quote, chainId, fromToken, toToken, signTypedData, onSwapSuccess, onSwapError]);
5333
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", style: { gap: "var(--compass-spacing-card)" }, children: [
5334
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between flex-wrap", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
5335
- /* @__PURE__ */ jsx(ChainSwitcher, {}),
5336
- /* @__PURE__ */ jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
5337
- /* @__PURE__ */ jsx(EarnAccountBalance, { compact: true }),
5338
- /* @__PURE__ */ jsx(WalletStatus, { compact: true })
5339
- ] })
5340
- ] }),
5341
- /* @__PURE__ */ jsxs(EarnAccountGuard, { children: [
5342
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
5343
- /* @__PURE__ */ jsxs("div", { className: "border relative", style: { backgroundColor: "var(--compass-color-surface)", borderColor: "var(--compass-color-border)", borderRadius: "var(--compass-border-radius-xl)", fontFamily: "var(--compass-font-family)", padding: "var(--compass-spacing-card)" }, children: [
5344
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", style: { marginBottom: "calc(var(--compass-spacing-unit) * 0.5)" }, children: /* @__PURE__ */ jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "From" }) }),
5345
- /* @__PURE__ */ jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.75)" }, children: [
5454
+ ),
5455
+ /* @__PURE__ */ jsx(
5456
+ ActionModal,
5457
+ {
5458
+ isOpen: isBalancesModalOpen,
5459
+ onClose: () => setIsBalancesModalOpen(false),
5460
+ title: "Balance Breakdown",
5461
+ children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: balancesQuery.data?.balances && Object.keys(balancesQuery.data.balances).length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
5462
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
5346
5463
  /* @__PURE__ */ jsx(
5347
- "input",
5464
+ "span",
5348
5465
  {
5349
- type: "number",
5350
- value: fromAmount,
5351
- onChange: (e) => setFromAmount(e.target.value),
5352
- placeholder: "0.00",
5353
- className: "flex-1 bg-transparent outline-none text-2xl font-mono min-w-0",
5354
- style: { color: "var(--compass-color-text)" }
5466
+ className: "text-xs font-medium uppercase tracking-wide",
5467
+ style: { color: "var(--compass-color-text-tertiary)" },
5468
+ children: "Available in Account"
5355
5469
  }
5356
5470
  ),
5357
- /* @__PURE__ */ jsx(
5358
- "select",
5471
+ Object.entries(balancesQuery.data.balances).map(([symbol, data]) => /* @__PURE__ */ jsxs(
5472
+ "div",
5359
5473
  {
5360
- value: fromToken,
5361
- onChange: (e) => setFromToken(e.target.value),
5362
- className: "border text-sm font-medium cursor-pointer flex-shrink-0",
5363
- style: { backgroundColor: "var(--compass-color-background)", borderColor: "var(--compass-color-border)", color: "var(--compass-color-text)", borderRadius: "var(--compass-border-radius-lg)", padding: "var(--compass-spacing-input)" },
5364
- children: allowedTokens.filter((t) => t !== toToken).map((token) => /* @__PURE__ */ jsx("option", { value: token, children: token }, token))
5365
- }
5366
- )
5367
- ] })
5368
- ] }),
5369
- showReverseButton && /* @__PURE__ */ jsx("div", { className: "flex justify-center relative z-10", style: { margin: "calc(var(--compass-spacing-unit) * -0.25) 0" }, children: /* @__PURE__ */ jsx(
5370
- "button",
5474
+ className: "flex items-center justify-between p-3 rounded-lg",
5475
+ style: {
5476
+ backgroundColor: "var(--compass-color-surface)",
5477
+ border: "1px solid var(--compass-color-border)"
5478
+ },
5479
+ children: [
5480
+ /* @__PURE__ */ jsx("span", { className: "font-medium", style: { color: "var(--compass-color-text)" }, children: symbol }),
5481
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end", children: [
5482
+ /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", style: { color: "var(--compass-color-text)" }, children: formatAmount2(data.balance) }),
5483
+ /* @__PURE__ */ jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: formatCurrency(data.usdValue) })
5484
+ ] })
5485
+ ]
5486
+ },
5487
+ symbol
5488
+ ))
5489
+ ] }),
5490
+ /* @__PURE__ */ jsxs(
5491
+ "div",
5492
+ {
5493
+ className: "flex items-center justify-between pt-3 mt-2",
5494
+ style: { borderTop: "2px solid var(--compass-color-border)" },
5495
+ children: [
5496
+ /* @__PURE__ */ jsx("span", { className: "font-semibold", style: { color: "var(--compass-color-text)" }, children: "Total" }),
5497
+ /* @__PURE__ */ jsx(
5498
+ "span",
5499
+ {
5500
+ className: "font-bold text-xl",
5501
+ style: { color: "var(--compass-color-text)" },
5502
+ children: formatCurrency(earnAccountTotalUsd)
5503
+ }
5504
+ )
5505
+ ]
5506
+ }
5507
+ )
5508
+ ] }) : /* @__PURE__ */ jsx(
5509
+ "div",
5371
5510
  {
5372
- onClick: handleReverse,
5373
- className: "border hover:opacity-80",
5374
- style: { backgroundColor: "var(--compass-color-surface)", borderColor: "var(--compass-color-border)", borderRadius: "var(--compass-border-radius-full)", padding: "calc(var(--compass-spacing-unit) * 0.5)", transition: "var(--compass-transition-normal)" },
5375
- children: /* @__PURE__ */ jsx(ArrowDownUp, { size: 16, style: { color: "var(--compass-color-text-secondary)" } })
5511
+ className: "text-center py-4",
5512
+ style: { color: "var(--compass-color-text-tertiary)" },
5513
+ children: "No tokens in account"
5376
5514
  }
5377
- ) }),
5378
- /* @__PURE__ */ jsxs("div", { className: "border", style: { backgroundColor: "var(--compass-color-surface)", borderColor: "var(--compass-color-border)", borderRadius: "var(--compass-border-radius-xl)", fontFamily: "var(--compass-font-family)", padding: "var(--compass-spacing-card)" }, children: [
5379
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", style: { marginBottom: "calc(var(--compass-spacing-unit) * 0.5)" }, children: /* @__PURE__ */ jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "To" }) }),
5380
- /* @__PURE__ */ jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.75)" }, children: [
5381
- /* @__PURE__ */ jsx("div", { className: "flex-1 text-2xl font-mono", style: { color: isQuoteLoading ? "var(--compass-color-text-tertiary)" : "var(--compass-color-text)" }, children: isQuoteLoading ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
5382
- /* @__PURE__ */ jsx(Loader2, { size: 16, className: "animate-spin" }),
5383
- "Loading..."
5384
- ] }) : quote?.outputAmount ? parseFloat(quote.outputAmount).toFixed(8) : "0.00000000" }),
5385
- /* @__PURE__ */ jsx(
5386
- "select",
5387
- {
5388
- value: toToken,
5389
- onChange: (e) => setToToken(e.target.value),
5390
- className: "border text-sm font-medium cursor-pointer",
5391
- style: { backgroundColor: "var(--compass-color-background)", borderColor: "var(--compass-color-border)", color: "var(--compass-color-text)", borderRadius: "var(--compass-border-radius-lg)", padding: "var(--compass-spacing-input)" },
5392
- children: allowedTokens.filter((t) => t !== fromToken).map((token) => /* @__PURE__ */ jsx("option", { value: token, children: token }, token))
5393
- }
5394
- )
5395
- ] })
5396
- ] })
5397
- ] }),
5398
- quote && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", style: { color: "var(--compass-color-text-secondary)" }, children: [
5399
- /* @__PURE__ */ jsx("span", { children: "Rate" }),
5400
- /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
5401
- "1 ",
5402
- fromToken,
5403
- " = ",
5404
- parseFloat(quote.rate).toFixed(6),
5405
- " ",
5406
- toToken
5407
- ] })
5408
- ] }),
5409
- quoteError && /* @__PURE__ */ jsxs(
5410
- "div",
5411
- {
5412
- className: "flex items-center text-sm",
5413
- style: {
5414
- backgroundColor: "var(--compass-color-error-muted)",
5415
- color: "var(--compass-color-error)",
5416
- borderRadius: "var(--compass-border-radius-lg)",
5417
- padding: "calc(var(--compass-spacing-unit) * 0.75)",
5418
- gap: "calc(var(--compass-spacing-unit) * 0.5)"
5419
- },
5420
- children: [
5421
- /* @__PURE__ */ jsx(AlertCircle, { size: 16 }),
5422
- /* @__PURE__ */ jsx("span", { children: quoteError.message })
5423
- ]
5424
- }
5425
- ),
5426
- swapStatus && /* @__PURE__ */ jsxs(
5427
- "div",
5428
- {
5429
- className: "flex items-center text-sm",
5430
- style: {
5431
- backgroundColor: swapStatus.includes("successful") ? "var(--compass-color-success-muted)" : "var(--compass-color-surface)",
5432
- color: swapStatus.includes("successful") ? "var(--compass-color-success)" : "var(--compass-color-text-secondary)",
5433
- borderRadius: "var(--compass-border-radius-lg)",
5434
- padding: "calc(var(--compass-spacing-unit) * 0.75)",
5435
- gap: "calc(var(--compass-spacing-unit) * 0.5)"
5436
- },
5437
- children: [
5438
- !swapStatus.includes("successful") && /* @__PURE__ */ jsx(Loader2, { size: 14, className: "animate-spin" }),
5439
- swapStatus
5440
- ]
5441
- }
5442
- ),
5443
- !isConnected ? /* @__PURE__ */ jsxs(
5444
- "div",
5445
- {
5446
- className: "flex flex-col items-center",
5447
- style: {
5448
- backgroundColor: "var(--compass-color-surface)",
5449
- border: "1px solid var(--compass-color-border)",
5450
- borderRadius: "var(--compass-border-radius-lg)",
5451
- fontFamily: "var(--compass-font-family)",
5452
- padding: "calc(var(--compass-spacing-unit) * 1.5) var(--compass-spacing-card)",
5453
- gap: "calc(var(--compass-spacing-unit) * 0.75)"
5454
- },
5455
- children: [
5456
- /* @__PURE__ */ jsx(AlertCircle, { size: 24, style: { color: "var(--compass-color-text-tertiary)" } }),
5457
- /* @__PURE__ */ jsx(
5458
- "p",
5459
- {
5460
- className: "text-sm text-center",
5461
- style: { color: "var(--compass-color-text-secondary)" },
5462
- children: "Connect your wallet to swap"
5463
- }
5464
- )
5465
- ]
5466
- }
5467
- ) : /* @__PURE__ */ jsxs(
5468
- "button",
5469
- {
5470
- onClick: handleSwap,
5471
- disabled: isSwapping || !quote || !fromAmount || parseFloat(fromAmount) <= 0,
5472
- className: "w-full font-medium flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed",
5473
- style: {
5474
- backgroundColor: "var(--compass-color-primary)",
5475
- color: "var(--compass-color-primary-text)",
5476
- borderRadius: "var(--compass-border-radius-lg)",
5477
- fontFamily: "var(--compass-font-family)",
5478
- padding: "calc(var(--compass-spacing-unit) * 0.75)",
5479
- gap: "calc(var(--compass-spacing-unit) * 0.5)",
5480
- transition: "var(--compass-transition-normal)"
5481
- },
5482
- children: [
5483
- isSwapping && /* @__PURE__ */ jsx(Loader2, { size: 18, className: "animate-spin" }),
5484
- isSwapping ? "Swapping..." : "Swap"
5485
- ]
5486
- }
5487
- )
5488
- ] })
5515
+ ) })
5516
+ }
5517
+ ),
5518
+ /* @__PURE__ */ jsx(
5519
+ ActionModal,
5520
+ {
5521
+ isOpen: isSwapModalOpen,
5522
+ onClose: () => setIsSwapModalOpen(false),
5523
+ title: "Swap Tokens",
5524
+ children: /* @__PURE__ */ jsx(
5525
+ SwapForm,
5526
+ {
5527
+ availableFromTokens: balanceTokens,
5528
+ balances: Object.fromEntries(
5529
+ Object.entries(balancesQuery.data?.balances || {}).map(([token, data]) => [token, data.balance])
5530
+ ),
5531
+ onSwapSuccess: () => {
5532
+ setIsSwapModalOpen(false);
5533
+ queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
5534
+ balancesQuery.refetch();
5535
+ setTimeout(() => {
5536
+ queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
5537
+ balancesQuery.refetch();
5538
+ }, 5e3);
5539
+ setTimeout(() => {
5540
+ queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
5541
+ balancesQuery.refetch();
5542
+ }, 15e3);
5543
+ }
5544
+ }
5545
+ )
5546
+ }
5547
+ ),
5548
+ /* @__PURE__ */ jsx(
5549
+ EarningsModal,
5550
+ {
5551
+ isOpen: isEarningsModalOpen,
5552
+ onClose: () => setIsEarningsModalOpen(false),
5553
+ positions,
5554
+ totalEarned,
5555
+ isLoading: positionQuery.isLoading
5556
+ }
5557
+ )
5489
5558
  ] });
5490
5559
  }
5491
5560
  function getEarliestDepositTimestamp(deposits) {
@@ -5972,7 +6041,8 @@ function PositionDetailModal({ position, onClose }) {
5972
6041
  className: "relative w-full max-w-md rounded-xl border overflow-hidden max-h-[90vh] overflow-y-auto",
5973
6042
  style: {
5974
6043
  backgroundColor: "var(--compass-color-background)",
5975
- borderColor: "var(--compass-color-border)"
6044
+ borderColor: "var(--compass-color-border)",
6045
+ scrollbarWidth: "none"
5976
6046
  },
5977
6047
  onClick: (e) => e.stopPropagation(),
5978
6048
  children: [
@@ -6475,7 +6545,7 @@ function useRebalancingData(chainOverride) {
6475
6545
  if (usdValue <= 0) continue;
6476
6546
  balances.push({
6477
6547
  token: symbol,
6478
- balance: parseFloat(td.balanceFormatted || td.balance_formatted || "0"),
6548
+ balance: parseFloat(td.balance || "0"),
6479
6549
  usdValue
6480
6550
  });
6481
6551
  }
@@ -6761,7 +6831,8 @@ function PortfolioBalanceCard({
6761
6831
  positionCount,
6762
6832
  totalEarned = 0,
6763
6833
  showTopUp = true,
6764
- onTopUp
6834
+ onTopUp,
6835
+ onSwap
6765
6836
  }) {
6766
6837
  const [showBalancesModal, setShowBalancesModal] = useState(false);
6767
6838
  const tokenBalances = idleBalances.map((b) => ({
@@ -6802,39 +6873,59 @@ function PortfolioBalanceCard({
6802
6873
  ]
6803
6874
  }
6804
6875
  ),
6805
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
6806
- /* @__PURE__ */ jsx(
6876
+ /* @__PURE__ */ jsx(
6877
+ "button",
6878
+ {
6879
+ onClick: () => setShowBalancesModal(true),
6880
+ className: "transition-opacity hover:opacity-80",
6881
+ children: /* @__PURE__ */ jsx(
6882
+ "span",
6883
+ {
6884
+ className: "font-bold",
6885
+ style: {
6886
+ color: "var(--compass-color-text)",
6887
+ fontSize: "2rem",
6888
+ lineHeight: "1"
6889
+ },
6890
+ children: formatUSD(totalUsd)
6891
+ }
6892
+ )
6893
+ }
6894
+ ),
6895
+ (onSwap || showTopUp && onTopUp) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col", style: { gap: "6px", marginTop: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
6896
+ onSwap && /* @__PURE__ */ jsxs(
6807
6897
  "button",
6808
6898
  {
6809
- onClick: () => setShowBalancesModal(true),
6810
- className: "transition-opacity hover:opacity-80",
6811
- children: /* @__PURE__ */ jsx(
6812
- "span",
6813
- {
6814
- className: "font-bold",
6815
- style: {
6816
- color: "var(--compass-color-text)",
6817
- fontSize: "2rem",
6818
- lineHeight: "1"
6819
- },
6820
- children: formatUSD(totalUsd)
6821
- }
6822
- )
6899
+ onClick: onSwap,
6900
+ className: "flex items-center justify-center font-medium transition-all hover:opacity-80 w-full",
6901
+ style: {
6902
+ backgroundColor: "var(--compass-color-surface-elevated, var(--compass-color-surface))",
6903
+ border: "1px solid var(--compass-color-border)",
6904
+ color: "var(--compass-color-text-secondary)",
6905
+ borderRadius: "var(--compass-border-radius-md)",
6906
+ padding: "8px 10px",
6907
+ gap: "6px",
6908
+ fontSize: "13px"
6909
+ },
6910
+ children: [
6911
+ /* @__PURE__ */ jsx(ArrowLeftRight, { size: 14 }),
6912
+ "Swap"
6913
+ ]
6823
6914
  }
6824
6915
  ),
6825
6916
  showTopUp && onTopUp && /* @__PURE__ */ jsxs(
6826
6917
  "button",
6827
6918
  {
6828
6919
  onClick: onTopUp,
6829
- className: "flex items-center font-medium transition-all hover:opacity-80",
6920
+ className: "flex items-center justify-center font-medium transition-all hover:opacity-80 w-full",
6830
6921
  style: {
6831
6922
  backgroundColor: "var(--compass-color-surface-elevated, var(--compass-color-surface))",
6832
6923
  border: "1px solid var(--compass-color-border)",
6833
6924
  color: "var(--compass-color-text-secondary)",
6834
6925
  borderRadius: "var(--compass-border-radius-md)",
6835
- padding: "6px 10px",
6836
- gap: "4px",
6837
- fontSize: "12px"
6926
+ padding: "8px 10px",
6927
+ gap: "6px",
6928
+ fontSize: "13px"
6838
6929
  },
6839
6930
  children: [
6840
6931
  /* @__PURE__ */ jsx(Plus, { size: 14 }),
@@ -7175,7 +7266,6 @@ function AllocationEditor({
7175
7266
  ) })
7176
7267
  ] });
7177
7268
  }
7178
- var SUPPORTED_TOKENS2 = ["USDC", "USDT", "DAI", "WETH", "SBC", "AUSD"];
7179
7269
  var EVM_CHAIN_IDS2 = {
7180
7270
  ethereum: 1,
7181
7271
  base: 8453,
@@ -7236,7 +7326,12 @@ function RebalancingWidget({
7236
7326
  const [txHash, setTxHash] = useState(null);
7237
7327
  const [hasInitializedTargets, setHasInitializedTargets] = useState(false);
7238
7328
  const [isEarningsModalOpen, setIsEarningsModalOpen] = useState(false);
7329
+ const [isSwapModalOpen, setIsSwapModalOpen] = useState(false);
7239
7330
  const [isAddMarketExpanded, setIsAddMarketExpanded] = useState(false);
7331
+ const balanceTokens = useMemo(() => {
7332
+ if (!portfolio?.idleBalances) return [];
7333
+ return portfolio.idleBalances.map((b) => b.token);
7334
+ }, [portfolio?.idleBalances]);
7240
7335
  const [marketTab, setMarketTab] = useState("variable");
7241
7336
  const [selectedMarket, setSelectedMarket] = useState(null);
7242
7337
  const [selectedToken, setSelectedToken] = useState("USDC");
@@ -7255,13 +7350,14 @@ function RebalancingWidget({
7255
7350
  setTxHash(null);
7256
7351
  setHasInitializedTargets(false);
7257
7352
  setIsAddMarketExpanded(false);
7353
+ setIsSwapModalOpen(false);
7258
7354
  setSelectedMarket(null);
7259
7355
  setDepositAmount("");
7260
7356
  setDepositError(null);
7261
7357
  setDepositStatus("");
7262
7358
  }, [CHAIN_ID]);
7263
7359
  useEffect(() => {
7264
- if (portfolio && portfolio.positions.length > 0 && !hasInitializedTargets) {
7360
+ if (portfolio && portfolio.positions.length > 0 && !hasInitializedTargets && !isLoading) {
7265
7361
  setTargets(
7266
7362
  portfolio.positions.map((p) => ({
7267
7363
  venueType: p.venueType,
@@ -7273,7 +7369,7 @@ function RebalancingWidget({
7273
7369
  );
7274
7370
  setHasInitializedTargets(true);
7275
7371
  }
7276
- }, [portfolio, hasInitializedTargets]);
7372
+ }, [portfolio, hasInitializedTargets, isLoading]);
7277
7373
  const state = useMemo(() => {
7278
7374
  if (isLoading) return "loading";
7279
7375
  if (!portfolio || portfolio.positions.length === 0) return "empty";
@@ -7696,7 +7792,7 @@ function RebalancingWidget({
7696
7792
  ]
7697
7793
  }
7698
7794
  ),
7699
- state === "empty" && /* @__PURE__ */ jsxs(
7795
+ state === "empty" && /* @__PURE__ */ jsx(
7700
7796
  "div",
7701
7797
  {
7702
7798
  className: "p-8 text-center",
@@ -7704,7 +7800,32 @@ function RebalancingWidget({
7704
7800
  backgroundColor: "var(--compass-color-surface)",
7705
7801
  borderRadius: "var(--compass-border-radius-xl)"
7706
7802
  },
7707
- children: [
7803
+ children: !isConnected ? /* @__PURE__ */ jsxs(Fragment, { children: [
7804
+ /* @__PURE__ */ jsx(
7805
+ "p",
7806
+ {
7807
+ className: "text-lg font-semibold mb-2",
7808
+ style: { color: "var(--compass-color-text)" },
7809
+ children: "Connect your wallet"
7810
+ }
7811
+ ),
7812
+ /* @__PURE__ */ jsx("p", { className: "text-sm mb-4", style: { color: "var(--compass-color-text-secondary)" }, children: "Connect your wallet to view and manage your DeFi portfolio." }),
7813
+ login && /* @__PURE__ */ jsx(
7814
+ "button",
7815
+ {
7816
+ onClick: login,
7817
+ className: "font-semibold transition-all",
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) calc(var(--compass-spacing-unit) * 1.5)",
7823
+ fontSize: "0.875rem"
7824
+ },
7825
+ children: "Connect Wallet"
7826
+ }
7827
+ )
7828
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
7708
7829
  /* @__PURE__ */ jsx(
7709
7830
  "p",
7710
7831
  {
@@ -7714,7 +7835,7 @@ function RebalancingWidget({
7714
7835
  }
7715
7836
  ),
7716
7837
  /* @__PURE__ */ 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." })
7717
- ]
7838
+ ] })
7718
7839
  }
7719
7840
  ),
7720
7841
  state !== "loading" && state !== "empty" && portfolio && /* @__PURE__ */ jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.75)" }, children: [
@@ -7728,7 +7849,8 @@ function RebalancingWidget({
7728
7849
  positionCount: portfolio.positions.length,
7729
7850
  totalEarned,
7730
7851
  showTopUp,
7731
- onTopUp: () => earnBalanceRef.current?.openTransferModal()
7852
+ onTopUp: () => earnBalanceRef.current?.openTransferModal(),
7853
+ onSwap: balanceTokens.length > 0 ? () => setIsSwapModalOpen(true) : void 0
7732
7854
  }
7733
7855
  ),
7734
7856
  /* @__PURE__ */ jsx(
@@ -7848,7 +7970,7 @@ function RebalancingWidget({
7848
7970
  boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
7849
7971
  minWidth: "100px"
7850
7972
  },
7851
- children: SUPPORTED_TOKENS2.map((token) => /* @__PURE__ */ jsx(
7973
+ children: balanceTokens.map((token) => /* @__PURE__ */ jsx(
7852
7974
  "button",
7853
7975
  {
7854
7976
  onClick: () => {
@@ -8188,6 +8310,30 @@ function RebalancingWidget({
8188
8310
  }
8189
8311
  ) }),
8190
8312
  /* @__PURE__ */ jsx(EarnAccountBalance, { ref: earnBalanceRef, compact: true, hideVisual: true, onTransferComplete: () => refetch() }),
8313
+ /* @__PURE__ */ jsx(
8314
+ ActionModal,
8315
+ {
8316
+ isOpen: isSwapModalOpen,
8317
+ onClose: () => setIsSwapModalOpen(false),
8318
+ title: "Swap Tokens",
8319
+ children: /* @__PURE__ */ jsx(
8320
+ SwapForm,
8321
+ {
8322
+ availableFromTokens: balanceTokens,
8323
+ balances: Object.fromEntries(
8324
+ (portfolio?.idleBalances || []).map((b) => [b.token, b.balance.toString()])
8325
+ ),
8326
+ onSwapSuccess: () => {
8327
+ setIsSwapModalOpen(false);
8328
+ refetch();
8329
+ setTimeout(() => refetch(), 5e3);
8330
+ setTimeout(() => refetch(), 15e3);
8331
+ },
8332
+ onSwapError: onError
8333
+ }
8334
+ )
8335
+ }
8336
+ ),
8191
8337
  /* @__PURE__ */ jsx(
8192
8338
  EarningsModal,
8193
8339
  {
@@ -8245,7 +8391,6 @@ function ActionList({ actions }) {
8245
8391
 
8246
8392
  // src/components/CompassEarnWidget/presets.ts
8247
8393
  var allTabs = [
8248
- { id: "swap", label: "Swap", enabled: true },
8249
8394
  { id: "rebalance", label: "Rebalance", enabled: true },
8250
8395
  // TODO: Positions tab temporarily disabled - needs more work on API response handling
8251
8396
  { id: "positions", label: "Positions", enabled: false }
@@ -8254,22 +8399,16 @@ function getTabsForPreset(preset) {
8254
8399
  switch (preset) {
8255
8400
  case "full":
8256
8401
  return allTabs;
8257
- case "swap-only":
8258
- return allTabs.map((tab) => ({
8259
- ...tab,
8260
- enabled: tab.id === "swap"
8261
- }));
8262
8402
  default:
8263
8403
  return allTabs;
8264
8404
  }
8265
8405
  }
8266
8406
  function getDefaultTab(tabs) {
8267
8407
  const enabledTab = tabs.find((t) => t.enabled);
8268
- return enabledTab?.id || "swap";
8408
+ return enabledTab?.id || "rebalance";
8269
8409
  }
8270
8410
  function CompassEarnWidget({
8271
8411
  preset = "full",
8272
- enableSwap,
8273
8412
  enablePositions,
8274
8413
  enableRebalance,
8275
8414
  defaultTab,
@@ -8281,12 +8420,11 @@ function CompassEarnWidget({
8281
8420
  const baseTabs = getTabsForPreset(preset);
8282
8421
  return baseTabs.map((tab) => {
8283
8422
  let enabled = tab.enabled;
8284
- if (tab.id === "swap" && enableSwap !== void 0) enabled = enableSwap;
8285
8423
  if (tab.id === "positions" && enablePositions !== void 0) enabled = enablePositions;
8286
8424
  if (tab.id === "rebalance" && enableRebalance !== void 0) enabled = enableRebalance;
8287
8425
  return { ...tab, enabled };
8288
8426
  });
8289
- }, [preset, enableSwap, enablePositions, enableRebalance]);
8427
+ }, [preset, enablePositions, enableRebalance]);
8290
8428
  const enabledTabs = tabs.filter((t) => t.enabled);
8291
8429
  const initialTab = defaultTab && tabs.find((t) => t.id === defaultTab)?.enabled ? defaultTab : getDefaultTab(tabs);
8292
8430
  const [activeTab, setActiveTab] = useState(initialTab);
@@ -8327,7 +8465,6 @@ function CompassEarnWidget({
8327
8465
  }
8328
8466
  ),
8329
8467
  /* @__PURE__ */ jsxs("div", { children: [
8330
- activeTab === "swap" && /* @__PURE__ */ jsx(SwapWidget, {}),
8331
8468
  activeTab === "rebalance" && /* @__PURE__ */ jsx(RebalancingWidget, {}),
8332
8469
  activeTab === "positions" && /* @__PURE__ */ jsx(
8333
8470
  EarnPositionsList,
@@ -8359,6 +8496,6 @@ var CHAINS = {
8359
8496
  }
8360
8497
  };
8361
8498
 
8362
- export { AccountBalancesModal, ActionModal, ApiProvider, CHAINS, ChainSwitcher, CompassEarnWidget, CompassProvider, DEFAULT_SWAP_TOKENS, DepositWithdrawForm, EarnAccount, EarnAccountBalance, EarnAccountGuard, PnLSummary, RebalancingWidget, SwapWidget, ThemeProvider, TransactionHistory, WalletStatus, themePresets, useChain, useCompassApi, useCompassChain, useCompassWallet, useEarnAccount, useEmbeddableApi, useEmbeddableWallet, useRebalancingData, useSwapQuote, useTheme };
8499
+ export { AccountBalancesModal, ActionModal, ApiProvider, CHAINS, ChainSwitcher, CompassEarnWidget, CompassProvider, DepositWithdrawForm, EarnAccount, EarnAccountBalance, EarnAccountGuard, PnLSummary, RebalancingWidget, SwapForm, ThemeProvider, TransactionHistory, WalletStatus, themePresets, useChain, useCompassApi, useCompassChain, useCompassWallet, useEarnAccount, useEmbeddableApi, useEmbeddableWallet, useRebalancingData, useSwapQuote, useTheme };
8363
8500
  //# sourceMappingURL=index.mjs.map
8364
8501
  //# sourceMappingURL=index.mjs.map