@compass-labs/widgets 0.1.27 → 0.1.29
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 +197 -56
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +197 -56
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.js +7 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +7 -3
- package/dist/server/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6477,7 +6477,7 @@ function useRebalancingData(chainOverride) {
|
|
|
6477
6477
|
if (usdValue <= 0) continue;
|
|
6478
6478
|
balances.push({
|
|
6479
6479
|
token: symbol,
|
|
6480
|
-
balance: parseFloat(td.
|
|
6480
|
+
balance: parseFloat(td.balanceFormatted || td.balance_formatted || "0"),
|
|
6481
6481
|
usdValue
|
|
6482
6482
|
});
|
|
6483
6483
|
}
|
|
@@ -6759,9 +6759,9 @@ function PortfolioBalanceCard({
|
|
|
6759
6759
|
totalIdleUsd,
|
|
6760
6760
|
idleBalances,
|
|
6761
6761
|
earnAccountAddress,
|
|
6762
|
-
|
|
6763
|
-
onTogglePositions,
|
|
6762
|
+
onViewPositions,
|
|
6764
6763
|
positionCount,
|
|
6764
|
+
totalEarned = 0,
|
|
6765
6765
|
showTopUp = true,
|
|
6766
6766
|
onTopUp
|
|
6767
6767
|
}) {
|
|
@@ -6859,27 +6859,43 @@ function PortfolioBalanceCard({
|
|
|
6859
6859
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6860
6860
|
"button",
|
|
6861
6861
|
{
|
|
6862
|
-
onClick:
|
|
6863
|
-
|
|
6862
|
+
onClick: onViewPositions,
|
|
6863
|
+
disabled: positionCount === 0,
|
|
6864
|
+
className: "flex items-center justify-between w-full text-left transition-opacity hover:opacity-80 disabled:hover:opacity-100 disabled:cursor-default",
|
|
6864
6865
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", style: { gap: "calc(var(--compass-spacing-unit) * 0.25)" }, children: [
|
|
6865
6866
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.5)" }, children: [
|
|
6866
6867
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm", style: { color: "var(--compass-color-text-tertiary)" }, children: [
|
|
6867
6868
|
"Earning interest",
|
|
6868
|
-
positionCount >
|
|
6869
|
+
positionCount > 1 ? ` \xB7 ${positionCount} positions` : ""
|
|
6869
6870
|
] }),
|
|
6870
|
-
positionCount > 0 &&
|
|
6871
|
+
positionCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 14, style: { color: "var(--compass-color-text-tertiary)" } })
|
|
6871
6872
|
] }),
|
|
6872
|
-
/* @__PURE__ */ jsxRuntime.
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6873
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", style: { gap: "calc(var(--compass-spacing-unit) * 0.75)" }, children: [
|
|
6874
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6875
|
+
"span",
|
|
6876
|
+
{
|
|
6877
|
+
className: "font-semibold",
|
|
6878
|
+
style: {
|
|
6879
|
+
color: "var(--compass-color-text)",
|
|
6880
|
+
fontSize: "1rem"
|
|
6881
|
+
},
|
|
6882
|
+
children: formatUSD(earningInterestUsd)
|
|
6883
|
+
}
|
|
6884
|
+
),
|
|
6885
|
+
positionCount > 0 && totalEarned !== 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6886
|
+
"span",
|
|
6887
|
+
{
|
|
6888
|
+
className: "text-sm font-medium",
|
|
6889
|
+
style: {
|
|
6890
|
+
color: totalEarned >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)"
|
|
6891
|
+
},
|
|
6892
|
+
children: [
|
|
6893
|
+
totalEarned >= 0 ? "+" : "",
|
|
6894
|
+
formatUSD(totalEarned)
|
|
6895
|
+
]
|
|
6896
|
+
}
|
|
6897
|
+
)
|
|
6898
|
+
] })
|
|
6883
6899
|
] })
|
|
6884
6900
|
}
|
|
6885
6901
|
)
|
|
@@ -6910,6 +6926,55 @@ var VENUE_LABELS = {
|
|
|
6910
6926
|
aave: "Aave",
|
|
6911
6927
|
pendle_pt: "Pendle PT"
|
|
6912
6928
|
};
|
|
6929
|
+
function formatPercent(value) {
|
|
6930
|
+
if (value === 0) return "0%";
|
|
6931
|
+
if (Number.isInteger(value)) return `${value}%`;
|
|
6932
|
+
const str = value.toFixed(2).replace(/0+$/, "").replace(/\.$/, "");
|
|
6933
|
+
return `${str}%`;
|
|
6934
|
+
}
|
|
6935
|
+
function PercentInput({ value, onChange }) {
|
|
6936
|
+
const [localValue, setLocalValue] = react.useState(value.toString());
|
|
6937
|
+
const [isFocused, setIsFocused] = react.useState(false);
|
|
6938
|
+
if (!isFocused && localValue !== value.toString()) {
|
|
6939
|
+
const parsed = parseFloat(localValue);
|
|
6940
|
+
if (isNaN(parsed) || Math.abs(parsed - value) > 1e-3) {
|
|
6941
|
+
setLocalValue(value.toString());
|
|
6942
|
+
}
|
|
6943
|
+
}
|
|
6944
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6945
|
+
"input",
|
|
6946
|
+
{
|
|
6947
|
+
type: "text",
|
|
6948
|
+
inputMode: "decimal",
|
|
6949
|
+
value: isFocused ? localValue : formatPercent(value).replace("%", ""),
|
|
6950
|
+
onFocus: () => {
|
|
6951
|
+
setIsFocused(true);
|
|
6952
|
+
setLocalValue(value.toString());
|
|
6953
|
+
},
|
|
6954
|
+
onChange: (e) => {
|
|
6955
|
+
const raw = e.target.value;
|
|
6956
|
+
setLocalValue(raw);
|
|
6957
|
+
const parsed = parseFloat(raw);
|
|
6958
|
+
if (!isNaN(parsed)) {
|
|
6959
|
+
onChange(Math.max(0, Math.min(100, parsed)));
|
|
6960
|
+
}
|
|
6961
|
+
},
|
|
6962
|
+
onBlur: () => {
|
|
6963
|
+
setIsFocused(false);
|
|
6964
|
+
const parsed = parseFloat(localValue);
|
|
6965
|
+
if (!isNaN(parsed)) {
|
|
6966
|
+
onChange(Math.max(0, Math.min(100, parsed)));
|
|
6967
|
+
}
|
|
6968
|
+
},
|
|
6969
|
+
className: "w-16 text-center text-xs font-mono rounded bg-transparent outline-none",
|
|
6970
|
+
style: {
|
|
6971
|
+
color: "var(--compass-color-text)",
|
|
6972
|
+
border: "1px solid var(--compass-color-border)",
|
|
6973
|
+
padding: "2px 0"
|
|
6974
|
+
}
|
|
6975
|
+
}
|
|
6976
|
+
);
|
|
6977
|
+
}
|
|
6913
6978
|
function AllocationEditor({
|
|
6914
6979
|
portfolio,
|
|
6915
6980
|
targets,
|
|
@@ -7046,20 +7111,10 @@ function AllocationEditor({
|
|
|
7046
7111
|
),
|
|
7047
7112
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 flex-shrink-0", children: [
|
|
7048
7113
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7049
|
-
|
|
7114
|
+
PercentInput,
|
|
7050
7115
|
{
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
onChange: (e) => onUpdatePercent(index, parseFloat(e.target.value) || 0),
|
|
7054
|
-
className: "w-16 text-center text-xs font-mono rounded bg-transparent outline-none",
|
|
7055
|
-
style: {
|
|
7056
|
-
color: "var(--compass-color-text)",
|
|
7057
|
-
border: "1px solid var(--compass-color-border)",
|
|
7058
|
-
padding: "2px 0"
|
|
7059
|
-
},
|
|
7060
|
-
min: 0,
|
|
7061
|
-
max: 100,
|
|
7062
|
-
step: 1e-3
|
|
7116
|
+
value: target.targetPercent,
|
|
7117
|
+
onChange: (val) => onUpdatePercent(index, val)
|
|
7063
7118
|
}
|
|
7064
7119
|
),
|
|
7065
7120
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)", width: "12px" }, children: "%" })
|
|
@@ -7071,10 +7126,9 @@ function AllocationEditor({
|
|
|
7071
7126
|
className: "text-xs font-mono",
|
|
7072
7127
|
style: { color: diff > 0 ? "var(--compass-color-success)" : "var(--compass-color-error)", fontSize: "10px" },
|
|
7073
7128
|
children: [
|
|
7074
|
-
currentPercent
|
|
7075
|
-
"
|
|
7076
|
-
target.targetPercent
|
|
7077
|
-
"%"
|
|
7129
|
+
formatPercent(currentPercent),
|
|
7130
|
+
" \u2192 ",
|
|
7131
|
+
formatPercent(target.targetPercent)
|
|
7078
7132
|
]
|
|
7079
7133
|
}
|
|
7080
7134
|
) })
|
|
@@ -7116,8 +7170,8 @@ function AllocationEditor({
|
|
|
7116
7170
|
color: "var(--compass-color-text-secondary)"
|
|
7117
7171
|
},
|
|
7118
7172
|
children: [
|
|
7119
|
-
targetSum
|
|
7120
|
-
"
|
|
7173
|
+
formatPercent(targetSum),
|
|
7174
|
+
" allocated"
|
|
7121
7175
|
]
|
|
7122
7176
|
}
|
|
7123
7177
|
) })
|
|
@@ -7183,7 +7237,8 @@ function RebalancingWidget({
|
|
|
7183
7237
|
const [errorMessage, setErrorMessage] = react.useState(null);
|
|
7184
7238
|
const [txHash, setTxHash] = react.useState(null);
|
|
7185
7239
|
const [hasInitializedTargets, setHasInitializedTargets] = react.useState(false);
|
|
7186
|
-
const [
|
|
7240
|
+
const [isEarningsModalOpen, setIsEarningsModalOpen] = react.useState(false);
|
|
7241
|
+
const [isAddMarketExpanded, setIsAddMarketExpanded] = react.useState(false);
|
|
7187
7242
|
const [marketTab, setMarketTab] = react.useState("variable");
|
|
7188
7243
|
const [selectedMarket, setSelectedMarket] = react.useState(null);
|
|
7189
7244
|
const [selectedToken, setSelectedToken] = react.useState("USDC");
|
|
@@ -7201,7 +7256,7 @@ function RebalancingWidget({
|
|
|
7201
7256
|
setErrorMessage(null);
|
|
7202
7257
|
setTxHash(null);
|
|
7203
7258
|
setHasInitializedTargets(false);
|
|
7204
|
-
|
|
7259
|
+
setIsAddMarketExpanded(false);
|
|
7205
7260
|
setSelectedMarket(null);
|
|
7206
7261
|
setDepositAmount("");
|
|
7207
7262
|
setDepositError(null);
|
|
@@ -7273,11 +7328,22 @@ function RebalancingWidget({
|
|
|
7273
7328
|
const handleUpdatePercent = react.useCallback((index, value) => {
|
|
7274
7329
|
setTargets((prev) => prev.map((t, i) => i === index ? { ...t, targetPercent: Math.max(0, Math.min(100, value)) } : t));
|
|
7275
7330
|
}, []);
|
|
7331
|
+
const ensureCorrectChain = react.useCallback(async () => {
|
|
7332
|
+
const targetChainId = EVM_CHAIN_IDS2[CHAIN_ID];
|
|
7333
|
+
if (!targetChainId) return;
|
|
7334
|
+
if (walletChainId !== void 0 && walletChainId !== targetChainId) {
|
|
7335
|
+
if (!switchChain) {
|
|
7336
|
+
throw new Error(`Please switch your wallet to ${CHAIN_ID} (chain ${targetChainId})`);
|
|
7337
|
+
}
|
|
7338
|
+
await switchChain(targetChainId);
|
|
7339
|
+
}
|
|
7340
|
+
}, [walletChainId, switchChain, CHAIN_ID]);
|
|
7276
7341
|
const handlePreview = react.useCallback(async () => {
|
|
7277
7342
|
if (!portfolio || !hasChanges || !address) return;
|
|
7278
7343
|
setWidgetState("previewing");
|
|
7279
7344
|
setErrorMessage(null);
|
|
7280
7345
|
try {
|
|
7346
|
+
await ensureCorrectChain();
|
|
7281
7347
|
const response = await fetch("/api/compass/rebalance/preview", {
|
|
7282
7348
|
method: "POST",
|
|
7283
7349
|
headers: { "Content-Type": "application/json" },
|
|
@@ -7306,7 +7372,7 @@ function RebalancingWidget({
|
|
|
7306
7372
|
setWidgetState("error");
|
|
7307
7373
|
onError?.(err instanceof Error ? err : new Error("Preview failed"));
|
|
7308
7374
|
}
|
|
7309
|
-
}, [portfolio, hasChanges, address, CHAIN_ID, targets, defaultSlippage, clientPreview, onError]);
|
|
7375
|
+
}, [portfolio, hasChanges, address, CHAIN_ID, targets, defaultSlippage, clientPreview, onError, ensureCorrectChain]);
|
|
7310
7376
|
const handleExecute = react.useCallback(async () => {
|
|
7311
7377
|
if (!serverPreview?.eip712 || !address) return;
|
|
7312
7378
|
setWidgetState("signing");
|
|
@@ -7353,16 +7419,59 @@ function RebalancingWidget({
|
|
|
7353
7419
|
return idleBalance?.balance ?? 0;
|
|
7354
7420
|
}, [portfolio, selectedToken]);
|
|
7355
7421
|
const needsSwap = selectedMarket ? selectedToken !== selectedMarket.underlyingToken : false;
|
|
7356
|
-
const
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
}
|
|
7365
|
-
|
|
7422
|
+
const rawPositionsQuery = reactQuery.useQuery({
|
|
7423
|
+
queryKey: ["rebalancingRawPositions", CHAIN_ID, address],
|
|
7424
|
+
queryFn: async () => {
|
|
7425
|
+
if (!address) return [];
|
|
7426
|
+
const response = await fetch(`/api/compass/positions?chain=${CHAIN_ID}&owner=${address}`);
|
|
7427
|
+
if (!response.ok) return [];
|
|
7428
|
+
const data = await response.json();
|
|
7429
|
+
return data.positions || [];
|
|
7430
|
+
},
|
|
7431
|
+
enabled: !!address,
|
|
7432
|
+
staleTime: 3e4
|
|
7433
|
+
});
|
|
7434
|
+
const earningsPositions = react.useMemo(() => {
|
|
7435
|
+
return (rawPositionsQuery.data || []).map((pos, index) => ({
|
|
7436
|
+
id: `pos-${index}`,
|
|
7437
|
+
marketType: pos.protocol,
|
|
7438
|
+
marketName: pos.name,
|
|
7439
|
+
marketId: `${pos.protocol}-${index}`,
|
|
7440
|
+
amount: parseFloat(pos.balance || "0"),
|
|
7441
|
+
token: pos.symbol,
|
|
7442
|
+
apy: pos.apy || 0,
|
|
7443
|
+
performance: parseFloat(pos.pnl?.totalPnl || "0"),
|
|
7444
|
+
pnl: pos.pnl ? {
|
|
7445
|
+
unrealizedPnl: parseFloat(pos.pnl.unrealizedPnl || "0"),
|
|
7446
|
+
realizedPnl: parseFloat(pos.pnl.realizedPnl || "0"),
|
|
7447
|
+
totalPnl: parseFloat(pos.pnl.totalPnl || "0")
|
|
7448
|
+
} : void 0,
|
|
7449
|
+
transactions: [
|
|
7450
|
+
...(pos.deposits || []).map((d, i) => ({
|
|
7451
|
+
id: `dep-${index}-${i}`,
|
|
7452
|
+
type: "deposit",
|
|
7453
|
+
amount: parseFloat(d.amount || "0"),
|
|
7454
|
+
token: pos.symbol,
|
|
7455
|
+
txHash: d.txHash || "",
|
|
7456
|
+
timestamp: d.timestamp || void 0
|
|
7457
|
+
})),
|
|
7458
|
+
...(pos.withdrawals || []).map((w, i) => ({
|
|
7459
|
+
id: `wit-${index}-${i}`,
|
|
7460
|
+
type: "withdraw",
|
|
7461
|
+
amount: parseFloat(w.amount || "0"),
|
|
7462
|
+
token: pos.symbol,
|
|
7463
|
+
txHash: w.txHash || "",
|
|
7464
|
+
timestamp: w.timestamp || void 0
|
|
7465
|
+
}))
|
|
7466
|
+
].sort((a, b) => {
|
|
7467
|
+
if (a.timestamp && b.timestamp) return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
|
|
7468
|
+
if (a.timestamp) return -1;
|
|
7469
|
+
if (b.timestamp) return 1;
|
|
7470
|
+
return 0;
|
|
7471
|
+
})
|
|
7472
|
+
}));
|
|
7473
|
+
}, [rawPositionsQuery.data]);
|
|
7474
|
+
const totalEarned = react.useMemo(() => earningsPositions.reduce((sum, p) => sum + p.performance, 0), [earningsPositions]);
|
|
7366
7475
|
const buildVenueParamsFromMarket = (market) => {
|
|
7367
7476
|
switch (market.type) {
|
|
7368
7477
|
case "aave":
|
|
@@ -7617,14 +7726,14 @@ function RebalancingWidget({
|
|
|
7617
7726
|
totalUsd: portfolio.totalUsd,
|
|
7618
7727
|
totalIdleUsd: portfolio.totalIdleUsd,
|
|
7619
7728
|
idleBalances: portfolio.idleBalances,
|
|
7620
|
-
|
|
7621
|
-
onTogglePositions: () => setIsPositionsExpanded((prev) => !prev),
|
|
7729
|
+
onViewPositions: () => setIsEarningsModalOpen(true),
|
|
7622
7730
|
positionCount: portfolio.positions.length,
|
|
7731
|
+
totalEarned,
|
|
7623
7732
|
showTopUp,
|
|
7624
7733
|
onTopUp: () => earnBalanceRef.current?.openTransferModal()
|
|
7625
7734
|
}
|
|
7626
7735
|
),
|
|
7627
|
-
|
|
7736
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7628
7737
|
AllocationEditor,
|
|
7629
7738
|
{
|
|
7630
7739
|
portfolio,
|
|
@@ -7637,8 +7746,30 @@ function RebalancingWidget({
|
|
|
7637
7746
|
onResetToCurrent: handleResetToCurrent,
|
|
7638
7747
|
onEqualSplit: handleEqualSplit
|
|
7639
7748
|
}
|
|
7640
|
-
)
|
|
7641
|
-
|
|
7749
|
+
),
|
|
7750
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7751
|
+
"button",
|
|
7752
|
+
{
|
|
7753
|
+
onClick: () => setIsAddMarketExpanded((prev) => !prev),
|
|
7754
|
+
className: "flex items-center font-medium transition-all hover:opacity-80",
|
|
7755
|
+
style: {
|
|
7756
|
+
backgroundColor: "var(--compass-color-surface)",
|
|
7757
|
+
border: "1px solid var(--compass-color-border)",
|
|
7758
|
+
color: "var(--compass-color-text-secondary)",
|
|
7759
|
+
borderRadius: "var(--compass-border-radius-lg)",
|
|
7760
|
+
padding: "calc(var(--compass-spacing-unit) * 0.75) calc(var(--compass-spacing-unit) * 1)",
|
|
7761
|
+
gap: "calc(var(--compass-spacing-unit) * 0.5)",
|
|
7762
|
+
fontSize: "0.8125rem",
|
|
7763
|
+
width: "100%",
|
|
7764
|
+
justifyContent: "center"
|
|
7765
|
+
},
|
|
7766
|
+
children: [
|
|
7767
|
+
isAddMarketExpanded ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 14 }),
|
|
7768
|
+
isAddMarketExpanded ? "Hide" : "Add new market"
|
|
7769
|
+
]
|
|
7770
|
+
}
|
|
7771
|
+
),
|
|
7772
|
+
isAddMarketExpanded && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7642
7773
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7643
7774
|
MarketSelector,
|
|
7644
7775
|
{
|
|
@@ -8058,7 +8189,17 @@ function RebalancingWidget({
|
|
|
8058
8189
|
]
|
|
8059
8190
|
}
|
|
8060
8191
|
) }),
|
|
8061
|
-
/* @__PURE__ */ jsxRuntime.jsx(EarnAccountBalance, { ref: earnBalanceRef, compact: true, hideVisual: true, onTransferComplete: () => refetch() })
|
|
8192
|
+
/* @__PURE__ */ jsxRuntime.jsx(EarnAccountBalance, { ref: earnBalanceRef, compact: true, hideVisual: true, onTransferComplete: () => refetch() }),
|
|
8193
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8194
|
+
EarningsModal,
|
|
8195
|
+
{
|
|
8196
|
+
isOpen: isEarningsModalOpen,
|
|
8197
|
+
onClose: () => setIsEarningsModalOpen(false),
|
|
8198
|
+
positions: earningsPositions,
|
|
8199
|
+
totalEarned,
|
|
8200
|
+
isLoading: rawPositionsQuery.isLoading
|
|
8201
|
+
}
|
|
8202
|
+
)
|
|
8062
8203
|
]
|
|
8063
8204
|
}
|
|
8064
8205
|
);
|