@compass-labs/widgets 0.1.0 → 0.1.2

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
@@ -81,6 +81,7 @@ var WalletContext = react.createContext(null);
81
81
  var disconnectedWallet = {
82
82
  address: null,
83
83
  isConnected: false,
84
+ walletChainId: void 0,
84
85
  signTypedData: async () => {
85
86
  throw new Error("No wallet connected. Please connect a wallet first.");
86
87
  },
@@ -92,6 +93,7 @@ function WalletProvider({ children, wallet }) {
92
93
  const value = wallet ? {
93
94
  address: wallet.address,
94
95
  isConnected: wallet.address !== null,
96
+ walletChainId: wallet.chainId,
95
97
  signTypedData: wallet.signTypedData,
96
98
  switchChain: wallet.switchChain ?? null,
97
99
  login: wallet.login ?? null,
@@ -1123,7 +1125,7 @@ function PnLSummary({ pnl, tokenSymbol, tokenPrice }) {
1123
1125
  const prefix = value >= 0 ? "+" : "";
1124
1126
  return `${prefix}${value.toFixed(4)}`;
1125
1127
  };
1126
- const formatUSD = (value) => {
1128
+ const formatUSD2 = (value) => {
1127
1129
  const usdValue = value * tokenPrice;
1128
1130
  const prefix = usdValue >= 0 ? "+$" : "-$";
1129
1131
  return `${prefix}${Math.abs(usdValue).toFixed(2)}`;
@@ -1200,7 +1202,7 @@ function PnLSummary({ pnl, tokenSymbol, tokenPrice }) {
1200
1202
  children: formatPnL(unrealizedPnl)
1201
1203
  }
1202
1204
  ),
1203
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-mono", style: { color: pnlColor(unrealizedPnl) }, children: formatUSD(unrealizedPnl) })
1205
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-mono", style: { color: pnlColor(unrealizedPnl) }, children: formatUSD2(unrealizedPnl) })
1204
1206
  ] }),
1205
1207
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
1206
1208
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1219,7 +1221,7 @@ function PnLSummary({ pnl, tokenSymbol, tokenPrice }) {
1219
1221
  children: formatPnL(realizedPnl)
1220
1222
  }
1221
1223
  ),
1222
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-mono", style: { color: pnlColor(realizedPnl) }, children: formatUSD(realizedPnl) })
1224
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-mono", style: { color: pnlColor(realizedPnl) }, children: formatUSD2(realizedPnl) })
1223
1225
  ] })
1224
1226
  ] }),
1225
1227
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1244,7 +1246,7 @@ function PnLSummary({ pnl, tokenSymbol, tokenPrice }) {
1244
1246
  ] }),
1245
1247
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-mono", style: { color: pnlColor(totalPnl) }, children: [
1246
1248
  "(",
1247
- formatUSD(totalPnl),
1249
+ formatUSD2(totalPnl),
1248
1250
  ")"
1249
1251
  ] })
1250
1252
  ] })
@@ -1256,161 +1258,327 @@ function PnLSummary({ pnl, tokenSymbol, tokenPrice }) {
1256
1258
  )
1257
1259
  ] });
1258
1260
  }
