@compass-labs/widgets 0.1.38 → 0.1.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import { createContext, forwardRef, useState, useCallback, useImperativeHandle, useContext, useEffect, useRef, useMemo } from 'react';
1
+ import { createContext, forwardRef, useState, useCallback, useImperativeHandle, useContext, useEffect, useMemo, useRef } from 'react';
2
2
  import { useQueryClient, useQuery, QueryClient, QueryClientProvider } from '@tanstack/react-query';
3
3
  import { CompassApiSDK } from '@compass-labs/api-sdk';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
  import { arbitrum, base, mainnet } from 'viem/chains';
6
- import { Wallet, Loader2, ArrowDownLeft, ArrowUpRight, X, ChevronDown, Check, Copy, LogOut, AlertCircle, ArrowRight, ExternalLink, ArrowDownUp, ArrowLeftRight, CheckCircle, TrendingUp, Plus, Minus, Circle, AlertTriangle, XCircle, TrendingDown, ChevronRight, ArrowUpCircle, ArrowDownCircle, RotateCcw, Equal, ChevronUp, Inbox, Percent, Clock, Calendar } from 'lucide-react';
6
+ import { Wallet, Loader2, ArrowDownLeft, ArrowUpRight, X, Check, Copy, LogOut, ChevronDown, AlertCircle, ArrowRight, ExternalLink, ArrowDownUp, ArrowLeftRight, CheckCircle, TrendingUp, Plus, Minus, Circle, AlertTriangle, XCircle, TrendingDown, ChevronRight, ArrowUpCircle, ArrowDownCircle, RotateCcw, Equal, ChevronUp, Inbox, Percent, Clock, Calendar } from 'lucide-react';
7
7
 
8
8
  // src/provider/CompassProvider.tsx
9
9
  var ApiContext = createContext(null);
@@ -1158,9 +1158,9 @@ function CopyableAddress({
1158
1158
  title: copied ? "Copied!" : `Copy: ${address}`,
1159
1159
  children: [
1160
1160
  label && /* @__PURE__ */ jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: label }),
1161
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
1161
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
1162
1162
  /* @__PURE__ */ jsx("span", { className: "text-xs font-mono", style: { color: "var(--compass-color-text)" }, children: displayAddr }),
1163
- copied ? /* @__PURE__ */ jsx(Check, { size: 12, className: "flex-shrink-0", style: { color: "var(--compass-color-success)" } }) : /* @__PURE__ */ jsx(Copy, { size: 12, className: "flex-shrink-0", style: { color: "var(--compass-color-text-tertiary)" } })
1163
+ copied ? /* @__PURE__ */ jsx(Check, { size: 14, className: "flex-shrink-0", style: { color: "var(--compass-color-success)" } }) : /* @__PURE__ */ jsx(Copy, { size: 14, className: "flex-shrink-0", style: { color: "var(--compass-color-text-secondary)" } })
1164
1164
  ] })
1165
1165
  ]
1166
1166
  }
@@ -2661,7 +2661,7 @@ function AccountBalancesModal({
2661
2661
  ] })
2662
2662
  ] }) });
2663
2663
  }
