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