1259
- var CHAINS = {
1260
- ethereum: {
1261
- id: "ethereum",
1262
- name: "Ethereum",
1263
- viemChain: chains.mainnet,
1264
- icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/info/logo.png"
1265
- },
1266
- base: {
1267
- id: "base",
1268
- name: "Base",
1269
- viemChain: chains.base,
1270
- icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/base/info/logo.png"
1271
- },
1272
- arbitrum: {
1273
- id: "arbitrum",
1274
- name: "Arbitrum",
1275
- viemChain: chains.arbitrum,
1276
- icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/arbitrum/info/logo.png"
1277
- }
1278
- };
1279
- var TOKEN_DECIMALS = {
1280
- USDC: 6,
1281
- USDT: 6,
1282
- DAI: 18,
1283
- ETH: 18,
1284
- WETH: 18,
1285
- WBTC: 8,
1286
- cbBTC: 8
1287
- };
1261
+
1262
+ // src/utils/format.ts
1263
+ function formatAmount(value) {
1264
+ const num = typeof value === "string" ? parseFloat(value) : value;
1265
+ if (isNaN(num)) return "0";
1266
+ return parseFloat(num.toFixed(6)).toString();
1267
+ }
1268
+ function formatUSD(value) {
1269
+ const num = typeof value === "string" ? parseFloat(value) : value;
1270
+ if (isNaN(num)) return "$0.00";
1271
+ return new Intl.NumberFormat("en-US", {
1272
+ style: "currency",
1273
+ currency: "USD",
1274
+ minimumFractionDigits: 2,
1275
+ maximumFractionDigits: 2
1276
+ }).format(num);
1277
+ }
1278
+ function TokenSelector({
1279
+ tokens,
1280
+ selectedToken,
1281
+ onSelect,
1282
+ balances,
1283
+ showBalances = true,
1284
+ disabled = false
1285
+ }) {
1286
+ const [isOpen, setIsOpen] = react.useState(false);
1287
+ const dropdownRef = react.useRef(null);
1288
+ react.useEffect(() => {
1289
+ function handleClickOutside(event) {
1290
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
1291
+ setIsOpen(false);
1292
+ }
1293
+ }
1294
+ document.addEventListener("mousedown", handleClickOutside);
1295
+ return () => document.removeEventListener("mousedown", handleClickOutside);
1296
+ }, []);
1297
+ const handleSelect = (token) => {
1298
+ onSelect(token);
1299
+ setIsOpen(false);
1300
+ };
1301
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: dropdownRef, children: [
1302
+ /* @__PURE__ */ jsxRuntime.jsxs(
1303
+ "button",
1304
+ {
1305
+ type: "button",
1306
+ onClick: () => !disabled && setIsOpen(!isOpen),
1307
+ disabled,
1308
+ className: "flex items-center gap-2 px-3 py-2 rounded-lg border transition-colors min-w-[120px]",
1309
+ style: {
1310
+ backgroundColor: "var(--compass-color-background)",
1311
+ borderColor: isOpen ? "var(--compass-color-primary)" : "var(--compass-color-border)",
1312
+ color: "var(--compass-color-text)",
1313
+ cursor: disabled ? "not-allowed" : "pointer",
1314
+ opacity: disabled ? 0.5 : 1
1315
+ },
1316
+ children: [
1317
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium flex-1 text-left", children: selectedToken }),
1318
+ /* @__PURE__ */ jsxRuntime.jsx(
1319
+ lucideReact.ChevronDown,
1320
+ {
1321
+ size: 16,
1322
+ style: {
1323
+ color: "var(--compass-color-text-secondary)",
1324
+ transform: isOpen ? "rotate(180deg)" : "rotate(0deg)",
1325
+ transition: "transform 0.2s"
1326
+ }
1327
+ }
1328
+ )
1329
+ ]
1330
+ }
1331
+ ),
1332
+ isOpen && /* @__PURE__ */ jsxRuntime.jsx(
1333
+ "div",
1334
+ {
1335
+ className: "absolute z-50 mt-1 w-full min-w-[160px] rounded-lg border overflow-y-auto",
1336
+ style: {
1337
+ backgroundColor: "var(--compass-color-surface)",
1338
+ borderColor: "var(--compass-color-border)",
1339
+ boxShadow: "var(--compass-shadow-lg)",
1340
+ maxHeight: "200px"
1341
+ },
1342
+ children: tokens.map((token) => {
1343
+ const balance = balances?.[token];
1344
+ const isSelected = token === selectedToken;
1345
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1346
+ "button",
1347
+ {
1348
+ type: "button",
1349
+ onClick: () => handleSelect(token),
1350
+ className: "w-full px-3 py-2 flex items-center justify-between transition-colors",
1351
+ style: {
1352
+ backgroundColor: isSelected ? "var(--compass-color-primary-muted)" : "transparent",
1353
+ color: "var(--compass-color-text)"
1354
+ },
1355
+ onMouseEnter: (e) => {
1356
+ if (!isSelected) {
1357
+ e.currentTarget.style.backgroundColor = "var(--compass-color-surface-hover)";
1358
+ }
1359
+ },
1360
+ onMouseLeave: (e) => {
1361
+ if (!isSelected) {
1362
+ e.currentTarget.style.backgroundColor = "transparent";
1363
+ }
1364
+ },
1365
+ children: [
1366
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: token }),
1367
+ showBalances && balance && /* @__PURE__ */ jsxRuntime.jsx(
1368
+ "span",
1369
+ {
1370
+ className: "text-sm font-mono",
1371
+ style: { color: "var(--compass-color-text-secondary)" },
1372
+ children: formatAmount(balance)
1373
+ }
1374
+ )
1375
+ ]
1376
+ },
1377
+ token
1378
+ );
1379
+ })
1380
+ }
1381
+ )
1382
+ ] });
1383
+ }
1288
1384
  function DepositWithdrawForm({
1289
1385
  venueType,
1290
1386
  venueAddress,
1291
- tokenSymbol,
1292
- tokenDecimals,
1387
+ venueToken,
1388
+ depositTokens = ["SBC", "AUSD", "USDC", "USDT", "WETH", "DAI"],
1293
1389
  positionBalance = "0",
1294
1390
  onSuccess,
1295
1391
  onError
1296
1392
  }) {
1297
1393
  const [activeTab, setActiveTab] = react.useState("deposit");
1394
+ const [selectedToken, setSelectedToken] = react.useState(venueToken);
1298
1395
  const [amount, setAmount] = react.useState("");
1299
1396
  const [isSubmitting, setIsSubmitting] = react.useState(false);
1300
1397
  const [statusMessage, setStatusMessage] = react.useState("");
1301
1398
  const [error, setError] = react.useState(null);
1302
1399
  const { address, isConnected, signTypedData, switchChain } = useCompassWallet();
1303
- const { client } = useEmbeddableApi();
1304
- const { chainId } = useChain();
1400
+ const { chainId, chain } = useChain();
1305
1401
  const { earnAccountAddress } = useEarnAccount();
1306
1402
  const queryClient = reactQuery.useQueryClient();
1307
- const chainConfig = CHAINS[chainId];
1308
- const targetChainId = chainConfig?.viemChain?.id;
1403
+ const needsSwap = activeTab === "deposit" && selectedToken !== venueToken;
1309
1404
  const { data: tokenBalance } = reactQuery.useQuery({
1310
- queryKey: ["earnAccountTokenBalance", chainId, earnAccountAddress, tokenSymbol],
1405
+ queryKey: ["earnAccountTokenBalance", chainId, earnAccountAddress, selectedToken],
1311
1406
  queryFn: async () => {
1312
1407
  if (!earnAccountAddress) return "0";
1313
1408
  try {
1314
- const response = await client.token.tokenBalance({
1315
- chain: chainId,
1316
- user: earnAccountAddress,
1317
- token: tokenSymbol
1318
- });
1319
- return response.amount || "0";
1320
- } catch (error2) {
1321
- console.error("Error fetching earn account token balance:", error2);
1409
+ const response = await fetch(
1410
+ `/api/compass/earn-account/balances?owner=${address}&chain=${chainId}&tokens=${selectedToken}`
1411
+ );
1412
+ if (!response.ok) return "0";
1413
+ const data = await response.json();
1414
+ return data.balances?.[selectedToken]?.balance || "0";
1415
+ } catch {
1322
1416
  return "0";
1323
1417
  }
1324
1418
  },
1325
- enabled: !!earnAccountAddress,
1419
+ enabled: !!earnAccountAddress && !!address,
1326
1420
  staleTime: 10 * 1e3
1327
1421
  });
1328
1422
  const availableBalance = tokenBalance || "0";
1329
1423
  const maxBalance = activeTab === "deposit" ? availableBalance : positionBalance;
1330
- const handleQuickAmount = react.useCallback((percentage) => {
1331
- const max = parseFloat(maxBalance);
1332
- if (isNaN(max)) return;
1333
- setAmount((max * percentage).toFixed(tokenDecimals > 6 ? 6 : tokenDecimals));
1334
- }, [maxBalance, tokenDecimals]);
1424
+ const handleQuickAmount = react.useCallback(
1425
+ (percentage) => {
1426
+ const max = parseFloat(maxBalance);
1427
+ if (isNaN(max)) return;
1428
+ setAmount(formatAmount(max * percentage));
1429
+ },
1430
+ [maxBalance]
1431
+ );
1335
1432
  const handleSubmit = react.useCallback(async () => {
1336
1433
  if (!address || !amount) return;
1337
1434
  setIsSubmitting(true);
1338
1435
  setStatusMessage("Preparing transaction...");
1339
1436
  setError(null);
1340
1437
  try {
1341
- if (switchChain && targetChainId) {
1342
- setStatusMessage("Checking network...");
1438
+ const targetChainId = chain.viemChain.id;
1439
+ if (switchChain) {
1440
+ setStatusMessage("Switching network...");
1343
1441
  try {
1344
1442
  await switchChain(targetChainId);
1345
1443
  } catch {
1444
+ throw new Error(`Please switch your wallet to ${chain.name} to continue`);
1346
1445
  }
1347
1446
  }
1348
1447
  const isDeposit = activeTab === "deposit";
1349
- const prepareEndpoint = isDeposit ? "/api/compass/deposit/prepare" : "/api/compass/withdraw/prepare";
1350
- const executeEndpoint = isDeposit ? "/api/compass/deposit/execute" : "/api/compass/withdraw/execute";
1351
- const prepareBody = {
1352
- amount,
1353
- token: tokenSymbol,
1354
- owner: address,
1355
- chain: chainId,
1356
- venueType
1357
- };
1358
- if (venueType === "VAULT") {
1359
- prepareBody.vaultAddress = venueAddress;
1360
- } else if (venueType === "PENDLE_PT") {
1361
- prepareBody.marketAddress = venueAddress;
1362
- prepareBody.maxSlippagePercent = 1;
1363
- }
1364
- setStatusMessage("Getting transaction data...");
1365
- const prepareResponse = await fetch(prepareEndpoint, {
1366
- method: "POST",
1367
- headers: { "Content-Type": "application/json" },
1368
- body: JSON.stringify(prepareBody)
1369
- });
1370
- if (!prepareResponse.ok) {
1371
- const errorData = await prepareResponse.json();
1372
- throw new Error(errorData.error || "Failed to prepare transaction");
1373
- }
1374
- const { eip712, normalizedTypes, domain, message } = await prepareResponse.json();
1375
- setStatusMessage("Please sign the transaction...");
1376
- const signature = await signTypedData({
1377
- domain,
1378
- types: normalizedTypes,
1379
- primaryType: "SafeTx",
1380
- message
1381
- });
1382
- setStatusMessage("Executing transaction...");
1383
- const executeResponse = await fetch(executeEndpoint, {
1384
- method: "POST",
1385
- headers: { "Content-Type": "application/json" },
1386
- body: JSON.stringify({
1448
+ if (isDeposit && needsSwap) {
1449
+ setStatusMessage("Getting swap quote...");
1450
+ const quoteResponse = await fetch(
1451
+ `/api/compass/swap/quote?owner=${address}&chain=${chainId}&tokenIn=${selectedToken}&tokenOut=${venueToken}&amountIn=${amount}`
1452
+ );
1453
+ if (!quoteResponse.ok) {
1454
+ const errorData = await quoteResponse.json();
1455
+ throw new Error(errorData.error || "Failed to get swap quote");
1456
+ }
1457
+ const quoteData = await quoteResponse.json();
1458
+ const estimatedOutput = quoteData.estimatedAmountOut;
1459
+ if (!estimatedOutput || parseFloat(estimatedOutput) <= 0) {
1460
+ throw new Error("Invalid swap quote - no output amount");
1461
+ }
1462
+ const depositAmount = (parseFloat(estimatedOutput) * 0.99).toString();
1463
+ setStatusMessage("Preparing swap and deposit...");
1464
+ const bundleActions = [
1465
+ {
1466
+ body: {
1467
+ actionType: "V2_SWAP",
1468
+ tokenIn: selectedToken,
1469
+ tokenOut: venueToken,
1470
+ amountIn: amount,
1471
+ maxSlippagePercent: 1
1472
+ }
1473
+ },
1474
+ {
1475
+ body: {
1476
+ actionType: "V2_MANAGE",
1477
+ action: "DEPOSIT",
1478
+ venue: venueType === "VAULT" ? { type: "VAULT", vaultAddress: venueAddress } : venueType === "AAVE" ? { type: "AAVE", token: venueToken } : { type: "PENDLE_PT", marketAddress: venueAddress },
1479
+ amount: depositAmount
1480
+ }
1481
+ }
1482
+ ];
1483
+ const prepareResponse = await fetch("/api/compass/bundle/prepare", {
1484
+ method: "POST",
1485
+ headers: { "Content-Type": "application/json" },
1486
+ body: JSON.stringify({
1487
+ owner: address,
1488
+ chain: chainId,
1489
+ actions: bundleActions
1490
+ })
1491
+ });
1492
+ if (!prepareResponse.ok) {
1493
+ const errorData = await prepareResponse.json();
1494
+ throw new Error(errorData.error || "Failed to prepare bundle");
1495
+ }
1496
+ const { eip712, normalizedTypes, domain, message } = await prepareResponse.json();
1497
+ setStatusMessage("Please sign the transaction...");
1498
+ const signature = await signTypedData({
1499
+ domain,
1500
+ types: normalizedTypes,
1501
+ primaryType: "SafeTx",
1502
+ message
1503
+ });
1504
+ setStatusMessage("Executing swap and deposit...");
1505
+ const executeResponse = await fetch("/api/compass/bundle/execute", {
1506
+ method: "POST",
1507
+ headers: { "Content-Type": "application/json" },
1508
+ body: JSON.stringify({
1509
+ owner: address,
1510
+ eip712,
1511
+ signature,
1512
+ chain: chainId
1513
+ })
1514
+ });
1515
+ if (!executeResponse.ok) {
1516
+ const errorData = await executeResponse.json();
1517
+ throw new Error(errorData.error || "Failed to execute bundle");
1518
+ }
1519
+ const { txHash } = await executeResponse.json();
1520
+ setStatusMessage("Transaction successful!");
1521
+ onSuccess?.(activeTab, amount, txHash);
1522
+ setAmount("");
1523
+ } else {
1524
+ const prepareEndpoint = isDeposit ? "/api/compass/deposit/prepare" : "/api/compass/withdraw/prepare";
1525
+ const executeEndpoint = isDeposit ? "/api/compass/deposit/execute" : "/api/compass/withdraw/execute";
1526
+ const prepareBody = {
1527
+ amount,
1528
+ token: venueToken,
1387
1529
  owner: address,
1388
- eip712,
1389
- signature,
1390
- chain: chainId
1391
- })
1392
- });
1393
- if (!executeResponse.ok) {
1394
- const errorData = await executeResponse.json();
1395
- throw new Error(errorData.error || "Failed to execute transaction");
1530
+ chain: chainId,
1531
+ venueType
1532
+ };
1533
+ if (venueType === "VAULT") {
1534
+ prepareBody.vaultAddress = venueAddress;
1535
+ } else if (venueType === "PENDLE_PT") {
1536
+ prepareBody.marketAddress = venueAddress;
1537
+ prepareBody.maxSlippagePercent = 1;
1538
+ }
1539
+ setStatusMessage("Getting transaction data...");
1540
+ const prepareResponse = await fetch(prepareEndpoint, {
1541
+ method: "POST",
1542
+ headers: { "Content-Type": "application/json" },
1543
+ body: JSON.stringify(prepareBody)
1544
+ });
1545
+ if (!prepareResponse.ok) {
1546
+ const errorData = await prepareResponse.json();
1547
+ throw new Error(errorData.error || "Failed to prepare transaction");
1548
+ }
1549
+ const { eip712, normalizedTypes, domain, message } = await prepareResponse.json();
1550
+ setStatusMessage("Please sign the transaction...");
1551
+ const signature = await signTypedData({
1552
+ domain,
1553
+ types: normalizedTypes,
1554
+ primaryType: "SafeTx",
1555
+ message
1556
+ });
1557
+ setStatusMessage("Executing transaction...");
1558
+ const executeResponse = await fetch(executeEndpoint, {
1559
+ method: "POST",
1560
+ headers: { "Content-Type": "application/json" },
1561
+ body: JSON.stringify({
1562
+ owner: address,
1563
+ eip712,
1564
+ signature,
1565
+ chain: chainId
1566
+ })
1567
+ });
1568
+ if (!executeResponse.ok) {
1569
+ const errorData = await executeResponse.json();
1570
+ throw new Error(errorData.error || "Failed to execute transaction");
1571
+ }
1572
+ const { txHash } = await executeResponse.json();
1573
+ setStatusMessage("Transaction successful!");
1574
+ onSuccess?.(activeTab, amount, txHash);
1575
+ setAmount("");
1396
1576
  }
1397
- const { txHash } = await executeResponse.json();
1398
- setStatusMessage("Transaction successful!");
1399
- onSuccess?.(activeTab, amount, txHash);
1400
- setAmount("");
1401
1577
  queryClient.invalidateQueries({ queryKey: ["earnAccountTokenBalance"] });
1402
1578
  queryClient.invalidateQueries({ queryKey: ["vaults"] });
1403
- queryClient.invalidateQueries({ queryKey: ["vaultPositions"] });
1404
1579
  queryClient.invalidateQueries({ queryKey: ["aaveMarkets"] });
1405
- queryClient.invalidateQueries({ queryKey: ["aavePositions"] });
1406
1580
  queryClient.invalidateQueries({ queryKey: ["pendleMarkets"] });
1407
- queryClient.invalidateQueries({ queryKey: ["pendlePositions"] });
1408
1581
  setTimeout(() => setStatusMessage(""), 3e3);
1409
- setTimeout(() => {
1410
- queryClient.invalidateQueries({ queryKey: ["earnAccountTokenBalance"] });
1411
- queryClient.invalidateQueries({ queryKey: ["vaults"] });
1412
- queryClient.invalidateQueries({ queryKey: ["vaultPositions"] });
1413
- }, 5e3);
1414
1582
  } catch (err) {
1415
1583
  console.error("Transaction failed:", err);
1416
1584
  const errorMessage = err instanceof Error ? err.message : "Transaction failed";
@@ -1423,11 +1591,13 @@ function DepositWithdrawForm({
1423
1591
  address,
1424
1592
  amount,
1425
1593
  chainId,
1426
- targetChainId,
1594
+ chain,
1427
1595
  activeTab,
1596
+ needsSwap,
1597
+ selectedToken,
1598
+ venueToken,
1428
1599
  venueType,
1429
1600
  venueAddress,
1430
- tokenSymbol,
1431
1601
  signTypedData,
1432
1602
  switchChain,
1433
1603
  queryClient,
@@ -1468,6 +1638,7 @@ function DepositWithdrawForm({
1468
1638
  {
1469
1639
  onClick: () => {
1470
1640
  setActiveTab(tab);
1641
+ setSelectedToken(venueToken);
1471
1642
  setError(null);
1472
1643
  setStatusMessage("");
1473
1644
  },
@@ -1482,6 +1653,45 @@ function DepositWithdrawForm({
1482
1653
  ))
1483
1654
  }
1484
1655
  ),
1656
+ activeTab === "deposit" && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1657
+ /* @__PURE__ */ jsxRuntime.jsx(
1658
+ "label",
1659
+ {
1660
+ className: "text-sm font-medium mb-1 block",
1661
+ style: { color: "var(--compass-color-text-secondary)" },
1662
+ children: "From Token"
1663
+ }
1664
+ ),
1665
+ /* @__PURE__ */ jsxRuntime.jsx(
1666
+ TokenSelector,
1667
+ {
1668
+ tokens: depositTokens,
1669
+ selectedToken,
1670
+ onSelect: setSelectedToken,
1671
+ disabled: isSubmitting
1672
+ }
1673
+ )
1674
+ ] }),
1675
+ needsSwap && /* @__PURE__ */ jsxRuntime.jsxs(
1676
+ "div",
1677
+ {
1678
+ className: "flex items-center gap-2 p-2 rounded-lg text-sm",
1679
+ style: {
1680
+ backgroundColor: "var(--compass-color-primary-muted)",
1681
+ color: "var(--compass-color-primary)"
1682
+ },
1683
+ children: [
1684
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { size: 14 }),
1685
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1686
+ "Swaps ",
1687
+ selectedToken,
1688
+ " to ",
1689
+ venueToken,
1690
+ ", then deposits"
1691
+ ] })
1692
+ ]
1693
+ }
1694
+ ),
1485
1695
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1486
1696
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [
1487
1697
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1494,9 +1704,9 @@ function DepositWithdrawForm({
1494
1704
  ),
1495
1705
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: [
1496
1706
  "Available: ",
1497
- parseFloat(maxBalance).toFixed(4),
1707
+ formatAmount(maxBalance),
1498
1708
  " ",
1499
- tokenSymbol
1709
+ activeTab === "deposit" ? selectedToken : venueToken
1500
1710
  ] })
1501
1711
  ] }),
