Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "stability-ui",
"type": "module",
"version": "0.11.44-alpha",
"version": "0.11.46-alpha",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
Expand All @@ -18,7 +18,7 @@
"@astrojs/tailwind": "6.0.2",
"@astrojs/vercel": "8.2.10",
"@nanostores/react": "^0.7.1",
"@stabilitydao/stability": "=0.57.2",
"@stabilitydao/stability": "=0.58.2",
"@tanstack/query-sync-storage-persister": "^5.22.2",
"@tanstack/react-query": "^5.22.2",
"@tanstack/react-query-persist-client": "^5.22.2",
Expand Down
Binary file added public/ogs/markets/Core_Instance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/ogs/pages/recovery.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 7 additions & 1 deletion src/modules/DAO/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ import { useVestingData } from "./hooks";

import { getInitialStateFromUrl } from "./functions";

import { daos } from "@stabilitydao/stability";

import { DAOSectionTypes } from "@types";

const DAO = (): JSX.Element => {
const { data: vestingData, isLoading } = useVestingData("146");

const stabilityDAO = daos?.find(({ name }) => name === "Stability");

const { section } = getInitialStateFromUrl();

const [activeSection, setActiveSection] = useState(section);
Expand Down Expand Up @@ -72,7 +76,9 @@ const DAO = (): JSX.Element => {
<span className="text-[#97979A] text-[16px] leading-5 font-medium">
xSTBL instant exit fee
</span>
<span className="font-semibold">80%</span>
<span className="font-semibold">
{stabilityDAO?.params?.pvpFee}%
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-[#97979A] text-[16px] leading-5 font-medium">
Expand Down
115 changes: 64 additions & 51 deletions src/modules/Market/hooks/useUserReservesData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { useEffect } from "react";

import { useStore } from "@nanostores/react";

import { formatUnits } from "viem";
import { formatUnits, erc20Abi, Abi } from "viem";

import { getBalance, getAllowance, getTokenData } from "@utils";
import { getTokenData } from "@utils";

import { web3clients, AaveProtocolDataProviderABI, AavePoolABI } from "@web3";

Expand Down Expand Up @@ -76,22 +76,65 @@ export const useUserReservesData = (market: TMarket): TResult => {
availableBorrowsBase = Number(formatUnits(userData[2], 8));
liquidationThreshold = Number(userData[3]) / 10000;
} catch (err) {
console.warn("Failed to get availableBorrowsBase:", err);
console.warn("Failed to get user account data:", err);
}

const contracts = market.reserves.flatMap((asset) => {
const address = asset.address as TAddress;
const aToken = asset.aToken as TAddress;

const calls = [
{
address: aToken,
abi: erc20Abi,
functionName: "balanceOf",
args: [$account],
},
{
address,
abi: erc20Abi,
functionName: "balanceOf",
args: [$account],
},
{
address,
abi: erc20Abi,
functionName: "allowance",
args: [$account, pool],
},
];

if (asset.isBorrowable) {
calls.push({
address: provider,
abi: AaveProtocolDataProviderABI as Abi,
functionName: "getUserReserveData",
args: [address, $account],
});
}

return calls;
});

const results = await client.multicall({
contracts,
allowFailure: false,
});

let index = 0;

for (const asset of market.reserves) {
const address = asset.address as TAddress;
const aTokenAddress = asset.aToken as TAddress;
const priceUSD = Number(asset.price);
const decimals = getTokenData(address)?.decimals ?? 18;
const priceUSD = Number(asset.price);

const rawATokenBalance = await getBalance(
client,
aTokenAddress,
$account
);
const rawATokenBalance = results[index++] as bigint;
const rawBalance = results[index++] as bigint;
const rawAllowance = results[index++] as bigint;

const withdraw = formatUnits(rawATokenBalance, decimals);
const balance = formatUnits(rawBalance, decimals);
const allowance = formatUnits(rawAllowance, decimals);

let maxWithdraw = "0";

Expand All @@ -114,7 +157,7 @@ export const useUserReservesData = (market: TMarket): TResult => {
let _maxWithdraw = withdraw;

if (maxWithdrawTokens < Number(withdraw)) {
maxWithdrawTokens *= 0.998999; // * for safe amount
maxWithdrawTokens *= 0.998999;
_maxWithdraw = maxWithdrawTokens.toString();
}

Expand All @@ -124,68 +167,38 @@ export const useUserReservesData = (market: TMarket): TResult => {
}
}

const [rawBalance, rawAllowance] = await Promise.all([
getBalance(client, address, $account),
getAllowance(client, address, $account, pool),
]);

const balance = formatUnits(rawBalance, decimals);
const allowance = formatUnits(rawAllowance, decimals);

const reserveData: any = {
supply: { balance, allowance },
withdraw: { balance: withdraw, maxWithdraw },
};

if (asset.isBorrowable) {
const tokenPrice = Number(asset.price);
const userReserveData = results[index++] as bigint[];
const rawVariableDebt = userReserveData[2];

const repayBalance = formatUnits(rawVariableDebt, decimals);

let borrow = { balance: "0", maxBorrow: "0" };

if (availableBorrowsBase > 0 && tokenPrice > 0) {
if (availableBorrowsBase > 0 && priceUSD > 0) {
const rawAmount =
(availableBorrowsBase / tokenPrice) * 10 ** decimals;
(availableBorrowsBase / priceUSD) * 10 ** decimals;

const safeAmount = BigInt(Math.floor(rawAmount * 0.999999));

const maxBorrow = formatUnits(safeAmount, decimals);

const availableToBorrow = Number(asset?.availableToBorrow) ?? 0;

const canBorrow = String(
Math.min(availableToBorrow, Number(maxBorrow))
);
const availableToBorrow = Number(asset.availableToBorrow) || 0;

borrow = {
balance: canBorrow,
balance: String(Math.min(availableToBorrow, Number(maxBorrow))),
maxBorrow,
};
}

reserveData.borrow = borrow;

const userReserveData = (await client.readContract({
address: provider,
abi: AaveProtocolDataProviderABI,
functionName: "getUserReserveData",
args: [address, $account],
})) as bigint[];

const rawVariableDebt = userReserveData[2];

const repayBalance = formatUnits(rawVariableDebt, decimals);

const rawRepayAllowance = await getAllowance(
client,
address,
$account,
pool
);

const repayAllowance = formatUnits(rawRepayAllowance, decimals);

reserveData.repay = {
balance: repayBalance,
allowance: repayAllowance,
allowance,
};
}

Expand Down
54 changes: 54 additions & 0 deletions src/modules/RecoveryDashboard/components/Dashboard/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { DashboardCardData } from "../../types";

interface IProps {
card: DashboardCardData;
averageBurnRate: number | null;
totalBurnPercent: number | null;
}

const Card = ({
card,
averageBurnRate,
totalBurnPercent,
}: IProps): JSX.Element => {
return (
<div className="bg-[#101012] backdrop-blur-md border border-[#23252A] rounded-lg p-6 hover:bg-[#23252A] flex flex-col gap-2">
<h3 className="text-[#9798A4] text-base">{card.title}</h3>

<p className="text-white text-[32px] font-bold">
{card.specialType === "averageBurnRate"
? averageBurnRate !== null
? averageBurnRate.toFixed(2) + "%"
: "—"
: card.value}
</p>

{card.subtitle && (
<p className="text-[#9798A4] text-base">{card.subtitle}</p>
)}

{card.specialType === "averageBurnRate" ? (
<p className="text-green-500 text-sm">
{totalBurnPercent !== null &&
totalBurnPercent.toFixed(2) + "% burned across all tokens"}
</p>
) : (
card.change && (
<p
className={`text-sm ${
card.changeType === "positive"
? "text-green-500"
: card.changeType === "negative"
? "text-red-500"
: "text-gray-400"
}`}
>
{card.change}
</p>
)
)}
</div>
);
};

export { Card };
30 changes: 30 additions & 0 deletions src/modules/RecoveryDashboard/components/Dashboard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Card } from "./Card";

import type { DashboardCardData } from "../../types";

interface IProps {
cards: DashboardCardData[];
averageBurnRate: number | null;
totalBurnPercent: number | null;
}

const Dashboard = ({
cards,
averageBurnRate,
totalBurnPercent,
}: IProps): JSX.Element => {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mt-8">
{cards.map((card, index) => (
<Card
key={index}
card={card}
averageBurnRate={averageBurnRate}
totalBurnPercent={totalBurnPercent}
/>
))}
</div>
);
};

export { Dashboard };
22 changes: 22 additions & 0 deletions src/modules/RecoveryDashboard/components/PriceCell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { PriceCellProps } from "../types";

const PriceCell: React.FC<PriceCellProps> = ({ price }) => {
if (!price) {
return <div className="text-[#97979A] text-sm">—</div>;
}

return (
<div className="min-w-0">
<span className="text-[#EAECEF] font-medium text-sm text-balance leading-tight break-words">
1 {price.sym0} ={" "}
{price.price_token1_per_token0.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 4,
})}{" "}
{price.sym1}
</span>
</div>
);
};

export { PriceCell };
Loading
Loading