2664
- var TRANSFER_TOKENS = ["USDC", "SBC"];
2664
+ var TRANSFER_TOKEN = "USDC";
2665
2665
  var EarnAccountBalance = forwardRef(function EarnAccountBalance2({
2666
2666
  compact = false,
2667
2667
  hideVisual = false,
@@ -2669,7 +2669,7 @@ var EarnAccountBalance = forwardRef(function EarnAccountBalance2({
2669
2669
  }, ref) {
2670
2670
  const [isModalOpen, setIsModalOpen] = useState(false);
2671
2671
  const [activeAction, setActiveAction] = useState("deposit");
2672
- const [selectedToken, setSelectedToken] = useState(TRANSFER_TOKENS[0]);
2672
+ const selectedToken = TRANSFER_TOKEN;
2673
2673
  const [amount, setAmount] = useState("");
2674
2674
  const [transferState, setTransferState] = useState("idle");
2675
2675
  const [statusMessage, setStatusMessage] = useState("");
@@ -2695,23 +2695,20 @@ var EarnAccountBalance = forwardRef(function EarnAccountBalance2({
2695
2695
  staleTime: 30 * 1e3
2696
2696
  });
2697
2697
  const { data: eoaBalances } = useQuery({
2698
- queryKey: ["eoaBalances", chainId, address, TRANSFER_TOKENS.join(",")],
2698
+ queryKey: ["eoaBalances", chainId, address, TRANSFER_TOKEN],
2699
2699
  queryFn: async () => {
2700
2700
  if (!address) return {};
2701
- const balances = {};
2702
- for (const token of TRANSFER_TOKENS) {
2703
- try {
2704
- const response = await fetch(
2705
- `/api/compass/token/balance?chain=${chainId}&token=${token}&address=${address}`
2706
- );
2707
- if (response.ok) {
2708
- const data = await response.json();
2709
- balances[token] = data.balance || "0";
2710
- }
2711
- } catch {
2701
+ try {
2702
+ const response = await fetch(
2703
+ `/api/compass/token/balance?chain=${chainId}&token=${TRANSFER_TOKEN}&address=${address}`
2704
+ );
2705
+ if (response.ok) {
2706
+ const data = await response.json();
2707
+ return { [TRANSFER_TOKEN]: data.balance || "0" };
2712
2708
  }
2709
+ } catch {
2713
2710
  }
2714
- return balances;
2711
+ return {};
2715
2712
  },
2716
2713
  enabled: !!address && activeAction === "deposit",
2717
2714
  staleTime: 30 * 1e3
@@ -2758,11 +2755,6 @@ var EarnAccountBalance = forwardRef(function EarnAccountBalance2({
2758
2755
  }
2759
2756
  return earnBalanceAmounts[selectedToken] || "0";
2760
2757
  };
2761
- const handleQuickAmount = (percentage) => {
2762
- const max = parseFloat(getMaxBalance());
2763
- if (isNaN(max)) return;
2764
- setAmount(formatAmount(max * percentage));
2765
- };
2766
2758
  const handleTransfer = useCallback(async () => {
2767
2759
  if (!address || !amount || !signTypedData) return;
2768
2760
  setError(null);
@@ -3025,37 +3017,9 @@ var EarnAccountBalance = forwardRef(function EarnAccountBalance2({
3025
3017
  {
3026
3018
  className: "text-sm font-medium mb-1 block",
3027
3019
  style: { color: "var(--compass-color-text-secondary)" },
3028
- children: "Token"
3020
+ children: "Amount"
3029
3021
  }
3030
3022
  ),
3031
- /* @__PURE__ */ jsx(
3032
- TokenSelector,
3033
- {
3034
- tokens: TRANSFER_TOKENS,
3035
- selectedToken,
3036
- onSelect: setSelectedToken,
3037
- balances: activeAction === "deposit" ? eoaBalances : earnBalanceAmounts,
3038
- disabled: isProcessing
3039
- }
3040
- )
3041
- ] }),
3042
- /* @__PURE__ */ jsxs("div", { children: [
3043
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1", children: [
3044
- /* @__PURE__ */ jsx(
3045
- "label",
3046
- {
3047
- className: "text-sm font-medium",
3048
- style: { color: "var(--compass-color-text-secondary)" },
3049
- children: "Amount"
3050
- }
3051
- ),
3052
- /* @__PURE__ */ jsxs("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: [
3053
- "Available: ",
3054
- formatAmount(getMaxBalance()),
3055
- " ",
3056
- selectedToken
3057
- ] })
3058
- ] }),
3059
3023
  /* @__PURE__ */ jsxs(
3060
3024
  "div",
3061
3025
  {
@@ -3086,28 +3050,27 @@ var EarnAccountBalance = forwardRef(function EarnAccountBalance2({
3086
3050
  {
3087
3051
  className: "text-sm font-medium",
3088
3052
  style: { color: "var(--compass-color-text-secondary)" },
3089
- children: selectedToken
3053
+ children: "USDC"
3090
3054
  }
3091
3055
  )
3092
3056
  ]
3093
3057
  }
3058
+ ),
3059
+ /* @__PURE__ */ jsxs(
3060
+ "button",
3061
+ {
3062
+ onClick: () => setAmount(formatAmount(getMaxBalance())),
3063
+ className: "text-xs mt-1",
3064
+ style: { color: "var(--compass-color-primary)", cursor: "pointer", background: "none", border: "none", padding: 0 },
3065
+ children: [
3066
+ "Available: ",
3067
+ formatAmount(getMaxBalance()),
3068
+ " USDC"
3069
+ ]
3070
+ }
3094
3071
  )
3095
3072
  ] }),
3096
- /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: [0.25, 0.5, 1].map((pct) => /* @__PURE__ */ jsx(
3097
- "button",
3098
- {
3099
- onClick: () => handleQuickAmount(pct),
3100
- disabled: isProcessing,
3101
- className: "flex-1 py-1.5 text-xs font-medium transition-colors",
3102
- style: {
3103
- backgroundColor: "var(--compass-color-secondary)",
3104
- color: "var(--compass-color-text-secondary)",
3105
- borderRadius: "var(--compass-border-radius-md)"
3106
- },
3107
- children: pct === 1 ? "Max" : `${pct * 100}%`
3108
- },
3109
- pct
3110
- )) }),
3073
+ /* @__PURE__ */ jsx("p", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: activeAction === "deposit" ? "Approves the token transfer, then transfers USDC from your wallet into your savings account. Requires 2 signatures." : "Withdraws USDC from your savings account back to your wallet. Requires 1 signature." }),
3111
3074
  error && /* @__PURE__ */ jsx(
3112
3075
  "div",
3113
3076
  {
@@ -3132,7 +3095,7 @@ var EarnAccountBalance = forwardRef(function EarnAccountBalance2({
3132
3095
  children: statusMessage
3133
3096
  }
3134
3097
  ),
3135
- /* @__PURE__ */ jsxs(
3098
+ /* @__PURE__ */ jsx(
3136
3099
  "button",
3137
3100
  {
3138
3101
  onClick: handleTransfer,
@@ -3144,10 +3107,14 @@ var EarnAccountBalance = forwardRef(function EarnAccountBalance2({
3144
3107
  borderRadius: "var(--compass-border-radius-lg)",
3145
3108
  fontFamily: "var(--compass-font-family)"
3146
3109
  },
3147
- children: [
3148
- isProcessing && /* @__PURE__ */ jsx(Loader2, { size: 18, className: "animate-spin" }),
3149
- isProcessing ? "Processing..." : activeAction === "deposit" ? "Deposit to Earn Account" : "Withdraw to Wallet"
3150
- ]
3110
+ children: isProcessing ? /* @__PURE__ */ jsxs(Fragment, { children: [
3111
+ /* @__PURE__ */ jsx(Loader2, { size: 18, className: "animate-spin" }),
3112
+ " Processing..."
3113
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3114
+ /* @__PURE__ */ jsx(ArrowUpRight, { size: 16 }),
3115
+ " ",
3116
+ activeAction === "deposit" ? "Transfer to Savings Account" : "Withdraw to Wallet"
3117
+ ] })
3151
3118
  }
3152
3119
  ),
3153
3120
  /* @__PURE__ */ jsx(
@@ -5670,31 +5637,7 @@ function EarnAccount({
5670
5637
  ]
5671
5638
  }
5672
5639
  )
5673
- ] }),
5674
- /* @__PURE__ */ jsx("div", { className: "flex", style: { gap: "6px", marginTop: activeTab === "deposit" && maxAmount >= 10 ? "8px" : "0" }, children: activeTab === "deposit" && maxAmount > 0 && [10, 20, 30].filter((v) => v <= maxAmount).map((val) => /* @__PURE__ */ jsxs(
5675
- "button",
5676
- {
5677
- onClick: () => {
5678
- setAmount(val.toString());
5679
- setError(null);
5680
- },
5681
- disabled: isProcessing,
5682
- className: "text-xs font-medium",
5683
- style: {
5684
- padding: "4px 12px",
5685
- backgroundColor: parseFloat(amount) === val ? "transparent" : "var(--compass-color-surface)",
5686
- border: parseFloat(amount) === val ? "1.5px solid var(--compass-color-primary)" : "1.5px solid var(--compass-color-border)",
5687
- color: parseFloat(amount) === val ? "var(--compass-color-primary)" : "var(--compass-color-text-secondary)",
5688
- borderRadius: "var(--compass-border-radius-xl)",
5689
- transition: "all 150ms ease"
5690
- },
5691
- children: [
5692
- "$",
5693
- val
5694
- ]
5695
- },
5696
- val
5697
- )) })
5640
+ ] })
5698
5641
  ]
5699
5642
  }
5700
5643
  ),
@@ -10708,6 +10651,7 @@ function RebalancingWidget({
10708
10651
  const [txHash, setTxHash] = useState(null);
10709
10652
  const [txConfirmed, setTxConfirmed] = useState(false);
10710
10653
  const [hasInitializedTargets, setHasInitializedTargets] = useState(false);
10654
+ const [originalAllocations, setOriginalAllocations] = useState({});
10711
10655
  const [isSwapModalOpen, setIsSwapModalOpen] = useState(false);
10712
10656
  const [isBalancesModalOpen, setIsBalancesModalOpen] = useState(false);
10713
10657
  const [isAddMarketExpanded, setIsAddMarketExpanded] = useState(false);
@@ -10726,6 +10670,7 @@ function RebalancingWidget({
10726
10670
  const earnBalanceRef = useRef(null);
10727
10671
  useEffect(() => {
10728
10672
  setTargets([]);
10673
+ setOriginalAllocations({});
10729
10674
  setPreviewPlan(null);
10730
10675
  setServerPreview(null);
10731
10676
  setWidgetState("editing");
@@ -10742,15 +10687,21 @@ function RebalancingWidget({
10742
10687
  }, [CHAIN_ID]);
10743
10688
  useEffect(() => {
10744
10689
  if (portfolio && portfolio.positions.length > 0 && !hasInitializedTargets && !isLoading) {
10690
+ const origAllocs = {};
10745
10691
  setTargets(
10746
- portfolio.positions.map((p) => ({
10747
- venueType: p.venueType,
10748
- venueAddress: p.venueAddress,
10749
- venueName: p.venueName,
10750
- token: p.token,
10751
- targetPercent: parseFloat(p.allocationPercent.toFixed(2))
10752
- }))
10692
+ portfolio.positions.map((p) => {
10693
+ const pct = parseFloat(p.allocationPercent.toFixed(2));
10694
+ origAllocs[`${p.venueType}-${p.venueAddress.toLowerCase()}`] = pct;
10695
+ return {
10696
+ venueType: p.venueType,
10697
+ venueAddress: p.venueAddress,
10698
+ venueName: p.venueName,
10699
+ token: p.token,
10700
+ targetPercent: pct
10701
+ };
10702
+ })
10753
10703
  );
10704
+ setOriginalAllocations(origAllocs);
10754
10705
  setHasInitializedTargets(true);
10755
10706
  }
10756
10707
  }, [portfolio, hasInitializedTargets, isLoading]);
@@ -10782,15 +10733,21 @@ function RebalancingWidget({
10782
10733
  }, [portfolio, targets, hasChanges, defaultSlippage, minRebalanceThresholdUsd]);
10783
10734
  const handleResetToCurrent = useCallback(() => {
10784
10735
  if (!portfolio) return;
10736
+ const origAllocs = {};
10785
10737
  setTargets(
10786
- portfolio.positions.map((p) => ({
10787
- venueType: p.venueType,
10788
- venueAddress: p.venueAddress,
10789
- venueName: p.venueName,
10790
- token: p.token,
10791
- targetPercent: parseFloat(p.allocationPercent.toFixed(2))
10792
- }))
10738
+ portfolio.positions.map((p) => {
10739
+ const pct = parseFloat(p.allocationPercent.toFixed(2));
10740
+ origAllocs[`${p.venueType}-${p.venueAddress.toLowerCase()}`] = pct;
10741
+ return {
10742
+ venueType: p.venueType,
10743
+ venueAddress: p.venueAddress,
10744
+ venueName: p.venueName,
10745
+ token: p.token,
10746
+ targetPercent: pct
10747
+ };
10748
+ })
10793
10749
  );
10750
+ setOriginalAllocations(origAllocs);
10794
10751
  setPreviewPlan(null);
10795
10752
  setServerPreview(null);
10796
10753
  setWidgetState("editing");
@@ -10833,7 +10790,8 @@ function RebalancingWidget({
10833
10790
  venueType: t.venueType === "vault" ? "VAULT" : t.venueType === "aave" ? "AAVE" : "PENDLE_PT",
10834
10791
  venueAddress: t.venueAddress,
10835
10792
  targetPercent: t.targetPercent,
10836
- token: t.token
10793
+ token: t.token,
10794
+ originalPercent: originalAllocations[`${t.venueType}-${t.venueAddress.toLowerCase()}`] ?? 0
10837
10795
  })),
10838
10796
  slippage: defaultSlippage
10839
10797
  })
@@ -10851,7 +10809,7 @@ function RebalancingWidget({
10851
10809
  setWidgetState("error");
10852
10810
  onError?.(err instanceof Error ? err : new Error("Preview failed"));
10853
10811
  }
10854
- }, [portfolio, hasChanges, address, CHAIN_ID, targets, defaultSlippage, clientPreview, onError, ensureCorrectChain]);
10812
+ }, [portfolio, hasChanges, address, CHAIN_ID, targets, defaultSlippage, clientPreview, onError, ensureCorrectChain, originalAllocations]);
10855
10813
  const handleExecute = useCallback(async () => {
10856
10814
  if (!serverPreview?.eip712 || !address) return;
10857
10815
  setWidgetState("signing");
@@ -11487,11 +11445,7 @@ function RebalancingWidget({
11487
11445
  clientPreview.warnings.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-col gap-1", children: clientPreview.warnings.map((w, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 text-xs", style: { color: "var(--compass-color-warning, #eab308)" }, children: [
11488
11446
  /* @__PURE__ */ jsx(AlertTriangle, { size: 12, className: "flex-shrink-0 mt-0.5" }),
11489
11447
  /* @__PURE__ */ jsx("span", { children: w })
11490
- ] }, i)) }),
11491
- /* @__PURE__ */ jsxs("p", { className: "text-xs mt-2", style: { color: "var(--compass-color-text-tertiary)" }, children: [
11492
- "Estimated gas savings: ",
11493
- clientPreview.estimatedGasSavings
11494
- ] })
11448
+ ] }, i)) })
11495
11449
  ]