1502
1712
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1527,7 +1737,7 @@ function DepositWithdrawForm({
1527
1737
  {
1528
1738
  className: "text-sm font-medium",
1529
1739
  style: { color: "var(--compass-color-text-secondary)" },
1530
- children: tokenSymbol
1740
+ children: activeTab === "deposit" ? selectedToken : venueToken
1531
1741
  }
1532
1742
  )
1533
1743
  ]
@@ -1552,8 +1762,8 @@ function DepositWithdrawForm({
1552
1762
  {
1553
1763
  className: "p-3 rounded-lg text-sm",
1554
1764
  style: {
1555
- backgroundColor: "var(--compass-color-error-muted, rgba(239, 68, 68, 0.1))",
1556
- color: "var(--compass-color-error, #ef4444)"
1765
+ backgroundColor: "var(--compass-color-error-muted)",
1766
+ color: "var(--compass-color-error)"
1557
1767
  },
1558
1768
  children: error
1559
1769
  }
@@ -1563,8 +1773,8 @@ function DepositWithdrawForm({
1563
1773
  {
1564
1774
  className: "p-3 rounded-lg text-sm text-center",
1565
1775
  style: {
1566
- backgroundColor: "var(--compass-color-success-muted, rgba(34, 197, 94, 0.1))",
1567
- color: "var(--compass-color-success, #22c55e)"
1776
+ backgroundColor: "var(--compass-color-success-muted)",
1777
+ color: "var(--compass-color-success)"
1568
1778
  },
1569
1779
  children: statusMessage
1570
1780
  }
@@ -1581,7 +1791,7 @@ function DepositWithdrawForm({
1581
1791
  },
1582
1792
  children: [
1583
1793
  isSubmitting && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 18, className: "animate-spin" }),
1584
- isSubmitting ? "Processing..." : activeTab === "deposit" ? "Deposit" : "Withdraw"
1794
+ isSubmitting ? "Processing..." : needsSwap ? `Swap & ${activeTab === "deposit" ? "Deposit" : "Withdraw"}` : activeTab === "deposit" ? "Deposit" : "Withdraw"
1585
1795
  ]
1586
1796
  }
1587
1797
  )
@@ -1889,6 +2099,678 @@ function EarnAccountGuard({
1889
2099
  }
1890
2100
  );
1891
2101
  }
