diff --git a/package.json b/package.json
index 3a72d0e..e4aa1af 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "stability-ui",
"type": "module",
- "version": "0.13.15-alpha",
+ "version": "0.13.17-alpha",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
diff --git a/src/layouts/AppStore.tsx b/src/layouts/AppStore.tsx
index 73bde6c..122f54b 100644
--- a/src/layouts/AppStore.tsx
+++ b/src/layouts/AppStore.tsx
@@ -38,8 +38,6 @@ import {
import { wagmiConfig, platforms, PlatformABI, IVaultManagerABI } from "@web3";
-import { ErrorMessage } from "@ui";
-
import {
calculateAPY,
getStrategyInfo,
@@ -100,7 +98,8 @@ const AppStore = (props: React.PropsWithChildren): JSX.Element => {
const $lastTx = useStore(lastTx);
const $reload = useStore(reload);
- const $error = useStore(error);
+
+ let isError = false;
const localVaults: {
[network: string]: TVaults;
@@ -110,6 +109,11 @@ const AppStore = (props: React.PropsWithChildren): JSX.Element => {
let stabilityAPIData: TAPIData = {};
+ const handleError = (errType: string, description: string) => {
+ error.set({ state: true, type: errType, description });
+ isError = true;
+ };
+
const getDataFromStabilityAPI = async () => {
const maxRetries = 3;
let currentRetry = 0;
@@ -121,11 +125,7 @@ const AppStore = (props: React.PropsWithChildren): JSX.Element => {
stabilityAPIData = response.data;
if (stabilityAPIData?.error) {
- error.set({
- state: true,
- type: "API",
- description: stabilityAPIData?.error,
- });
+ handleError("API", stabilityAPIData?.error);
return;
}
@@ -143,11 +143,7 @@ const AppStore = (props: React.PropsWithChildren): JSX.Element => {
await new Promise((resolve) => setTimeout(resolve, 1000));
} else {
console.error("API error:", err);
- error.set({
- state: true,
- type: "API",
- description: err?.message,
- });
+ handleError("API", err);
}
}
}
@@ -697,6 +693,7 @@ const AppStore = (props: React.PropsWithChildren): JSX.Element => {
isVaultsLoaded.set(true);
} catch (txError: any) {
console.log("BLOCKCHAIN ERROR:", txError);
+
error.set({
state: true,
type: "WEB3",
@@ -724,7 +721,9 @@ const AppStore = (props: React.PropsWithChildren): JSX.Element => {
await getDataFromStabilityAPI();
- getData();
+ if (!isError) {
+ getData();
+ }
if (chain?.id) {
currentChainID.set(String(chain?.id));
@@ -739,14 +738,6 @@ const AppStore = (props: React.PropsWithChildren): JSX.Element => {
fetchAllData();
}, [address, chain?.id, isConnected, $lastTx, $reload]);
- if ($error.state && $error.type === "API") {
- return (
-
-
-
- );
- }
-
return (
{props.children}
diff --git a/src/modules/Platform/components/Chains/Chain.tsx b/src/modules/Platform/components/Chains/Chain.tsx
index 2c8c8a3..ee98178 100644
--- a/src/modules/Platform/components/Chains/Chain.tsx
+++ b/src/modules/Platform/components/Chains/Chain.tsx
@@ -1,3 +1,5 @@
+import { useMemo } from "react";
+
import {
type ApiMainReply,
assets,
@@ -11,7 +13,7 @@ import {
import { useStore } from "@nanostores/react";
-import { apiData } from "@store";
+import { apiData, vaults } from "@store";
import { formatNumber } from "@utils";
@@ -21,7 +23,7 @@ import { ChainStatus, StrategyStatus } from "../../ui";
import tokenlist from "@stabilitydao/stability/out/stability.tokenlist.json";
-import type { TStrategyState } from "@types";
+import type { TStrategyState, TVault } from "@types";
interface IProps {
chain: number;
@@ -29,6 +31,7 @@ interface IProps {
const Chain: React.FC = ({ chain }) => {
const $apiData: ApiMainReply | undefined = useStore(apiData);
+ const $vaults = useStore(vaults);
const chainData = {
...chains[chain],
@@ -69,10 +72,45 @@ const Chain: React.FC = ({ chain }) => {
},
{
name: "TVL",
- content: `\$${formatNumber($apiData?.total.chainTvl[chain.toString()] ? $apiData?.total.chainTvl[chain.toString()].toFixed(0) : "-", "withSpaces")}`,
+ content: `${formatNumber($apiData?.total.chainTvl[chain.toString()] ? $apiData?.total.chainTvl[chain.toString()].toFixed(0) : "-", "abbreviate")}`,
},
];
+ const vaultsInfo = useMemo(() => {
+ if (!$apiData) return [];
+
+ const chainVaults: TVault[] = Object.entries($vaults[chain] || {}).map(
+ (vault) => vault[1] as TVault
+ );
+
+ const vaultsTVL: number = chainVaults.reduce(
+ (acc: number, cur) => (acc += Number(cur?.tvl)),
+ 0
+ );
+
+ const weightedAverageAPR: number = chainVaults.reduce(
+ (acc: number, cur) =>
+ acc +
+ (Number(cur?.earningData?.apr?.daily) * Number(cur?.tvl)) / vaultsTVL,
+ 0
+ );
+
+ return [
+ {
+ name: "Vaults",
+ content: chainVaults.length,
+ },
+ {
+ name: "APR",
+ content: `${weightedAverageAPR.toFixed(2)}%`,
+ },
+ {
+ name: "Vaults TVL",
+ content: formatNumber(vaultsTVL, "abbreviate"),
+ },
+ ];
+ }, [$vaults, chain]);
+
const chainAssets = assets.filter((asset) =>
Object.keys(asset.addresses).includes(chain.toString())
);
@@ -109,6 +147,36 @@ const Chain: React.FC = ({ chain }) => {
))}
+ {chainData.status === "SUPPORTED" && vaultsInfo.length ? (
+
+ {vaultsInfo.map(({ name, content }, index) => (
+
+ {!index ? (
+
+ {content}
+
+ ) : (
+
+ {content}
+
+ )}
+
+
+ {name}
+
+
+ ))}
+
+ ) : (
+ ""
+ )}
{strategies.length > 0 && (
diff --git a/src/modules/Vault/components/HistoricalRate/index.tsx b/src/modules/Vault/components/HistoricalRate/index.tsx
index 16000b8..fc24619 100644
--- a/src/modules/Vault/components/HistoricalRate/index.tsx
+++ b/src/modules/Vault/components/HistoricalRate/index.tsx
@@ -18,6 +18,7 @@ import type { TAddress, TChartData } from "@types";
interface IProps {
network: string;
address: TAddress;
+ created: number;
vaultStrategy: string;
lastHardWork: number;
}
@@ -31,7 +32,7 @@ type TActiveChart =
| undefined;
const HistoricalRate: React.FC = memo(
- ({ network, address, vaultStrategy, lastHardWork }) => {
+ ({ network, address, created, vaultStrategy, lastHardWork }) => {
const APRType = vaultStrategy === "Compound Farm" ? "APR" : "Farm APR";
const timelineSegments = {
@@ -55,6 +56,8 @@ const HistoricalRate: React.FC = memo(
errorMessage: "",
});
+ const createdDaysDifference = getTimeDifference(created)?.days;
+
const formatData = (obj: TChartData) => {
const date = new Date(Number(obj.timestamp) * 1000);
@@ -81,8 +84,6 @@ const HistoricalRate: React.FC = memo(
const NOW = Math.floor(Date.now() / 1000);
const DATA = [];
- let newData = [];
- let time = NOW - TIMESTAMPS_IN_SECONDS.WEEK;
let entities = 0;
let status = true;
@@ -90,7 +91,7 @@ const HistoricalRate: React.FC = memo(
while (status) {
const HISTORY_QUERY = `{
vaultHistoryEntities(
- orderBy: timestamp,
+ orderBy: timestamp,
orderDirection: asc,
where: {address: "${address}", periodVsHoldAPR_not: null}
skip: ${entities}
@@ -117,55 +118,17 @@ const HistoricalRate: React.FC = memo(
const workedData = DATA.map(formatData);
- let APRChartData = workedData
- .filter(
- (obj) =>
- obj.APR && obj.unixTimestamp >= NOW - TIMESTAMPS_IN_SECONDS.WEEK
- )
- .map((obj) => ({
- unixTimestamp: obj.unixTimestamp,
- timestamp: obj.timestamp,
- date: obj.date,
- APR: formatFromBigInt(obj.APR, 3, "withDecimals"),
- APR24H: obj.APR24H,
- vsHoldAPR: Number(obj.periodVsHoldAPR).toFixed(3),
- }));
+ let _chartData = workedData.filter(
+ (obj) =>
+ obj.APR && obj.unixTimestamp >= NOW - TIMESTAMPS_IN_SECONDS.WEEK
+ );
- if (!APRChartData.length) {
+ if (!_chartData.length) {
setIsData(false);
return;
}
- const lastTimestamp =
- APRChartData[APRChartData.length - 1].unixTimestamp;
-
- do {
- let sortedAPRs = APRChartData.filter(
- (obj) => obj.unixTimestamp >= time
- );
-
- let firstEl = sortedAPRs[0] || APRChartData[APRChartData.length - 1];
-
- newData.push({ ...firstEl, timestamp: time });
- time += 3600;
- if (time >= lastTimestamp) {
- newData.push({
- ...APRChartData[APRChartData.length - 1],
- timestamp: APRChartData[APRChartData.length - 1].unixTimestamp,
- });
- }
- } while (time < lastTimestamp);
-
- APRChartData = newData.map(formatData);
-
- if (daysFromLastHardWork >= 3) {
- APRChartData = APRChartData.filter(
- (data) => data.unixTimestamp < lastHardWork
- );
- }
-
setChartData(workedData);
- setActiveChart({ name: "APR", data: APRChartData as [] });
} catch (error) {
const err = error as AxiosError;
if (err.response) {
@@ -190,8 +153,9 @@ const HistoricalRate: React.FC = memo(
const NOW = Math.floor(Date.now() / 1000);
const TIME: number = TIMESTAMPS_IN_SECONDS[segment];
- let time = NOW - TIME,
- newData;
+ let time = NOW - TIME;
+
+ let newData;
const lastTimestamp = chartData[chartData.length - 1].unixTimestamp;
@@ -203,24 +167,25 @@ const HistoricalRate: React.FC = memo(
newData = [];
- if (segment === "MONTH") {
+ if (segment === "WEEK" && createdDaysDifference >= 7) {
do {
let sortedAPRs = APRArr.filter(
(obj) => obj.unixTimestamp >= time
);
+
let firstEl = sortedAPRs[0] || APRArr[APRArr.length - 1];
newData.push({ ...firstEl, timestamp: time });
- time += 7200;
+ time += 3600;
+
if (time >= lastTimestamp) {
newData.push({
...APRArr[APRArr.length - 1],
- timestamp: APRArr[APRArr.length - 1].unixTimestamp,
+ timestamp: APRArr[APRArr.length - 1]?.unixTimestamp,
});
}
} while (time < lastTimestamp);
- } else if (segment === "YEAR") {
- time = APRArr[0].unixTimestamp;
+ } else if (segment === "MONTH" && createdDaysDifference >= 30) {
do {
let sortedAPRs = APRArr.filter(
(obj) => obj.unixTimestamp >= time
@@ -228,7 +193,7 @@ const HistoricalRate: React.FC = memo(
let firstEl = sortedAPRs[0] || APRArr[APRArr.length - 1];
newData.push({ ...firstEl, timestamp: time });
- time += 14400;
+ time += 7200;
if (time >= lastTimestamp) {
newData.push({
...APRArr[APRArr.length - 1],
@@ -237,6 +202,7 @@ const HistoricalRate: React.FC = memo(
}
} while (time < lastTimestamp);
} else {
+ time = APRArr[0].unixTimestamp;
do {
let sortedAPRs = APRArr.filter(
(obj) => obj.unixTimestamp >= time
@@ -244,7 +210,7 @@ const HistoricalRate: React.FC = memo(
let firstEl = sortedAPRs[0] || APRArr[APRArr.length - 1];
newData.push({ ...firstEl, timestamp: time });
- time += 3600;
+ time += 14400;
if (time >= lastTimestamp) {
newData.push({
...APRArr[APRArr.length - 1],
@@ -293,13 +259,87 @@ const HistoricalRate: React.FC = memo(
data: APRChartData as [],
});
break;
+ case "vsHodl":
+ let vsHoldArr = chartData.filter(
+ (obj: TChartData) => obj.APR && obj.unixTimestamp >= NOW - TIME
+ );
+
+ newData = [];
+ if (segment === "WEEK" && createdDaysDifference >= 7) {
+ do {
+ let sortedAPRs = vsHoldArr.filter(
+ (obj) => obj.unixTimestamp >= time
+ );
+ let firstEl = sortedAPRs[0] || vsHoldArr[vsHoldArr.length - 1];
+
+ newData.push({ ...firstEl, timestamp: time });
+ time += 3600;
+ if (time >= lastTimestamp) {
+ newData.push({
+ ...vsHoldArr[vsHoldArr.length - 1],
+ timestamp: vsHoldArr[vsHoldArr.length - 1].unixTimestamp,
+ });
+ }
+ } while (time < lastTimestamp);
+ } else if (segment === "MONTH" && createdDaysDifference >= 30) {
+ do {
+ let sortedAPRs = vsHoldArr.filter(
+ (obj) => obj.unixTimestamp >= time
+ );
+ let firstEl = sortedAPRs[0] || vsHoldArr[vsHoldArr.length - 1];
+
+ newData.push({ ...firstEl, timestamp: time });
+ time += 7200;
+ if (time >= lastTimestamp) {
+ newData.push({
+ ...vsHoldArr[vsHoldArr.length - 1],
+ timestamp: vsHoldArr[vsHoldArr.length - 1].unixTimestamp,
+ });
+ }
+ } while (time < lastTimestamp);
+ } else {
+ time = vsHoldArr[0].unixTimestamp;
+ do {
+ let sortedAPRs = vsHoldArr.filter(
+ (obj) => obj.unixTimestamp >= time
+ );
+ let firstEl = sortedAPRs[0] || vsHoldArr[vsHoldArr.length - 1];
+
+ newData.push({ ...firstEl, timestamp: time });
+ time += 14400;
+ if (time >= lastTimestamp) {
+ newData.push({
+ ...vsHoldArr[vsHoldArr.length - 1],
+ timestamp: vsHoldArr[vsHoldArr.length - 1].unixTimestamp,
+ });
+ }
+ } while (time < lastTimestamp);
+ }
+
+ vsHoldArr = newData.map(formatData);
+
+ const vsHoldAPRChartData = vsHoldArr.map((obj: TChartData) => ({
+ unixTimestamp: obj.unixTimestamp,
+ timestamp: obj.timestamp,
+ date: obj.date,
+ vsHodl: Number(obj.periodVsHoldAPR).toFixed(3),
+ }));
+ setActiveChart({
+ name: "vsHodl",
+ data: vsHoldAPRChartData as [],
+ });
+ break;
case "TVL":
let TVLArr = chartData.filter(
(obj: TChartData) => obj.unixTimestamp >= NOW - TIME
);
newData = [];
- if (segment === "YEAR") {
+ if (
+ segment === "YEAR" ||
+ (segment === "WEEK" && createdDaysDifference < 7) ||
+ (segment === "MONTH" && createdDaysDifference < 30)
+ ) {
time = TVLArr[0].unixTimestamp;
}
@@ -356,7 +396,11 @@ const HistoricalRate: React.FC = memo(
newData = [];
- if (segment === "YEAR") {
+ if (
+ segment === "YEAR" ||
+ (segment === "WEEK" && createdDaysDifference < 7) ||
+ (segment === "MONTH" && createdDaysDifference < 30)
+ ) {
time = priceArr[0].unixTimestamp;
}
@@ -411,78 +455,6 @@ const HistoricalRate: React.FC = memo(
data: priceChartData as [],
});
break;
-
- case "vsHodl":
- let vsHoldArr = chartData.filter(
- (obj: TChartData) => obj.APR && obj.unixTimestamp >= NOW - TIME
- );
-
- newData = [];
-
- if (segment === "MONTH") {
- do {
- let sortedAPRs = vsHoldArr.filter(
- (obj) => obj.unixTimestamp >= time
- );
- let firstEl = sortedAPRs[0] || vsHoldArr[vsHoldArr.length - 1];
-
- newData.push({ ...firstEl, timestamp: time });
- time += 7200;
- if (time >= lastTimestamp) {
- newData.push({
- ...vsHoldArr[vsHoldArr.length - 1],
- timestamp: vsHoldArr[vsHoldArr.length - 1].unixTimestamp,
- });
- }
- } while (time < lastTimestamp);
- } else if (segment === "YEAR") {
- time = vsHoldArr[0].unixTimestamp;
- do {
- let sortedAPRs = vsHoldArr.filter(
- (obj) => obj.unixTimestamp >= time
- );
- let firstEl = sortedAPRs[0] || vsHoldArr[vsHoldArr.length - 1];
-
- newData.push({ ...firstEl, timestamp: time });
- time += 14400;
- if (time >= lastTimestamp) {
- newData.push({
- ...vsHoldArr[vsHoldArr.length - 1],
- timestamp: vsHoldArr[vsHoldArr.length - 1].unixTimestamp,
- });
- }
- } while (time < lastTimestamp);
- } else {
- do {
- let sortedAPRs = vsHoldArr.filter(
- (obj) => obj.unixTimestamp >= time
- );
- let firstEl = sortedAPRs[0] || vsHoldArr[vsHoldArr.length - 1];
-
- newData.push({ ...firstEl, timestamp: time });
- time += 3600;
- if (time >= lastTimestamp) {
- newData.push({
- ...vsHoldArr[vsHoldArr.length - 1],
- timestamp: vsHoldArr[vsHoldArr.length - 1].unixTimestamp,
- });
- }
- } while (time < lastTimestamp);
- }
-
- vsHoldArr = newData.map(formatData);
-
- const vsHoldAPRChartData = vsHoldArr.map((obj: TChartData) => ({
- unixTimestamp: obj.unixTimestamp,
- timestamp: obj.timestamp,
- date: obj.date,
- vsHodl: Number(obj.periodVsHoldAPR).toFixed(3),
- }));
- setActiveChart({
- name: "vsHodl",
- data: vsHoldAPRChartData as [],
- });
- break;
default:
console.log("NO ACTIVE CASE");
break;
@@ -495,6 +467,12 @@ const HistoricalRate: React.FC = memo(
chartHandler(activeChart?.name || "", segment);
};
+ useEffect(() => {
+ if (chartData.length) {
+ chartHandler("APR", "WEEK");
+ }
+ }, [chartData]);
+
useEffect(() => {
getData();
}, []);
diff --git a/src/modules/Vault/index.tsx b/src/modules/Vault/index.tsx
index 09ca8e2..c536e9f 100644
--- a/src/modules/Vault/index.tsx
+++ b/src/modules/Vault/index.tsx
@@ -46,13 +46,6 @@ const Vault: React.FC = ({ network, vault }) => {
}
}, [$vaults, $vaultData]);
- if ($error.state && $error.type === "API") {
- return (
-
-
-
- );
- }
return vault && localVault ? (
@@ -70,6 +63,7 @@ const Vault: React.FC = ({ network, vault }) => {
@@ -109,11 +103,13 @@ const Vault: React.FC = ({ network, vault }) => {
strategy={localVault?.strategyAddress}
/>
-
) : (
-
-
+
);
};
diff --git a/src/modules/Vaults/components/NetworksFilter.tsx b/src/modules/Vaults/components/NetworksFilter.tsx
index 544fdb1..7e80209 100644
--- a/src/modules/Vaults/components/NetworksFilter.tsx
+++ b/src/modules/Vaults/components/NetworksFilter.tsx
@@ -13,7 +13,7 @@ const NetworkFilters: React.FC
= memo(
{activeNetworks.map((chain) => (
= memo(({ vaults, tab, setTab }) => {
const paginationNumbers = [];
+
for (let i = 1; i <= Math.ceil(vaults.length / PAGINATION_VAULTS); i++) {
paginationNumbers.push(i);
}
+ const VISIBLE_VAULTS = {
+ first: tab === 1 ? tab : PAGINATION_VAULTS * (tab - 1),
+ latest:
+ PAGINATION_VAULTS * tab >= vaults.length
+ ? vaults.length
+ : PAGINATION_VAULTS * tab,
+ };
return (
paginationNumbers.length > 1 && (
-
- {paginationNumbers.map((number) => (
-
setTab(number)}
- key={number}
- >
- {number}
-
- ))}
+
+
+ {paginationNumbers.map((number) => (
+
setTab(number)}
+ key={number}
+ >
+ {number}
+
+ ))}
+
+
+ Vaults: {VISIBLE_VAULTS.first}-{VISIBLE_VAULTS.latest} of{" "}
+ {vaults.length}
+
)
);
diff --git a/src/modules/Vaults/index.tsx b/src/modules/Vaults/index.tsx
index 47fc5f4..b3110e6 100644
--- a/src/modules/Vaults/index.tsx
+++ b/src/modules/Vaults/index.tsx
@@ -30,6 +30,7 @@ import {
isVaultsLoaded,
aprFilter,
connected,
+ error,
// platformVersions,
// currentChainID,
// assetsPrices,
@@ -94,6 +95,8 @@ const Vaults = (): JSX.Element => {
const $isVaultsLoaded = useStore(isVaultsLoaded);
const $aprFilter: TAPRPeriod = useStore(aprFilter);
const $connected = useStore(connected);
+
+ const $error = useStore(error);
// const $publicClient = useStore(publicClient);
// const $platformVersions = useStore(platformVersions);
// const $currentChainID = useStore(currentChainID);
@@ -506,7 +509,7 @@ const Vaults = (): JSX.Element => {
-
+
{/* {!!platformUpdates?.newVersion &&
platformUpdates?.newVersion != $platformVersions[$currentChainID] &&
!!upgradesTable?.length && (
diff --git a/src/types/index.ts b/src/types/index.ts
index d19b039..4400032 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -565,6 +565,7 @@ type TAPIData = {
};
};
};
+ error?: string;
};
type TVLRange = { min: number; max: number };
diff --git a/src/ui/ErrorMessage.tsx b/src/ui/ErrorMessage.tsx
index 2f77b88..4cce80c 100644
--- a/src/ui/ErrorMessage.tsx
+++ b/src/ui/ErrorMessage.tsx
@@ -10,22 +10,7 @@ const ErrorMessage: React.FC
= ({ type }) => {
const $error = useStore(error);
const $reload = useStore(reload);
- if ($error.state && type === "API") {
- return (
-
-
-
{$error.description.slice(0, 40)}...
-
-
-
- );
- }
- if ($error.state && type === "WEB3") {
+ if ($error.state) {
return (
-
{$error.description.slice(0, 25)}...
+
+ {$error.description.slice(0, type === "WEB3" ? 25 : 40)}...
+