11496
11450
  }
11497
11451
  ),
@@ -11734,46 +11688,8 @@ function RebalancingWidget({
11734
11688
  onClose: () => setIsBalancesModalOpen(false),
11735
11689
  title: "Balance Breakdown",
11736
11690
  children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", style: { gap: "12px" }, children: [
11737
- address && /* @__PURE__ */ jsxs(
11738
- "div",
11739
- {
11740
- className: "flex items-center justify-between",
11741
- style: {
11742
- backgroundColor: "var(--compass-color-surface)",
11743
- borderRadius: "var(--compass-border-radius-lg)",
11744
- padding: "10px 16px",
11745
- border: "1.5px solid var(--compass-color-border)"
11746
- },
11747
- children: [
11748
- /* @__PURE__ */ jsx("span", { style: { color: "var(--compass-color-text-tertiary)", fontSize: "0.8125rem" }, children: "Wallet" }),
11749
- /* @__PURE__ */ jsxs("span", { style: { color: "var(--compass-color-text)", fontSize: "0.8125rem", fontFamily: "monospace" }, children: [
11750
- address.slice(0, 6),
11751
- "...",
11752
- address.slice(-4)
11753
- ] })
11754
- ]
11755
- }
11756
- ),
11757
- earnAccountAddress && /* @__PURE__ */ jsxs(
11758
- "div",
11759
- {
11760
- className: "flex items-center justify-between",
11761
- style: {
11762
- backgroundColor: "var(--compass-color-surface)",
11763
- borderRadius: "var(--compass-border-radius-lg)",
11764
- padding: "10px 16px",
11765
- border: "1.5px solid var(--compass-color-border)"
11766
- },
11767
- children: [
11768
- /* @__PURE__ */ jsx("span", { style: { color: "var(--compass-color-text-tertiary)", fontSize: "0.8125rem" }, children: "Product Account" }),
11769
- /* @__PURE__ */ jsxs("span", { style: { color: "var(--compass-color-text)", fontSize: "0.8125rem", fontFamily: "monospace" }, children: [
11770
- earnAccountAddress.slice(0, 6),
11771
- "...",
11772
- earnAccountAddress.slice(-4)
11773
- ] })
11774
- ]
11775
- }
11776
- ),
11691
+ address && /* @__PURE__ */ jsx(CopyableAddress, { address, label: "Wallet" }),
11692
+ earnAccountAddress && /* @__PURE__ */ jsx(CopyableAddress, { address: earnAccountAddress, label: "Product Account" }),
11777
11693
  portfolio && portfolio.idleBalances.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
11778
11694
  /* @__PURE__ */ jsx(
11779
11695
  "span",