2102
+ function AccountBalancesModal({
2103
+ isOpen,
2104
+ onClose,
2105
+ balances,
2106
+ totalUsdValue,
2107
+ isLoading = false,
2108
+ earnAccountAddress
2109
+ }) {
2110
+ react.useEffect(() => {
2111
+ const handleEscape = (e) => {
2112
+ if (e.key === "Escape") onClose();
2113
+ };
2114
+ if (isOpen) {
2115
+ document.addEventListener("keydown", handleEscape);
2116
+ document.body.style.overflow = "hidden";
2117
+ }
2118
+ return () => {
2119
+ document.removeEventListener("keydown", handleEscape);
2120
+ document.body.style.overflow = "";
2121
+ };
2122
+ }, [isOpen, onClose]);
2123
+ if (!isOpen) return null;
2124
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
2125
+ /* @__PURE__ */ jsxRuntime.jsx(
2126
+ "div",
2127
+ {
2128
+ className: "absolute inset-0",
2129
+ style: { backgroundColor: "var(--compass-color-overlay)" },
2130
+ onClick: onClose
2131
+ }
2132
+ ),
2133
+ /* @__PURE__ */ jsxRuntime.jsxs(
2134
+ "div",
2135
+ {
2136
+ className: "relative w-full max-w-md mx-4 rounded-xl overflow-hidden",
2137
+ style: {
2138
+ backgroundColor: "var(--compass-color-surface)",
2139
+ boxShadow: "var(--compass-shadow-lg)"
2140
+ },
2141
+ children: [
2142
+ /* @__PURE__ */ jsxRuntime.jsxs(
2143
+ "div",
2144
+ {
2145
+ className: "flex items-center justify-between px-4 py-3 border-b",
2146
+ style: { borderColor: "var(--compass-color-border)" },
2147
+ children: [
2148
+ /* @__PURE__ */ jsxRuntime.jsxs(
2149
+ "h2",
2150
+ {
2151
+ className: "font-semibold flex items-center gap-2",
2152
+ style: {
2153
+ fontSize: "var(--compass-font-size-subheading)",
2154
+ color: "var(--compass-color-text)"
2155
+ },
2156
+ children: [
2157
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Coins, { size: 20 }),
2158
+ "Account Balances"
2159
+ ]
2160
+ }
2161
+ ),
2162
+ /* @__PURE__ */ jsxRuntime.jsx(
2163
+ "button",
2164
+ {
2165
+ onClick: onClose,
2166
+ className: "p-1 rounded-md transition-colors hover:opacity-70",
2167
+ style: { color: "var(--compass-color-text-secondary)" },
2168
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 20 })
2169
+ }
2170
+ )
2171
+ ]
2172
+ }
2173
+ ),
2174
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
2175
+ isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(
2176
+ lucideReact.Loader2,
2177
+ {
2178
+ size: 24,
2179
+ className: "animate-spin",
2180
+ style: { color: "var(--compass-color-primary)" }
2181
+ }
2182
+ ) }) : balances.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
2183
+ "div",
2184
+ {
2185
+ className: "text-center py-8",
2186
+ style: { color: "var(--compass-color-text-secondary)" },
2187
+ children: "No token balances found"
2188
+ }
2189
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
2190
+ balances.map((token) => /* @__PURE__ */ jsxRuntime.jsxs(
2191
+ "div",
2192
+ {
2193
+ className: "flex items-center justify-between p-3 rounded-lg",
2194
+ style: { backgroundColor: "var(--compass-color-background)" },
2195
+ children: [
2196
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2197
+ /* @__PURE__ */ jsxRuntime.jsx(
2198
+ "div",
2199
+ {
2200
+ className: "w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold",
2201
+ style: {
2202
+ backgroundColor: "var(--compass-color-primary-muted)",
2203
+ color: "var(--compass-color-primary)"
2204
+ },
2205
+ children: token.symbol.slice(0, 2)
2206
+ }
2207
+ ),
2208
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2209
+ /* @__PURE__ */ jsxRuntime.jsx(
2210
+ "div",
2211
+ {
2212
+ className: "font-medium",
2213
+ style: { color: "var(--compass-color-text)" },
2214
+ children: token.symbol
2215
+ }
2216
+ ),
2217
+ /* @__PURE__ */ jsxRuntime.jsx(
2218
+ "div",
2219
+ {
2220
+ className: "text-sm font-mono",
2221
+ style: { color: "var(--compass-color-text-secondary)" },
2222
+ children: formatAmount(token.balance)
2223
+ }
2224
+ )
2225
+ ] })
2226
+ ] }),
2227
+ /* @__PURE__ */ jsxRuntime.jsx(
2228
+ "div",
2229
+ {
2230
+ className: "font-medium",
2231
+ style: { color: "var(--compass-color-text)" },
2232
+ children: formatUSD(token.usdValue)
2233
+ }
2234
+ )
2235
+ ]
2236
+ },
2237
+ token.symbol
2238
+ )),
2239
+ /* @__PURE__ */ jsxRuntime.jsxs(
2240
+ "div",
2241
+ {
2242
+ className: "flex items-center justify-between p-3 mt-2 rounded-lg border",
2243
+ style: {
2244
+ borderColor: "var(--compass-color-border)",
2245
+ backgroundColor: "var(--compass-color-surface)"
2246
+ },
2247
+ children: [
2248
+ /* @__PURE__ */ jsxRuntime.jsx(
2249
+ "span",
2250
+ {
2251
+ className: "font-medium",
2252
+ style: { color: "var(--compass-color-text-secondary)" },
2253
+ children: "Total Balance"
2254
+ }
2255
+ ),
2256
+ /* @__PURE__ */ jsxRuntime.jsx(
2257
+ "span",
2258
+ {
2259
+ className: "font-bold text-lg",
2260
+ style: { color: "var(--compass-color-text)" },
2261
+ children: formatUSD(totalUsdValue)
2262
+ }
2263
+ )
2264
+ ]
2265
+ }
2266
+ )
2267
+ ] }),
2268
+ earnAccountAddress && /* @__PURE__ */ jsxRuntime.jsx(
2269
+ "div",
2270
+ {
2271
+ className: "mt-4 pt-4 border-t text-center",
2272
+ style: { borderColor: "var(--compass-color-border)" },
2273
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
2274
+ "span",
2275
+ {
2276
+ className: "text-xs",
2277
+ style: { color: "var(--compass-color-text-tertiary)" },
2278
+ children: [
2279
+ "Earn Account: ",
2280
+ earnAccountAddress.slice(0, 6),
2281
+ "...",
2282
+ earnAccountAddress.slice(-4)
2283
+ ]
2284
+ }
2285
+ )
2286
+ }
2287
+ )
2288
+ ] })
2289
+ ]
2290
+ }
2291
+ )
2292
+ ] });
2293
+ }
2294
+ var TRANSFER_TOKENS = ["USDC"];
2295
+ function EarnAccountBalance({
2296
+ compact = false,
2297
+ onTransferComplete
2298
+ }) {
2299
+ const [isModalOpen, setIsModalOpen] = react.useState(false);
2300
+ const [activeAction, setActiveAction] = react.useState("deposit");
2301
+ const [selectedToken, setSelectedToken] = react.useState(TRANSFER_TOKENS[0]);
2302
+ const [amount, setAmount] = react.useState("");
2303
+ const [transferState, setTransferState] = react.useState("idle");
2304
+ const [statusMessage, setStatusMessage] = react.useState("");
2305
+ const [error, setError] = react.useState(null);
2306
+ const [isBalancesModalOpen, setIsBalancesModalOpen] = react.useState(false);
2307
+ const { address, isConnected, signTypedData, switchChain, walletChainId } = useEmbeddableWallet();
2308
+ const { chainId, chain } = useChain();
2309
+ const { earnAccountAddress, isDeployed } = useEarnAccount();
2310
+ const queryClient = reactQuery.useQueryClient();
2311
+ const { data: balanceData, isLoading: balancesLoading } = reactQuery.useQuery({
2312
+ queryKey: ["earnAccountBalances", chainId, address],
2313
+ queryFn: async () => {
2314
+ if (!address) return null;
2315
+ const response = await fetch(
2316
+ `/api/compass/earn-account/balances?owner=${address}&chain=${chainId}`
2317
+ );
2318
+ if (!response.ok) {
2319
+ throw new Error("Failed to fetch balances");
2320
+ }
2321
+ return response.json();
2322
+ },
2323
+ enabled: !!address && isDeployed,
2324
+ staleTime: 30 * 1e3
2325
+ });
2326
+ const { data: eoaBalances } = reactQuery.useQuery({
2327
+ queryKey: ["eoaBalances", chainId, address, TRANSFER_TOKENS.join(",")],
2328
+ queryFn: async () => {
2329
+ if (!address) return {};
2330
+ const balances = {};
2331
+ for (const token of TRANSFER_TOKENS) {
2332
+ try {
2333
+ const response = await fetch(
2334
+ `/api/compass/token/balance?chain=${chainId}&token=${token}&address=${address}`
2335
+ );
2336
+ if (response.ok) {
2337
+ const data = await response.json();
2338
+ balances[token] = data.balance || "0";
2339
+ }
2340
+ } catch {
2341
+ }
2342
+ }
2343
+ return balances;
2344
+ },
2345
+ enabled: !!address && activeAction === "deposit",
2346
+ staleTime: 30 * 1e3
2347
+ });
2348
+ const earnBalances = balanceData?.balances || {};
2349
+ const totalUsdValue = parseFloat(balanceData?.totalUsdValue || "0");
2350
+ const tokenBalances = Object.entries(earnBalances).filter(([, data]) => {
2351
+ const usdVal = parseFloat(data.usdValue || "0");
2352
+ return usdVal > 0;
2353
+ }).map(([symbol, data]) => ({
2354
+ symbol,
2355
+ balance: data.balance || "0",
2356
+ usdValue: data.usdValue || "0"
2357
+ }));
2358
+ const earnBalanceAmounts = {};
2359
+ for (const [symbol, data] of Object.entries(earnBalances)) {
2360
+ earnBalanceAmounts[symbol] = data.balance;
2361
+ }
2362
+ const resetForm = react.useCallback(() => {
2363
+ setAmount("");
2364
+ setTransferState("idle");
2365
+ setStatusMessage("");
2366
+ setError(null);
2367
+ }, []);
2368
+ const handleOpenModal = () => {
2369
+ resetForm();
2370
+ setIsModalOpen(true);
2371
+ };
2372
+ const handleCloseModal = () => {
2373
+ setIsModalOpen(false);
2374
+ resetForm();
2375
+ };
2376
+ const handleActionChange = (action) => {
2377
+ setActiveAction(action);
2378
+ setAmount("");
2379
+ setError(null);
2380
+ };
2381
+ const getMaxBalance = () => {
2382
+ if (activeAction === "deposit") {
2383
+ return eoaBalances?.[selectedToken] || "0";
2384
+ }
2385
+ return earnBalanceAmounts[selectedToken] || "0";
2386
+ };
2387
+ const handleQuickAmount = (percentage) => {
2388
+ const max = parseFloat(getMaxBalance());
2389
+ if (isNaN(max)) return;
2390
+ setAmount(formatAmount(max * percentage));
2391
+ };
2392
+ const handleTransfer = react.useCallback(async () => {
2393
+ if (!address || !amount || !signTypedData) return;
2394
+ setError(null);
2395
+ try {
2396
+ const targetChainId = chain.viemChain.id;
2397
+ if (walletChainId !== void 0 && walletChainId !== targetChainId) {
2398
+ if (!switchChain) {
2399
+ throw new Error(`Please switch your wallet to ${chain.name} (chain ID ${targetChainId}) to continue. Your wallet is currently on chain ID ${walletChainId}.`);
2400
+ }
2401
+ setStatusMessage(`Switching network to ${chain.name}...`);
2402
+ try {
2403
+ await switchChain(targetChainId);
2404
+ } catch (switchError) {
2405
+ throw new Error(`Please switch your wallet to ${chain.name} to continue. Your wallet is on chain ID ${walletChainId}, but ${chain.name} requires chain ID ${targetChainId}.`);
2406
+ }
2407
+ } else if (switchChain && walletChainId === void 0) {
2408
+ setStatusMessage("Switching network...");
2409
+ try {
2410
+ await switchChain(targetChainId);
2411
+ } catch (switchError) {
2412
+ throw new Error(`Please switch your wallet to ${chain.name} to continue`);
2413
+ }
2414
+ }
2415
+ if (activeAction === "deposit") {
2416
+ setTransferState("checking_approval");
2417
+ setStatusMessage("Checking token approval...");
2418
+ const approveResponse = await fetch("/api/compass/transfer/approve", {
2419
+ method: "POST",
2420
+ headers: { "Content-Type": "application/json" },
2421
+ body: JSON.stringify({
2422
+ owner: address,
2423
+ chain: chainId,
2424
+ token: selectedToken
2425
+ })
2426
+ });
2427
+ if (!approveResponse.ok) {
2428
+ const errData = await approveResponse.json();
2429
+ throw new Error(errData.error || "Failed to check approval");
2430
+ }
2431
+ const approvalData = await approveResponse.json();
2432
+ if (!approvalData.approved) {
2433
+ if (approvalData.requiresTransaction) {
2434
+ throw new Error("This token requires a transaction-based approval. Please approve manually.");
2435
+ }
2436
+ setTransferState("awaiting_approval_signature");
2437
+ setStatusMessage("Please sign the approval...");
2438
+ const approvalSignature = await signTypedData({
2439
+ domain: approvalData.domain,
2440
+ types: approvalData.normalizedTypes,
2441
+ primaryType: "Permit",
2442
+ message: approvalData.message
2443
+ });
2444
+ setTransferState("approving");
2445
+ setStatusMessage("Executing approval...");
2446
+ const executeApprovalResponse = await fetch("/api/compass/transfer/execute", {
2447
+ method: "POST",
2448
+ headers: { "Content-Type": "application/json" },
2449
+ body: JSON.stringify({
2450
+ owner: address,
2451
+ chain: chainId,
2452
+ eip712: approvalData.eip712,
2453
+ signature: approvalSignature
2454
+ })
2455
+ });
2456
+ if (!executeApprovalResponse.ok) {
2457
+ const errData = await executeApprovalResponse.json();
2458
+ throw new Error(errData.error || "Approval failed");
2459
+ }
2460
+ }
2461
+ }
2462
+ setTransferState("awaiting_transfer_signature");
2463
+ setStatusMessage("Preparing transfer...");
2464
+ const prepareResponse = await fetch("/api/compass/transfer/prepare", {
2465
+ method: "POST",
2466
+ headers: { "Content-Type": "application/json" },
2467
+ body: JSON.stringify({
2468
+ owner: address,
2469
+ chain: chainId,
2470
+ token: selectedToken,
2471
+ amount,
2472
+ action: activeAction.toUpperCase()
2473
+ })
2474
+ });
2475
+ if (!prepareResponse.ok) {
2476
+ const errData = await prepareResponse.json();
2477
+ throw new Error(errData.error || "Failed to prepare transfer");
2478
+ }
2479
+ const prepareData = await prepareResponse.json();
2480
+ setStatusMessage("Please sign the transfer...");
2481
+ const transferSignature = await signTypedData({
2482
+ domain: prepareData.domain,
2483
+ types: prepareData.normalizedTypes,
2484
+ primaryType: prepareData.primaryType,
2485
+ message: prepareData.message
2486
+ });
2487
+ setTransferState("transferring");
2488
+ setStatusMessage("Executing transfer...");
2489
+ const executeResponse = await fetch("/api/compass/transfer/execute", {
2490
+ method: "POST",
2491
+ headers: { "Content-Type": "application/json" },
2492
+ body: JSON.stringify({
2493
+ owner: address,
2494
+ chain: chainId,
2495
+ eip712: prepareData.eip712,
2496
+ signature: transferSignature
2497
+ })
2498
+ });
2499
+ if (!executeResponse.ok) {
2500
+ const errData = await executeResponse.json();
2501
+ throw new Error(errData.error || "Transfer failed");
2502
+ }
2503
+ const { txHash } = await executeResponse.json();
2504
+ setTransferState("success");
2505
+ setStatusMessage("Transfer successful!");
2506
+ onTransferComplete?.(activeAction, selectedToken, amount, txHash);
2507
+ queryClient.invalidateQueries({ queryKey: ["earnAccountBalances"] });
2508
+ queryClient.invalidateQueries({ queryKey: ["eoaBalances"] });
2509
+ setTimeout(() => {
2510
+ resetForm();
2511
+ }, 2e3);
2512
+ } catch (err) {
2513
+ console.error("Transfer failed:", err);
2514
+ setTransferState("error");
2515
+ setError(err instanceof Error ? err.message : "Transfer failed");
2516
+ }
2517
+ }, [
2518
+ address,
2519
+ amount,
2520
+ activeAction,
2521
+ selectedToken,
2522
+ chainId,
2523
+ chain,
2524
+ walletChainId,
2525
+ signTypedData,
2526
+ switchChain,
2527
+ queryClient,
2528
+ onTransferComplete,
2529
+ resetForm
2530
+ ]);
2531
+ if (!isConnected) {
2532
+ return null;
2533
+ }
2534
+ const isProcessing = transferState !== "idle" && transferState !== "success" && transferState !== "error";
2535
+ if (!isDeployed) {
2536
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2537
+ "div",
2538
+ {
2539
+ className: `flex items-center gap-2 rounded-lg border ${compact ? "px-2 py-1.5" : "px-3 py-2"}`,
2540
+ style: {
2541
+ backgroundColor: "var(--compass-color-surface)",
2542
+ borderColor: "var(--compass-color-border)"
2543
+ },
2544
+ children: [
2545
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Wallet, { size: compact ? 14 : 16, style: { color: "var(--compass-color-text-secondary)" } }),
2546
+ /* @__PURE__ */ jsxRuntime.jsx(
2547
+ "span",
2548
+ {
2549
+ className: `${compact ? "text-xs" : "text-sm"}`,
2550
+ style: { color: "var(--compass-color-text-secondary)" },
2551
+ children: "No Earn Account"
2552
+ }
2553
+ )
2554
+ ]
2555
+ }
2556
+ );
2557
+ }
2558
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2559
+ /* @__PURE__ */ jsxRuntime.jsxs(
2560
+ "button",
2561
+ {
2562
+ onClick: () => setIsBalancesModalOpen(true),
2563
+ className: `flex items-center gap-2 rounded-lg border transition-colors hover:border-[var(--compass-color-primary)] ${compact ? "px-2 py-1.5" : "px-3 py-2"}`,
2564
+ style: {
2565
+ backgroundColor: "var(--compass-color-surface)",
2566
+ borderColor: "var(--compass-color-border)",
2567
+ cursor: "pointer"
2568
+ },
2569
+ children: [
2570
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Wallet, { size: compact ? 14 : 16, style: { color: "var(--compass-color-primary)" } }),
2571
+ balancesLoading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: compact ? 12 : 14, className: "animate-spin", style: { color: "var(--compass-color-text-secondary)" } }) : /* @__PURE__ */ jsxRuntime.jsx(
2572
+ "span",
2573
+ {
2574
+ className: `font-medium ${compact ? "text-xs" : "text-sm"}`,
2575
+ style: { color: "var(--compass-color-text)" },
2576
+ children: formatUSD(totalUsdValue)
2577
+ }
2578
+ )
2579
+ ]
2580
+ }
2581
+ ),
2582
+ /* @__PURE__ */ jsxRuntime.jsx(
2583
+ "button",
2584
+ {
2585
+ onClick: handleOpenModal,
2586
+ className: `rounded-md font-medium transition-colors ${compact ? "px-2 py-1 text-xs" : "px-3 py-1.5 text-sm"}`,
2587
+ style: {
2588
+ backgroundColor: "var(--compass-color-primary)",
2589
+ color: "var(--compass-color-primary-text)"
2590
+ },
2591
+ children: "Fund"
2592
+ }
2593
+ ),
2594
+ /* @__PURE__ */ jsxRuntime.jsx(
2595
+ AccountBalancesModal,
2596
+ {
2597
+ isOpen: isBalancesModalOpen,
2598
+ onClose: () => setIsBalancesModalOpen(false),
2599
+ balances: tokenBalances,
2600
+ totalUsdValue: totalUsdValue.toString(),
2601
+ isLoading: balancesLoading,
2602
+ earnAccountAddress: earnAccountAddress ?? void 0
2603
+ }
2604
+ ),
2605
+ /* @__PURE__ */ jsxRuntime.jsx(
2606
+ ActionModal,
2607
+ {
2608
+ isOpen: isModalOpen,
2609
+ onClose: handleCloseModal,
2610
+ title: "Transfer Funds",
2611
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
2612
+ /* @__PURE__ */ jsxRuntime.jsx(
2613
+ "div",
2614
+ {
2615
+ className: "flex gap-1 p-1 rounded-lg",
2616
+ style: { backgroundColor: "var(--compass-color-background)" },
2617
+ children: ["deposit", "withdraw"].map((action) => /* @__PURE__ */ jsxRuntime.jsxs(
2618
+ "button",
2619
+ {
2620
+ onClick: () => handleActionChange(action),
2621
+ disabled: isProcessing,
2622
+ className: "flex-1 py-2 rounded-md text-sm font-medium capitalize transition-all flex items-center justify-center gap-2",
2623
+ style: {
2624
+ backgroundColor: activeAction === action ? "var(--compass-color-surface)" : "transparent",
2625
+ color: activeAction === action ? "var(--compass-color-text)" : "var(--compass-color-text-secondary)"
2626
+ },
2627
+ children: [
2628
+ action === "deposit" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownLeft, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpRight, { size: 14 }),
2629
+ action
2630
+ ]
2631
+ },
2632
+ action
2633
+ ))
2634
+ }
2635
+ ),
2636
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2637
+ /* @__PURE__ */ jsxRuntime.jsx(
2638
+ "label",
2639
+ {
2640
+ className: "text-sm font-medium mb-1 block",
2641
+ style: { color: "var(--compass-color-text-secondary)" },
2642
+ children: "Token"
2643
+ }
2644
+ ),
2645
+ /* @__PURE__ */ jsxRuntime.jsx(
2646
+ TokenSelector,
2647
+ {
2648
+ tokens: TRANSFER_TOKENS,
2649
+ selectedToken,
2650
+ onSelect: setSelectedToken,
2651
+ balances: activeAction === "deposit" ? eoaBalances : earnBalanceAmounts,
2652
+ disabled: isProcessing
2653
+ }
2654
+ )
2655
+ ] }),
2656
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2657
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [
2658
+ /* @__PURE__ */ jsxRuntime.jsx(
2659
+ "label",
2660
+ {
2661
+ className: "text-sm font-medium",
2662
+ style: { color: "var(--compass-color-text-secondary)" },
2663
+ children: "Amount"
2664
+ }
2665
+ ),
2666
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: [
2667
+ "Available: ",
2668
+ formatAmount(getMaxBalance()),
2669
+ " ",
2670
+ selectedToken
2671
+ ] })
2672
+ ] }),
2673
+ /* @__PURE__ */ jsxRuntime.jsxs(
2674
+ "div",
2675
+ {
2676
+ className: "flex items-center gap-2 p-3 rounded-lg border",
2677
+ style: {
2678
+ backgroundColor: "var(--compass-color-background)",
2679
+ borderColor: "var(--compass-color-border)"
2680
+ },
2681
+ children: [
2682
+ /* @__PURE__ */ jsxRuntime.jsx(
2683
+ "input",
2684
+ {
2685
+ type: "number",
2686
+ value: amount,
2687
+ onChange: (e) => {
2688
+ setAmount(e.target.value);
2689
+ setError(null);
2690
+ },
2691
+ placeholder: "0.00",
2692
+ disabled: isProcessing,
2693
+ className: "flex-1 bg-transparent outline-none text-lg font-mono",
2694
+ style: { color: "var(--compass-color-text)" }
2695
+ }
2696
+ ),
2697
+ /* @__PURE__ */ jsxRuntime.jsx(
2698
+ "span",
2699
+ {
2700
+ className: "text-sm font-medium",
2701
+ style: { color: "var(--compass-color-text-secondary)" },
2702
+ children: selectedToken
2703
+ }
2704
+ )
2705
+ ]
2706
+ }
2707
+ )
2708
+ ] }),
2709
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2", children: [0.25, 0.5, 1].map((pct) => /* @__PURE__ */ jsxRuntime.jsx(
2710
+ "button",
2711
+ {
2712
+ onClick: () => handleQuickAmount(pct),
2713
+ disabled: isProcessing,
2714
+ className: "flex-1 py-1.5 rounded-md text-xs font-medium transition-colors",
2715
+ style: {
2716
+ backgroundColor: "var(--compass-color-secondary)",
2717
+ color: "var(--compass-color-text-secondary)"
2718
+ },
2719
+ children: pct === 1 ? "Max" : `${pct * 100}%`
2720
+ },
2721
+ pct
2722
+ )) }),
2723
+ error && /* @__PURE__ */ jsxRuntime.jsx(
2724
+ "div",
2725
+ {
2726
+ className: "p-3 rounded-lg text-sm",
2727
+ style: {
2728
+ backgroundColor: "var(--compass-color-error-muted)",
2729
+ color: "var(--compass-color-error)"
2730
+ },
2731
+ children: error
2732
+ }
2733
+ ),
2734
+ statusMessage && !error && /* @__PURE__ */ jsxRuntime.jsx(
2735
+ "div",
2736
+ {
2737
+ className: "p-3 rounded-lg text-sm text-center",
2738
+ style: {
2739
+ backgroundColor: transferState === "success" ? "var(--compass-color-success-muted)" : "var(--compass-color-primary-muted)",
2740
+ color: transferState === "success" ? "var(--compass-color-success)" : "var(--compass-color-primary)"
2741
+ },
2742
+ children: statusMessage
2743
+ }
2744
+ ),
2745
+ /* @__PURE__ */ jsxRuntime.jsxs(
2746
+ "button",
2747
+ {
2748
+ onClick: handleTransfer,
2749
+ disabled: isProcessing || !amount || parseFloat(amount) <= 0,
2750
+ className: "w-full py-3 rounded-lg font-medium transition-colors flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
2751
+ style: {
2752
+ backgroundColor: "var(--compass-color-primary)",
2753
+ color: "var(--compass-color-primary-text)"
2754
+ },
2755
+ children: [
2756
+ isProcessing && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 18, className: "animate-spin" }),
2757
+ isProcessing ? "Processing..." : activeAction === "deposit" ? "Deposit to Earn Account" : "Withdraw to Wallet"
2758
+ ]
2759
+ }
2760
+ ),
2761
+ /* @__PURE__ */ jsxRuntime.jsx(
2762
+ "p",
2763
+ {
2764
+ className: "text-xs text-center",
2765
+ style: { color: "var(--compass-color-text-tertiary)" },
2766
+ children: "Gas fees are sponsored by Compass"
2767
+ }
2768
+ )
2769
+ ] })
2770
+ }
2771
+ )
2772
+ ] });
2773
+ }
1892
2774
  function formatTVL(tvl) {
1893
2775
  if (!tvl) return "$0";
1894
2776
  const num = parseFloat(tvl);
@@ -2018,9 +2900,9 @@ function useVaultsData(options = {}) {
2018
2900
  const { client } = useEmbeddableApi();
2019
2901
  const { address } = useEmbeddableWallet();
2020
2902
  const { chainId } = useChain();
2021
- const { sortBy = "apy_7d", assetFilter, minApy } = options;
2903
+ const { sortBy = "apy_7d", assetFilter, minApy, minTvl } = options;
2022
2904
  const vaultsQuery = reactQuery.useQuery({
2023
- queryKey: ["vaults", chainId, sortBy, assetFilter, minApy],
2905
+ queryKey: ["vaults", chainId, sortBy, assetFilter, minApy, minTvl],
2024
2906
  queryFn: async () => {
2025
2907
  const assetSymbol = assetFilter && assetFilter.length === 1 ? assetFilter[0] : void 0;
2026
2908
  const response = await client.earn.earnVaults({
@@ -2050,6 +2932,12 @@ function useVaultsData(options = {}) {
2050
2932
  return apy >= minApy;
2051
2933
  });
2052
2934
  }
2935
+ if (minTvl !== void 0 && minTvl > 0) {
2936
+ vaults = vaults.filter((v) => {
2937
+ const tvl = parseFloat(v.tvlUsd || "0");
2938
+ return tvl >= minTvl;
2939
+ });
2940
+ }
2053
2941
  return vaults;
2054
2942
  },
2055
2943
  staleTime: 30 * 1e3
@@ -2103,15 +2991,6 @@ function useVaultsData(options = {}) {
2103
2991
  }
2104
2992
  };
2105
2993
  }
2106
- var TOKEN_DECIMALS2 = {
2107
- USDC: 6,
2108
- USDT: 6,
2109
- DAI: 18,
2110
- ETH: 18,
2111
- WETH: 18,
2112
- WBTC: 8,
2113
- cbBTC: 8
2114
- };
2115
2994
  function VaultsList({
2116
2995
  showApy = true,
2117
2996
  apyPeriods = ["7d", "30d", "90d"],
@@ -2121,10 +3000,11 @@ function VaultsList({
2121
3000
  showHistory = true,
2122
3001
  showSearch = true,
2123
3002
  showSort = true,
2124
- actionMode = "modal",
2125
3003
  defaultSort = "apy_7d",
2126
3004
  assetFilter,
2127
3005
  minApy,
3006
+ minTvl: initialMinTvl,
3007
+ showTvlFilter = true,
2128
3008
  onVaultSelect,
2129
3009
  onDeposit,
2130
3010
  onWithdraw
@@ -2132,11 +3012,18 @@ function VaultsList({
2132
3012
  const [searchQuery, setSearchQuery] = react.useState("");
2133
3013
  const [sortBy, setSortBy] = react.useState(defaultSort);
2134
3014
  const [selectedVault, setSelectedVault] = react.useState(null);
3015
+ const [minTvlFilter, setMinTvlFilter] = react.useState(initialMinTvl);
3016
+ const [showFilterPanel, setShowFilterPanel] = react.useState(false);
2135
3017
  const { vaults, isLoading, isError, refetch } = useVaultsData({
2136
3018
  sortBy,
2137
3019
  assetFilter,
2138
- minApy
3020
+ minApy,
3021
+ minTvl: minTvlFilter
2139
3022
  });
3023
+ const handleMinTvlChange = react.useCallback((value) => {
3024
+ const num = parseFloat(value);
3025
+ setMinTvlFilter(isNaN(num) || num <= 0 ? void 0 : num);
3026
+ }, []);
2140
3027
  const filteredVaults = react.useMemo(() => {
2141
3028
  if (!searchQuery) return vaults;
2142
3029
  const query = searchQuery.toLowerCase();
@@ -2159,51 +3046,121 @@ function VaultsList({
2159
3046
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
2160
3047
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
2161
3048
  /* @__PURE__ */ jsxRuntime.jsx(ChainSwitcher, {}),
2162
- /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
3049
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3050
+ /* @__PURE__ */ jsxRuntime.jsx(EarnAccountBalance, { compact: true }),
3051
+ /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
3052
+ ] })
2163
3053
  ] }),
2164
- (showSearch || showSort) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2165
- showSearch && /* @__PURE__ */ jsxRuntime.jsxs(
3054
+ (showSearch || showSort || showTvlFilter) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
3055
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
3056
+ showSearch && /* @__PURE__ */ jsxRuntime.jsxs(
3057
+ "div",
3058
+ {
3059
+ className: "flex-1 flex items-center gap-2 px-3 py-2 rounded-lg border",
3060
+ style: {
3061
+ backgroundColor: "var(--compass-color-background)",
3062
+ borderColor: "var(--compass-color-border)"
3063
+ },
3064
+ children: [
3065
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { size: 16, style: { color: "var(--compass-color-text-tertiary)" } }),
3066
+ /* @__PURE__ */ jsxRuntime.jsx(
3067
+ "input",
3068
+ {
3069
+ type: "text",
3070
+ placeholder: "Search vaults...",
3071
+ value: searchQuery,
3072
+ onChange: (e) => setSearchQuery(e.target.value),
3073
+ className: "flex-1 bg-transparent outline-none text-sm",
3074
+ style: { color: "var(--compass-color-text)" }
3075
+ }
3076
+ )
3077
+ ]
3078
+ }
3079
+ ),
3080
+ showSort && /* @__PURE__ */ jsxRuntime.jsxs(
3081
+ "select",
3082
+ {
3083
+ value: sortBy,
3084
+ onChange: (e) => setSortBy(e.target.value),
3085
+ className: "px-3 py-2 rounded-lg border text-sm cursor-pointer",
3086
+ style: {
3087
+ backgroundColor: "var(--compass-color-background)",
3088
+ borderColor: "var(--compass-color-border)",
3089
+ color: "var(--compass-color-text)"
3090
+ },
3091
+ children: [
3092
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "apy_7d", children: "APY (7D)" }),
3093
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "apy_30d", children: "APY (30D)" }),
3094
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "apy_90d", children: "APY (90D)" }),
3095
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "tvl", children: "TVL" })
3096
+ ]
3097
+ }
3098
+ ),
3099
+ showTvlFilter && /* @__PURE__ */ jsxRuntime.jsxs(
3100
+ "button",
3101
+ {
3102
+ onClick: () => setShowFilterPanel(!showFilterPanel),
3103
+ className: "px-3 py-2 rounded-lg border text-sm flex items-center gap-2",
3104
+ style: {
3105
+ backgroundColor: showFilterPanel || minTvlFilter ? "var(--compass-color-primary-muted)" : "var(--compass-color-background)",
3106
+ borderColor: showFilterPanel || minTvlFilter ? "var(--compass-color-primary)" : "var(--compass-color-border)",
3107
+ color: showFilterPanel || minTvlFilter ? "var(--compass-color-primary)" : "var(--compass-color-text)"
3108
+ },
3109
+ children: [
3110
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SlidersHorizontal, { size: 14 }),
3111
+ "Filter"
3112
+ ]
3113
+ }
3114
+ )
3115
+ ] }),
3116
+ showTvlFilter && showFilterPanel && /* @__PURE__ */ jsxRuntime.jsxs(
2166
3117
  "div",
2167
3118
  {
2168
- className: "flex-1 flex items-center gap-2 px-3 py-2 rounded-lg border",
3119
+ className: "flex items-center gap-3 p-3 rounded-lg border",
2169
3120
  style: {
2170
- backgroundColor: "var(--compass-color-background)",
3121
+ backgroundColor: "var(--compass-color-surface)",
2171
3122
  borderColor: "var(--compass-color-border)"
2172
3123
  },
2173
3124
  children: [
2174
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { size: 16, style: { color: "var(--compass-color-text-tertiary)" } }),
2175
3125
  /* @__PURE__ */ jsxRuntime.jsx(
2176
- "input",
3126
+ "label",
2177
3127
  {
2178
- type: "text",
2179
- placeholder: "Search vaults...",
2180
- value: searchQuery,
2181
- onChange: (e) => setSearchQuery(e.target.value),
2182
- className: "flex-1 bg-transparent outline-none text-sm",
2183
- style: { color: "var(--compass-color-text)" }
3128
+ className: "text-sm font-medium whitespace-nowrap",
3129
+ style: { color: "var(--compass-color-text-secondary)" },
3130
+ children: "Min TVL:"
3131
+ }
3132
+ ),
3133
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3134
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-tertiary)" }, children: "$" }),
3135
+ /* @__PURE__ */ jsxRuntime.jsx(
3136
+ "input",
3137
+ {
3138
+ type: "number",
3139
+ placeholder: "0",
3140
+ value: minTvlFilter || "",
3141
+ onChange: (e) => handleMinTvlChange(e.target.value),
3142
+ className: "w-24 px-2 py-1 rounded border text-sm bg-transparent",
3143
+ style: {
3144
+ borderColor: "var(--compass-color-border)",
3145
+ color: "var(--compass-color-text)"
3146
+ }
3147
+ }
3148
+ )
3149
+ ] }),
3150
+ minTvlFilter && /* @__PURE__ */ jsxRuntime.jsx(
3151
+ "button",
3152
+ {
3153
+ onClick: () => setMinTvlFilter(void 0),
3154
+ className: "text-xs px-2 py-1 rounded",
3155
+ style: {
3156
+ backgroundColor: "var(--compass-color-error-muted)",
3157
+ color: "var(--compass-color-error)"
3158
+ },
3159
+ children: "Clear"
2184
3160
  }
2185
3161
  )
2186
3162
  ]
2187
3163
  }
2188
- ),
2189
- showSort && /* @__PURE__ */ jsxRuntime.jsxs(
2190
- "select",
2191
- {
2192
- value: sortBy,
2193
- onChange: (e) => setSortBy(e.target.value),
2194
- className: "px-3 py-2 rounded-lg border text-sm cursor-pointer",
2195
- style: {
2196
- backgroundColor: "var(--compass-color-background)",
2197
- borderColor: "var(--compass-color-border)",
2198
- color: "var(--compass-color-text)"
2199
- },
2200
- children: [
2201
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "apy_7d", children: "APY (7D)" }),
2202
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "apy_30d", children: "APY (30D)" }),
2203
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "apy_90d", children: "APY (90D)" }),
2204
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "tvl", children: "TVL" })
2205
- ]
2206
- }
2207
3164
  )
2208
3165
  ] }),
2209
3166
  isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 24, className: "animate-spin", style: { color: "var(--compass-color-primary)" } }) }) : isError ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -2235,7 +3192,7 @@ function VaultsList({
2235
3192
  },
2236
3193
  vault.vaultAddress
2237
3194
  )) }),
2238
- actionMode === "modal" && selectedVault && /* @__PURE__ */ jsxRuntime.jsx(
3195
+ selectedVault && /* @__PURE__ */ jsxRuntime.jsx(
2239
3196
  ActionModal,
2240
3197
  {
2241
3198
  isOpen: !!selectedVault,
@@ -2263,8 +3220,7 @@ function VaultsList({
2263
3220
  {
2264
3221
  venueType: "VAULT",
2265
3222
  venueAddress: selectedVault.vaultAddress,
2266
- tokenSymbol: selectedVault.assetSymbol,
2267
- tokenDecimals: TOKEN_DECIMALS2[selectedVault.assetSymbol] || 18,
3223
+ venueToken: selectedVault.assetSymbol,
2268
3224
  positionBalance: selectedVault.userPosition?.balance,
2269
3225
  onSuccess: handleActionSuccess
2270
3226
  }
@@ -2364,15 +3320,6 @@ function useAaveData(options = {}) {
2364
3320
  }
2365
3321
  };
2366
3322
  }
2367
- var TOKEN_DECIMALS3 = {
2368
- USDC: 6,
2369
- USDT: 6,
2370
- DAI: 18,
2371
- ETH: 18,
2372
- WETH: 18,
2373
- WBTC: 8,
2374
- cbBTC: 8
2375
- };
2376
3323
  function formatAPY2(apy) {
2377
3324
  if (!apy) return "0.00%";
2378
3325
  const num = parseFloat(apy);
@@ -2396,7 +3343,6 @@ function AaveMarketsList({
2396
3343
  showSearch = true,
2397
3344
  showSort = false,
2398
3345
  // Only one sort option (APY), so hide by default
2399
- actionMode = "modal",
2400
3346
  defaultSort = "supply_apy",
2401
3347
  assetFilter,
2402
3348
  onMarketSelect,
@@ -2429,7 +3375,10 @@ function AaveMarketsList({
2429
3375
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
2430
3376
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
2431
3377
  /* @__PURE__ */ jsxRuntime.jsx(ChainSwitcher, {}),
2432
- /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
3378
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3379
+ /* @__PURE__ */ jsxRuntime.jsx(EarnAccountBalance, { compact: true }),
3380
+ /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
3381
+ ] })
2433
3382
  ] }),
2434
3383
  (showSearch || showSort) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2435
3384
  showSearch && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -2581,7 +3530,7 @@ function AaveMarketsList({
2581
3530
  market.marketAddress
2582
3531
  );
2583
3532
  }) }),
2584
- actionMode === "modal" && selectedMarket && /* @__PURE__ */ jsxRuntime.jsx(
3533
+ selectedMarket && /* @__PURE__ */ jsxRuntime.jsx(
2585
3534
  ActionModal,
2586
3535
  {
2587
3536
  isOpen: !!selectedMarket,
@@ -2609,8 +3558,7 @@ function AaveMarketsList({
2609
3558
  {
2610
3559
  venueType: "AAVE",
2611
3560
  venueAddress: selectedMarket.marketAddress,
2612
- tokenSymbol: selectedMarket.underlyingSymbol,
2613
- tokenDecimals: TOKEN_DECIMALS3[selectedMarket.underlyingSymbol] || 18,
3561
+ venueToken: selectedMarket.underlyingSymbol,
2614
3562
  positionBalance: selectedMarket.userPosition?.balance,
2615
3563
  onSuccess: handleActionSuccess
2616
3564
  }
@@ -2624,9 +3572,9 @@ function usePendleData(options = {}) {
2624
3572
  const { client } = useEmbeddableApi();
2625
3573
  const { address } = useEmbeddableWallet();
2626
3574
  const { chainId } = useChain();
2627
- const { sortBy = "fixed_apy", assetFilter } = options;
3575
+ const { sortBy = "fixed_apy", assetFilter, minTvl } = options;
2628
3576
  const marketsQuery = reactQuery.useQuery({
2629
- queryKey: ["pendleMarkets", chainId, sortBy, assetFilter],
3577
+ queryKey: ["pendleMarkets", chainId, sortBy, assetFilter, minTvl],
2630
3578
  queryFn: async () => {
2631
3579
  const underlyingSymbol = assetFilter && assetFilter.length === 1 ? assetFilter[0] : void 0;
2632
3580
  const orderBy = sortBy === "tvl" ? "tvl_usd" : "implied_apy";
@@ -2655,6 +3603,12 @@ function usePendleData(options = {}) {
2655
3603
  (m) => assetFilter.includes(m.underlyingSymbol.toUpperCase())
2656
3604
  );
2657
3605
  }
3606
+ if (minTvl !== void 0 && minTvl > 0) {
3607
+ markets = markets.filter((m) => {
3608
+ const tvl = parseFloat(m.tvlUsd || "0");
3609
+ return tvl >= minTvl;
3610
+ });
3611
+ }
2658
3612
  if (sortBy === "expiry") {
2659
3613
  markets.sort((a, b) => {
2660
3614
  return new Date(a.expiry).getTime() - new Date(b.expiry).getTime();
@@ -2713,15 +3667,6 @@ function usePendleData(options = {}) {
2713
3667
  }
2714
3668
  };
2715
3669
  }
2716
- var TOKEN_DECIMALS4 = {
2717
- USDC: 6,
2718
- USDT: 6,
2719
- DAI: 18,
2720
- ETH: 18,
2721
- WETH: 18,
2722
- WBTC: 8,
2723
- cbBTC: 8
2724
- };
2725
3670
  function formatAPY3(apy) {
2726
3671
  if (!apy) return "0.00%";
2727
3672
  return `${parseFloat(apy).toFixed(2)}%`;
@@ -2747,9 +3692,10 @@ function PendleMarketsList({
2747
3692
  showHistory = true,
2748
3693
  showSearch = true,
2749
3694
  showSort = true,
2750
- actionMode = "modal",
2751
3695
  defaultSort = "fixed_apy",
2752
3696
  assetFilter,
3697
+ minTvl: initialMinTvl,
3698
+ showTvlFilter = true,
2753
3699
  onMarketSelect,
2754
3700
  onDeposit,
2755
3701
  onWithdraw
@@ -2757,7 +3703,13 @@ function PendleMarketsList({
2757
3703
  const [searchQuery, setSearchQuery] = react.useState("");
2758
3704
  const [sortBy, setSortBy] = react.useState(defaultSort);
2759
3705
  const [selectedMarket, setSelectedMarket] = react.useState(null);
2760
- const { markets, isLoading, isError, refetch } = usePendleData({ sortBy, assetFilter });
3706
+ const [minTvlFilter, setMinTvlFilter] = react.useState(initialMinTvl);
3707
+ const [showFilterPanel, setShowFilterPanel] = react.useState(false);
3708
+ const { markets, isLoading, isError, refetch } = usePendleData({ sortBy, assetFilter, minTvl: minTvlFilter });
3709
+ const handleMinTvlChange = react.useCallback((value) => {
3710
+ const num = parseFloat(value);
3711
+ setMinTvlFilter(isNaN(num) || num <= 0 ? void 0 : num);
3712
+ }, []);
2761
3713
  const filteredMarkets = react.useMemo(() => {
2762
3714
  if (!searchQuery) return markets;
2763
3715
  const query = searchQuery.toLowerCase();
@@ -2780,50 +3732,120 @@ function PendleMarketsList({
2780
3732
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
2781
3733
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
2782
3734
  /* @__PURE__ */ jsxRuntime.jsx(ChainSwitcher, {}),
2783
- /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
3735
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3736
+ /* @__PURE__ */ jsxRuntime.jsx(EarnAccountBalance, { compact: true }),
3737
+ /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
3738
+ ] })
2784
3739
  ] }),
2785
- (showSearch || showSort) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2786
- showSearch && /* @__PURE__ */ jsxRuntime.jsxs(
3740
+ (showSearch || showSort || showTvlFilter) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
3741
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
3742
+ showSearch && /* @__PURE__ */ jsxRuntime.jsxs(
3743
+ "div",
3744
+ {
3745
+ className: "flex-1 flex items-center gap-2 px-3 py-2 rounded-lg border",
3746
+ style: {
3747
+ backgroundColor: "var(--compass-color-background)",
3748
+ borderColor: "var(--compass-color-border)"
3749
+ },
3750
+ children: [
3751
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { size: 16, style: { color: "var(--compass-color-text-tertiary)" } }),
3752
+ /* @__PURE__ */ jsxRuntime.jsx(
3753
+ "input",
3754
+ {
3755
+ type: "text",
3756
+ placeholder: "Search markets...",
3757
+ value: searchQuery,
3758
+ onChange: (e) => setSearchQuery(e.target.value),
3759
+ className: "flex-1 bg-transparent outline-none text-sm",
3760
+ style: { color: "var(--compass-color-text)" }
3761
+ }
3762
+ )
3763
+ ]
3764
+ }
3765
+ ),
3766
+ showSort && /* @__PURE__ */ jsxRuntime.jsxs(
3767
+ "select",
3768
+ {
3769
+ value: sortBy,
3770
+ onChange: (e) => setSortBy(e.target.value),
3771
+ className: "px-3 py-2 rounded-lg border text-sm cursor-pointer",
3772
+ style: {
3773
+ backgroundColor: "var(--compass-color-background)",
3774
+ borderColor: "var(--compass-color-border)",
3775
+ color: "var(--compass-color-text)"
3776
+ },
3777
+ children: [
3778
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "fixed_apy", children: "Fixed APY" }),
3779
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "tvl", children: "TVL" }),
3780
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "expiry", children: "Expiry" })
3781
+ ]
3782
+ }
3783
+ ),
3784
+ showTvlFilter && /* @__PURE__ */ jsxRuntime.jsxs(
3785
+ "button",
3786
+ {
3787
+ onClick: () => setShowFilterPanel(!showFilterPanel),
3788
+ className: "px-3 py-2 rounded-lg border text-sm flex items-center gap-2",
3789
+ style: {
3790
+ backgroundColor: showFilterPanel || minTvlFilter ? "var(--compass-color-primary-muted)" : "var(--compass-color-background)",
3791
+ borderColor: showFilterPanel || minTvlFilter ? "var(--compass-color-primary)" : "var(--compass-color-border)",
3792
+ color: showFilterPanel || minTvlFilter ? "var(--compass-color-primary)" : "var(--compass-color-text)"
3793
+ },
3794
+ children: [
3795
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SlidersHorizontal, { size: 14 }),
3796
+ "Filter"
3797
+ ]
3798
+ }
3799
+ )
3800
+ ] }),
3801
+ showTvlFilter && showFilterPanel && /* @__PURE__ */ jsxRuntime.jsxs(
2787
3802
  "div",
2788
3803
  {
2789
- className: "flex-1 flex items-center gap-2 px-3 py-2 rounded-lg border",
3804
+ className: "flex items-center gap-3 p-3 rounded-lg border",
2790
3805
  style: {
2791
- backgroundColor: "var(--compass-color-background)",
3806
+ backgroundColor: "var(--compass-color-surface)",
2792
3807
  borderColor: "var(--compass-color-border)"
2793
3808
  },
2794
3809
  children: [
2795
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { size: 16, style: { color: "var(--compass-color-text-tertiary)" } }),
2796
3810
  /* @__PURE__ */ jsxRuntime.jsx(
2797
- "input",
3811
+ "label",
2798
3812
  {
2799
- type: "text",
2800
- placeholder: "Search markets...",
2801
- value: searchQuery,
2802
- onChange: (e) => setSearchQuery(e.target.value),
2803
- className: "flex-1 bg-transparent outline-none text-sm",
2804
- style: { color: "var(--compass-color-text)" }
3813
+ className: "text-sm font-medium whitespace-nowrap",
3814
+ style: { color: "var(--compass-color-text-secondary)" },
3815
+ children: "Min TVL:"
3816
+ }
3817
+ ),
3818
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3819
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--compass-color-text-tertiary)" }, children: "$" }),
3820
+ /* @__PURE__ */ jsxRuntime.jsx(
3821
+ "input",
3822
+ {
3823
+ type: "number",
3824
+ placeholder: "0",
3825
+ value: minTvlFilter || "",
3826
+ onChange: (e) => handleMinTvlChange(e.target.value),
3827
+ className: "w-24 px-2 py-1 rounded border text-sm bg-transparent",
3828
+ style: {
3829
+ borderColor: "var(--compass-color-border)",
3830
+ color: "var(--compass-color-text)"
3831
+ }
3832
+ }
3833
+ )
3834
+ ] }),
3835
+ minTvlFilter && /* @__PURE__ */ jsxRuntime.jsx(
3836
+ "button",
3837
+ {
3838
+ onClick: () => setMinTvlFilter(void 0),
3839
+ className: "text-xs px-2 py-1 rounded",
3840
+ style: {
3841
+ backgroundColor: "var(--compass-color-error-muted)",
3842
+ color: "var(--compass-color-error)"
3843
+ },
3844
+ children: "Clear"
2805
3845
  }
2806
3846
  )
2807
3847
  ]
2808
3848
  }
2809
- ),
2810
- showSort && /* @__PURE__ */ jsxRuntime.jsxs(
2811
- "select",
2812
- {
2813
- value: sortBy,
2814
- onChange: (e) => setSortBy(e.target.value),
2815
- className: "px-3 py-2 rounded-lg border text-sm cursor-pointer",
2816
- style: {
2817
- backgroundColor: "var(--compass-color-background)",
2818
- borderColor: "var(--compass-color-border)",
2819
- color: "var(--compass-color-text)"
2820
- },
2821
- children: [
2822
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "fixed_apy", children: "Fixed APY" }),
2823
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "tvl", children: "TVL" }),
2824
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "expiry", children: "Expiry" })
2825
- ]
2826
- }
2827
3849
  )
2828
3850
  ] }),
2829
3851
  isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 24, className: "animate-spin", style: { color: "var(--compass-color-primary)" } }) }) : isError ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -2958,7 +3980,7 @@ function PendleMarketsList({
2958
3980
  market.marketAddress
2959
3981
  );
2960
3982
  }) }),
2961
- actionMode === "modal" && selectedMarket && /* @__PURE__ */ jsxRuntime.jsx(
3983
+ selectedMarket && /* @__PURE__ */ jsxRuntime.jsx(
2962
3984
  ActionModal,
2963
3985
  {
2964
3986
  isOpen: !!selectedMarket,
@@ -2986,8 +4008,7 @@ function PendleMarketsList({
2986
4008
  {
2987
4009
  venueType: "PENDLE_PT",
2988
4010
  venueAddress: selectedMarket.marketAddress,
2989
- tokenSymbol: selectedMarket.underlyingSymbol,
2990
- tokenDecimals: TOKEN_DECIMALS4[selectedMarket.underlyingSymbol] || 18,
4011
+ venueToken: selectedMarket.underlyingSymbol,
2991
4012
  positionBalance: selectedMarket.userPosition?.balance,
2992
4013
  onSuccess: handleActionSuccess
2993
4014
  }
@@ -2998,43 +4019,55 @@ function PendleMarketsList({
2998
4019
  ] });
2999
4020
  }
3000
4021
  function useSwapQuote({ fromToken, toToken, amount, enabled = true }) {
3001
- const { client } = useEmbeddableApi();
3002
4022
  const { chainId } = useChain();
4023
+ const { address } = useCompassWallet();
3003
4024
  const query = reactQuery.useQuery({
3004
- queryKey: ["swapQuote", chainId, fromToken, toToken, amount],
4025
+ queryKey: ["swapQuote", chainId, fromToken, toToken, amount, address],
3005
4026
  queryFn: async () => {
3006
- if (!fromToken || !toToken || !amount || parseFloat(amount) <= 0) {
4027
+ if (!fromToken || !toToken || !amount || parseFloat(amount) <= 0 || !address) {
3007
4028
  return null;
3008
4029
  }
3009
4030
  try {
3010
- const estimatedRate = 1;
3011
- const outputAmount = (parseFloat(amount) * estimatedRate).toString();
4031
+ const params = new URLSearchParams({
4032
+ owner: address,
4033
+ chain: chainId,
4034
+ tokenIn: fromToken,
4035
+ tokenOut: toToken,
4036
+ amountIn: amount
4037
+ });
4038
+ const response = await fetch(`/api/compass/swap/quote?${params}`);
4039
+ if (!response.ok) {
4040
+ const error = await response.json();
4041
+ throw new Error(error.error || "Failed to get swap quote");
4042
+ }
4043
+ const data = await response.json();
4044
+ const outputAmount = data.estimatedAmountOut || "0";
4045
+ const inputAmountNum = parseFloat(amount);
4046
+ const outputAmountNum = parseFloat(outputAmount);
3012
4047
  return {
3013
4048
  inputAmount: amount,
3014
4049
  outputAmount,
3015
- rate: estimatedRate.toString(),
3016
- priceImpact: "0",
3017
- fee: "0"
4050
+ rate: inputAmountNum > 0 ? (outputAmountNum / inputAmountNum).toString() : "0"
3018
4051
  };
3019
4052
  } catch (error) {
3020
4053
  console.error("Failed to get swap quote:", error);
3021
- return null;
4054
+ throw error;
3022
4055
  }
3023
4056
  },
3024
- enabled: enabled && !!fromToken && !!toToken && !!amount && parseFloat(amount) > 0,
4057
+ enabled: enabled && !!address && !!fromToken && !!toToken && !!amount && parseFloat(amount) > 0,
3025
4058
  staleTime: 10 * 1e3,
3026
- // 10 seconds - quotes change frequently
3027
- refetchInterval: 15 * 1e3
3028
- // Refresh every 15 seconds
4059
+ refetchInterval: 15 * 1e3,
4060
+ retry: 1
3029
4061
  });
3030
4062
  return {
3031
4063
  quote: query.data,
3032
4064
  isLoading: query.isLoading,
3033
4065
  isError: query.isError,
4066
+ error: query.error,
3034
4067
  refetch: query.refetch
3035
4068
  };
3036
4069
  }
3037
- var DEFAULT_TOKENS = ["USDC", "ETH", "WETH", "WBTC", "DAI", "USDT"];
4070
+ var DEFAULT_TOKENS = ["USDC", "ETH", "WETH", "WBTC", "DAI", "USDT", "AUSD", "SBC"];
3038
4071
  function SwapWidget({
3039
4072
  layout = "full",
3040
4073
  defaultFromToken = "ETH",
@@ -3042,7 +4075,6 @@ function SwapWidget({
3042
4075
  allowedTokens = DEFAULT_TOKENS,
3043
4076
  showReverseButton = true,
3044
4077
  showSettings = false,
3045
- showPriceImpact = true,
3046
4078
  onSwapSuccess,
3047
4079
  onSwapError
3048
4080
  }) {
@@ -3050,8 +4082,8 @@ function SwapWidget({
3050
4082
  const [toToken, setToToken] = react.useState(defaultToToken);
3051
4083
  const [fromAmount, setFromAmount] = react.useState("");
3052
4084
  const [isSwapping, setIsSwapping] = react.useState(false);
3053
- const { address, isConnected } = useCompassWallet();
3054
- const { client } = useEmbeddableApi();
4085
+ const [swapStatus, setSwapStatus] = react.useState("");
4086
+ const { address, isConnected, signTypedData } = useCompassWallet();
3055
4087
  const { chainId } = useChain();
3056
4088
  const { quote, isLoading: isQuoteLoading } = useSwapQuote({
3057
4089
  fromToken,
@@ -3067,32 +4099,75 @@ function SwapWidget({
3067
4099
  const handleSwap = react.useCallback(async () => {
3068
4100
  if (!address || !fromAmount || !quote) return;
3069
4101
  setIsSwapping(true);
4102
+ setSwapStatus("Preparing swap...");
3070
4103
  try {
3071
- const response = await client.earn.earnSwap({
3072
- chain: chainId,
3073
- sender: address,
3074
- tokenIn: fromToken,
3075
- tokenOut: toToken,
3076
- humanReadableAmountIn: fromAmount
4104
+ const prepareResponse = await fetch("/api/compass/swap/prepare", {
4105
+ method: "POST",
4106
+ headers: { "Content-Type": "application/json" },
4107
+ body: JSON.stringify({
4108
+ owner: address,
4109
+ chain: chainId,
4110
+ tokenIn: fromToken,
4111
+ tokenOut: toToken,
4112
+ amountIn: fromAmount
4113
+ })
4114
+ });
4115
+ if (!prepareResponse.ok) {
4116
+ const error = await prepareResponse.json();
4117
+ throw new Error(error.error || "Failed to prepare swap");
4118
+ }
4119
+ const prepareData = await prepareResponse.json();
4120
+ const { eip712, normalizedTypes } = prepareData;
4121
+ if (!eip712) {
4122
+ throw new Error("No EIP-712 data returned from prepare");
4123
+ }
4124
+ setSwapStatus("Please sign the transaction...");
4125
+ const signature = await signTypedData({
4126
+ domain: eip712.domain,
4127
+ types: normalizedTypes || eip712.types,
4128
+ primaryType: "SafeTx",
4129
+ message: eip712.message
4130
+ });
4131
+ setSwapStatus("Executing swap...");
4132
+ const executeResponse = await fetch("/api/compass/swap/execute", {
4133
+ method: "POST",
4134
+ headers: { "Content-Type": "application/json" },
4135
+ body: JSON.stringify({
4136
+ owner: address,
4137
+ chain: chainId,
4138
+ eip712,
4139
+ signature
4140
+ })
3077
4141
  });
3078
- console.log("Swap prepared:", response);
3079
- onSwapSuccess?.(fromToken, toToken, fromAmount, quote.outputAmount, "pending");
4142
+ if (!executeResponse.ok) {
4143
+ const error = await executeResponse.json();
4144
+ throw new Error(error.error || "Failed to execute swap");
4145
+ }
4146
+ const executeData = await executeResponse.json();
4147
+ const txHash = executeData.txHash;
4148
+ setSwapStatus("Swap successful!");
4149
+ onSwapSuccess?.(fromToken, toToken, fromAmount, quote.outputAmount, txHash);
3080
4150
  setFromAmount("");
4151
+ setTimeout(() => setSwapStatus(""), 3e3);
3081
4152
  } catch (error) {
3082
4153
  console.error("Swap failed:", error);
4154
+ setSwapStatus("");
3083
4155
  onSwapError?.(error);
3084
4156
  } finally {
3085
4157
  setIsSwapping(false);
3086
4158
  }
3087
- }, [address, fromAmount, quote, client, chainId, fromToken, toToken, onSwapSuccess, onSwapError]);
4159
+ }, [address, fromAmount, quote, chainId, fromToken, toToken, signTypedData, onSwapSuccess, onSwapError]);
3088
4160
  const isCompact = layout === "compact";
3089
4161
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
3090
4162
  !isCompact && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
3091
4163
  /* @__PURE__ */ jsxRuntime.jsx(ChainSwitcher, {}),
3092
- /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
4164
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
4165
+ /* @__PURE__ */ jsxRuntime.jsx(EarnAccountBalance, { compact: true }),
4166
+ /* @__PURE__ */ jsxRuntime.jsx(WalletStatus, { compact: true })
4167
+ ] })
3093
4168
  ] }),
3094
4169
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
3095
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 rounded-xl border", style: { backgroundColor: "var(--compass-color-surface)", borderColor: "var(--compass-color-border)" }, children: [
4170
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 rounded-xl border relative", style: { backgroundColor: "var(--compass-color-surface)", borderColor: "var(--compass-color-border)" }, children: [
3096
4171
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between mb-2", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "From" }) }),
3097
4172
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
3098
4173
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -3102,7 +4177,7 @@ function SwapWidget({
3102
4177
  value: fromAmount,
3103
4178
  onChange: (e) => setFromAmount(e.target.value),
3104
4179
  placeholder: "0.00",
3105
- className: "flex-1 bg-transparent outline-none text-2xl font-mono",
4180
+ className: "flex-1 bg-transparent outline-none text-2xl font-mono min-w-0",
3106
4181
  style: { color: "var(--compass-color-text)" }
3107
4182
  }
3108
4183
  ),
@@ -3111,9 +4186,9 @@ function SwapWidget({
3111
4186
  {
3112
4187
  value: fromToken,
3113
4188
  onChange: (e) => setFromToken(e.target.value),
3114
- className: "px-3 py-2 rounded-lg border text-sm font-medium cursor-pointer",
4189
+ className: "px-3 py-2 rounded-lg border text-sm font-medium cursor-pointer flex-shrink-0",
3115
4190
  style: { backgroundColor: "var(--compass-color-background)", borderColor: "var(--compass-color-border)", color: "var(--compass-color-text)" },
3116
- children: allowedTokens.map((token) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: token, children: token }, token))
4191
+ children: allowedTokens.filter((t) => t !== toToken).map((token) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: token, children: token }, token))
3117
4192
  }
3118
4193
  )
3119
4194
  ] })
@@ -3133,7 +4208,7 @@ function SwapWidget({
3133
4208
  /* @__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: [
3134
4209
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 16, className: "animate-spin" }),
3135
4210
  "Loading..."
3136
- ] }) : quote?.outputAmount ? parseFloat(quote.outputAmount).toFixed(6) : "0.00" }),
4211
+ ] }) : quote?.outputAmount ? parseFloat(quote.outputAmount).toFixed(8) : "0.00000000" }),
3137
4212
  /* @__PURE__ */ jsxRuntime.jsx(
3138
4213
  "select",
3139
4214
  {
@@ -3147,14 +4222,6 @@ function SwapWidget({
3147
4222
  ] })
3148
4223
  ] })
3149
4224
  ] }),
3150
- showPriceImpact && quote && parseFloat(quote.priceImpact) > 0.01 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 p-3 rounded-lg", style: { backgroundColor: "var(--compass-color-warning-muted)" }, children: [
3151
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { size: 16, style: { color: "var(--compass-color-warning)" } }),
3152
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm", style: { color: "var(--compass-color-warning)" }, children: [
3153
- "Price impact: ",
3154
- (parseFloat(quote.priceImpact) * 100).toFixed(2),
3155
- "%"
3156
- ] })
3157
- ] }),
3158
4225
  quote && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm", style: { color: "var(--compass-color-text-secondary)" }, children: [
3159
4226
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Rate" }),
3160
4227
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono", children: [
@@ -3166,6 +4233,20 @@ function SwapWidget({
3166
4233
  toToken
3167
4234
  ] })
3168
4235
  ] }),
4236
+ swapStatus && /* @__PURE__ */ jsxRuntime.jsxs(
4237
+ "div",
4238
+ {
4239
+ className: "flex items-center gap-2 p-3 rounded-lg text-sm",
4240
+ style: {
4241
+ backgroundColor: swapStatus.includes("successful") ? "var(--compass-color-success-muted)" : "var(--compass-color-surface)",
4242
+ color: swapStatus.includes("successful") ? "var(--compass-color-success)" : "var(--compass-color-text-secondary)"
4243
+ },
4244
+ children: [
4245
+ !swapStatus.includes("successful") && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 14, className: "animate-spin" }),
4246
+ swapStatus
4247
+ ]
4248
+ }
4249
+ ),
3169
4250
  !isConnected ? /* @__PURE__ */ jsxRuntime.jsxs(
3170
4251
  "div",
3171
4252
  {
@@ -3201,78 +4282,29 @@ function SwapWidget({
3201
4282
  )
3202
4283
  ] });
3203
4284
  }
3204
-
3205
- // src/components/CompassEarnWidget/presets.ts
3206
- var allTabs = [
4285
+ var tabs = [
3207
4286
  { id: "vaults", label: "Vaults", enabled: true },
3208
4287
  { id: "aave", label: "Aave", enabled: true },
3209
4288
  { id: "pendle", label: "Pendle", enabled: true },
3210
4289
  { id: "swap", label: "Swap", enabled: true }
3211
4290
  ];
3212
- function getTabsForPreset(preset) {
3213
- switch (preset) {
3214
- case "full":
3215
- return allTabs;
3216
- case "earn-only":
3217
- return allTabs.map((tab) => ({
3218
- ...tab,
3219
- enabled: tab.id !== "swap"
3220
- }));
3221
- case "swap-only":
3222
- return allTabs.map((tab) => ({
3223
- ...tab,
3224
- enabled: tab.id === "swap"
3225
- }));
3226
- case "vaults-only":
3227
- return allTabs.map((tab) => ({
3228
- ...tab,
3229
- enabled: tab.id === "vaults"
3230
- }));
3231
- default:
3232
- return allTabs;
3233
- }
3234
- }
3235
- function getDefaultTab(tabs) {
3236
- const enabledTab = tabs.find((t) => t.enabled);
3237
- return enabledTab?.id || "vaults";
3238
- }
3239
4291
  function CompassEarnWidget({
3240
- preset = "full",
3241
- enableVaults,
3242
- enableAave,
3243
- enablePendle,
3244
- enableSwap,
3245
- defaultTab,
3246
- showHeader = true,
4292
+ defaultTab = "vaults",
3247
4293
  onTabChange
3248
4294
  }) {
3249
- const tabs = react.useMemo(() => {
3250
- const baseTabs = getTabsForPreset(preset);
3251
- return baseTabs.map((tab) => {
3252
- let enabled = tab.enabled;
3253
- if (tab.id === "vaults" && enableVaults !== void 0) enabled = enableVaults;
3254
- if (tab.id === "aave" && enableAave !== void 0) enabled = enableAave;
3255
- if (tab.id === "pendle" && enablePendle !== void 0) enabled = enablePendle;
3256
- if (tab.id === "swap" && enableSwap !== void 0) enabled = enableSwap;
3257
- return { ...tab, enabled };
3258
- });
3259
- }, [preset, enableVaults, enableAave, enablePendle, enableSwap]);
3260
- const enabledTabs = tabs.filter((t) => t.enabled);
3261
- const initialTab = defaultTab && tabs.find((t) => t.id === defaultTab)?.enabled ? defaultTab : getDefaultTab(tabs);
3262
- const [activeTab, setActiveTab] = react.useState(initialTab);
4295
+ const [activeTab, setActiveTab] = react.useState(defaultTab);
3263
4296
  const handleTabChange = (tab) => {
3264
4297
  setActiveTab(tab);
3265
4298
  onTabChange?.(tab);
3266
4299
  };
3267
- const showTabs = enabledTabs.length > 1;
3268
4300
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
3269
- showHeader && /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "font-bold text-xl", style: { color: "var(--compass-color-text)" }, children: "Compass Earn" }),
3270
- showTabs && /* @__PURE__ */ jsxRuntime.jsx(
4301
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "font-bold text-xl", style: { color: "var(--compass-color-text)" }, children: "Compass Earn" }),
4302
+ /* @__PURE__ */ jsxRuntime.jsx(
3271
4303
  "div",
3272
4304
  {
3273
4305
  className: "flex gap-1 p-1 rounded-lg",
3274
4306
  style: { backgroundColor: "var(--compass-color-surface)" },
3275
- children: enabledTabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
4307
+ children: tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
3276
4308
  "button",
3277
4309
  {
3278
4310
  onClick: () => handleTabChange(tab.id),
@@ -3288,15 +4320,36 @@ function CompassEarnWidget({
3288
4320
  }
3289
4321
  ),
3290
4322
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3291
- activeTab === "vaults" && tabs.find((t) => t.id === "vaults")?.enabled && /* @__PURE__ */ jsxRuntime.jsx(VaultsList, { showSearch: true, showSort: true }),
3292
- activeTab === "aave" && tabs.find((t) => t.id === "aave")?.enabled && /* @__PURE__ */ jsxRuntime.jsx(AaveMarketsList, { showSearch: true, showSort: true }),
3293
- activeTab === "pendle" && tabs.find((t) => t.id === "pendle")?.enabled && /* @__PURE__ */ jsxRuntime.jsx(PendleMarketsList, { showSearch: true, showSort: true }),
3294
- activeTab === "swap" && tabs.find((t) => t.id === "swap")?.enabled && /* @__PURE__ */ jsxRuntime.jsx(SwapWidget, { layout: "full" })
4323
+ activeTab === "vaults" && /* @__PURE__ */ jsxRuntime.jsx(VaultsList, {}),
4324
+ activeTab === "aave" && /* @__PURE__ */ jsxRuntime.jsx(AaveMarketsList, {}),
4325
+ activeTab === "pendle" && /* @__PURE__ */ jsxRuntime.jsx(PendleMarketsList, {}),
4326
+ activeTab === "swap" && /* @__PURE__ */ jsxRuntime.jsx(SwapWidget, {})
3295
4327
  ] })
3296
4328
  ] });
3297
4329
  }
4330
+ var CHAINS = {
4331
+ ethereum: {
4332
+ id: "ethereum",
4333
+ name: "Ethereum",
4334
+ viemChain: chains.mainnet,
4335
+ icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/info/logo.png"
4336
+ },
4337
+ base: {
4338
+ id: "base",
4339
+ name: "Base",
4340
+ viemChain: chains.base,
4341
+ icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/base/info/logo.png"
4342
+ },
4343
+ arbitrum: {
4344
+ id: "arbitrum",
4345
+ name: "Arbitrum",
4346
+ viemChain: chains.arbitrum,
4347
+ icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/arbitrum/info/logo.png"
4348
+ }
4349
+ };
3298
4350
 
3299
4351
  exports.AaveMarketsList = AaveMarketsList;
4352
+ exports.AccountBalancesModal = AccountBalancesModal;
3300
4353
  exports.ActionModal = ActionModal;
3301
4354
  exports.ApiProvider = ApiProvider;
3302
4355
  exports.CHAINS = CHAINS;
@@ -3304,11 +4357,11 @@ exports.ChainSwitcher = ChainSwitcher;
3304
4357
  exports.CompassEarnWidget = CompassEarnWidget;
3305
4358
  exports.CompassProvider = CompassProvider;
3306
4359
  exports.DepositWithdrawForm = DepositWithdrawForm;
4360
+ exports.EarnAccountBalance = EarnAccountBalance;
3307
4361
  exports.EarnAccountGuard = EarnAccountGuard;
3308
4362
  exports.PendleMarketsList = PendleMarketsList;
3309
4363
  exports.PnLSummary = PnLSummary;
3310
4364
  exports.SwapWidget = SwapWidget;
3311
- exports.TOKEN_DECIMALS = TOKEN_DECIMALS;
3312
4365
  exports.ThemeProvider = ThemeProvider;
3313
4366
  exports.TransactionHistory = TransactionHistory;
3314
4367
  exports.VaultsList = VaultsList;