Skip to content

Commit df4a1db

Browse files
authored
Merge pull request #622 from stabilitydao/beta-ui-mvp
upd: market reserves -> multicall + core instance -> og
2 parents 04e4a24 + 670fd96 commit df4a1db

File tree

22 files changed

+1744
-64
lines changed

22 files changed

+1744
-64
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "stability-ui",
33
"type": "module",
4-
"version": "0.11.44-alpha",
4+
"version": "0.11.46-alpha",
55
"scripts": {
66
"dev": "astro dev",
77
"start": "astro dev",
@@ -18,7 +18,7 @@
1818
"@astrojs/tailwind": "6.0.2",
1919
"@astrojs/vercel": "8.2.10",
2020
"@nanostores/react": "^0.7.1",
21-
"@stabilitydao/stability": "=0.57.2",
21+
"@stabilitydao/stability": "=0.58.2",
2222
"@tanstack/query-sync-storage-persister": "^5.22.2",
2323
"@tanstack/react-query": "^5.22.2",
2424
"@tanstack/react-query-persist-client": "^5.22.2",
114 KB
Loading

public/ogs/pages/recovery.png

1.14 MB
Loading

src/modules/DAO/index.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@ import { useVestingData } from "./hooks";
1818

1919
import { getInitialStateFromUrl } from "./functions";
2020

21+
import { daos } from "@stabilitydao/stability";
22+
2123
import { DAOSectionTypes } from "@types";
2224

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

28+
const stabilityDAO = daos?.find(({ name }) => name === "Stability");
29+
2630
const { section } = getInitialStateFromUrl();
2731

2832
const [activeSection, setActiveSection] = useState(section);
@@ -72,7 +76,9 @@ const DAO = (): JSX.Element => {
7276
<span className="text-[#97979A] text-[16px] leading-5 font-medium">
7377
xSTBL instant exit fee
7478
</span>
75-
<span className="font-semibold">80%</span>
79+
<span className="font-semibold">
80+
{stabilityDAO?.params?.pvpFee}%
81+
</span>
7682
</div>
7783
<div className="flex items-center justify-between">
7884
<span className="text-[#97979A] text-[16px] leading-5 font-medium">

src/modules/Market/hooks/useUserReservesData.ts

Lines changed: 64 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { useEffect } from "react";
22

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

5-
import { formatUnits } from "viem";
5+
import { formatUnits, erc20Abi, Abi } from "viem";
66

7-
import { getBalance, getAllowance, getTokenData } from "@utils";
7+
import { getTokenData } from "@utils";
88

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

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

82+
const contracts = market.reserves.flatMap((asset) => {
83+
const address = asset.address as TAddress;
84+
const aToken = asset.aToken as TAddress;
85+
86+
const calls = [
87+
{
88+
address: aToken,
89+
abi: erc20Abi,
90+
functionName: "balanceOf",
91+
args: [$account],
92+
},
93+
{
94+
address,
95+
abi: erc20Abi,
96+
functionName: "balanceOf",
97+
args: [$account],
98+
},
99+
{
100+
address,
101+
abi: erc20Abi,
102+
functionName: "allowance",
103+
args: [$account, pool],
104+
},
105+
];
106+
107+
if (asset.isBorrowable) {
108+
calls.push({
109+
address: provider,
110+
abi: AaveProtocolDataProviderABI as Abi,
111+
functionName: "getUserReserveData",
112+
args: [address, $account],
113+
});
114+
}
115+
116+
return calls;
117+
});
118+
119+
const results = await client.multicall({
120+
contracts,
121+
allowFailure: false,
122+
});
123+
124+
let index = 0;
125+
82126
for (const asset of market.reserves) {
83127
const address = asset.address as TAddress;
84-
const aTokenAddress = asset.aToken as TAddress;
85-
const priceUSD = Number(asset.price);
86128
const decimals = getTokenData(address)?.decimals ?? 18;
129+
const priceUSD = Number(asset.price);
87130

88-
const rawATokenBalance = await getBalance(
89-
client,
90-
aTokenAddress,
91-
$account
92-
);
131+
const rawATokenBalance = results[index++] as bigint;
132+
const rawBalance = results[index++] as bigint;
133+
const rawAllowance = results[index++] as bigint;
93134

94135
const withdraw = formatUnits(rawATokenBalance, decimals);
136+
const balance = formatUnits(rawBalance, decimals);
137+
const allowance = formatUnits(rawAllowance, decimals);
95138

96139
let maxWithdraw = "0";
97140

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

116159
if (maxWithdrawTokens < Number(withdraw)) {
117-
maxWithdrawTokens *= 0.998999; // * for safe amount
160+
maxWithdrawTokens *= 0.998999;
118161
_maxWithdraw = maxWithdrawTokens.toString();
119162
}
120163

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

127-
const [rawBalance, rawAllowance] = await Promise.all([
128-
getBalance(client, address, $account),
129-
getAllowance(client, address, $account, pool),
130-
]);
131-
132-
const balance = formatUnits(rawBalance, decimals);
133-
const allowance = formatUnits(rawAllowance, decimals);
134-
135170
const reserveData: any = {
136171
supply: { balance, allowance },
137172
withdraw: { balance: withdraw, maxWithdraw },
138173
};
139174

140175
if (asset.isBorrowable) {
141-
const tokenPrice = Number(asset.price);
176+
const userReserveData = results[index++] as bigint[];
177+
const rawVariableDebt = userReserveData[2];
178+
179+
const repayBalance = formatUnits(rawVariableDebt, decimals);
180+
142181
let borrow = { balance: "0", maxBorrow: "0" };
143182

144-
if (availableBorrowsBase > 0 && tokenPrice > 0) {
183+
if (availableBorrowsBase > 0 && priceUSD > 0) {
145184
const rawAmount =
146-
(availableBorrowsBase / tokenPrice) * 10 ** decimals;
185+
(availableBorrowsBase / priceUSD) * 10 ** decimals;
147186

148187
const safeAmount = BigInt(Math.floor(rawAmount * 0.999999));
149-
150188
const maxBorrow = formatUnits(safeAmount, decimals);
151189

152-
const availableToBorrow = Number(asset?.availableToBorrow) ?? 0;
153-
154-
const canBorrow = String(
155-
Math.min(availableToBorrow, Number(maxBorrow))
156-
);
190+
const availableToBorrow = Number(asset.availableToBorrow) || 0;
157191

158192
borrow = {
159-
balance: canBorrow,
193+
balance: String(Math.min(availableToBorrow, Number(maxBorrow))),
160194
maxBorrow,
161195
};
162196
}
163197

164198
reserveData.borrow = borrow;
165-
166-
const userReserveData = (await client.readContract({
167-
address: provider,
168-
abi: AaveProtocolDataProviderABI,
169-
functionName: "getUserReserveData",
170-
args: [address, $account],
171-
})) as bigint[];
172-
173-
const rawVariableDebt = userReserveData[2];
174-
175-
const repayBalance = formatUnits(rawVariableDebt, decimals);
176-
177-
const rawRepayAllowance = await getAllowance(
178-
client,
179-
address,
180-
$account,
181-
pool
182-
);
183-
184-
const repayAllowance = formatUnits(rawRepayAllowance, decimals);
185-
186199
reserveData.repay = {
187200
balance: repayBalance,
188-
allowance: repayAllowance,
201+
allowance,
189202
};
190203
}
191204

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import type { DashboardCardData } from "../../types";
2+
3+
interface IProps {
4+
card: DashboardCardData;
5+
averageBurnRate: number | null;
6+
totalBurnPercent: number | null;
7+
}
8+
9+
const Card = ({
10+
card,
11+
averageBurnRate,
12+
totalBurnPercent,
13+
}: IProps): JSX.Element => {
14+
return (
15+
<div className="bg-[#101012] backdrop-blur-md border border-[#23252A] rounded-lg p-6 hover:bg-[#23252A] flex flex-col gap-2">
16+
<h3 className="text-[#9798A4] text-base">{card.title}</h3>
17+
18+
<p className="text-white text-[32px] font-bold">
19+
{card.specialType === "averageBurnRate"
20+
? averageBurnRate !== null
21+
? averageBurnRate.toFixed(2) + "%"
22+
: "—"
23+
: card.value}
24+
</p>
25+
26+
{card.subtitle && (
27+
<p className="text-[#9798A4] text-base">{card.subtitle}</p>
28+
)}
29+
30+
{card.specialType === "averageBurnRate" ? (
31+
<p className="text-green-500 text-sm">
32+
{totalBurnPercent !== null &&
33+
totalBurnPercent.toFixed(2) + "% burned across all tokens"}
34+
</p>
35+
) : (
36+
card.change && (
37+
<p
38+
className={`text-sm ${
39+
card.changeType === "positive"
40+
? "text-green-500"
41+
: card.changeType === "negative"
42+
? "text-red-500"
43+
: "text-gray-400"
44+
}`}
45+
>
46+
{card.change}
47+
</p>
48+
)
49+
)}
50+
</div>
51+
);
52+
};
53+
54+
export { Card };
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Card } from "./Card";
2+
3+
import type { DashboardCardData } from "../../types";
4+
5+
interface IProps {
6+
cards: DashboardCardData[];
7+
averageBurnRate: number | null;
8+
totalBurnPercent: number | null;
9+
}
10+
11+
const Dashboard = ({
12+
cards,
13+
averageBurnRate,
14+
totalBurnPercent,
15+
}: IProps): JSX.Element => {
16+
return (
17+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mt-8">
18+
{cards.map((card, index) => (
19+
<Card
20+
key={index}
21+
card={card}
22+
averageBurnRate={averageBurnRate}
23+
totalBurnPercent={totalBurnPercent}
24+
/>
25+
))}
26+
</div>
27+
);
28+
};
29+
30+
export { Dashboard };
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { PriceCellProps } from "../types";
2+
3+
const PriceCell: React.FC<PriceCellProps> = ({ price }) => {
4+
if (!price) {
5+
return <div className="text-[#97979A] text-sm"></div>;
6+
}
7+
8+
return (
9+
<div className="min-w-0">
10+
<span className="text-[#EAECEF] font-medium text-sm text-balance leading-tight break-words">
11+
1 {price.sym0} ={" "}
12+
{price.price_token1_per_token0.toLocaleString("en-US", {
13+
minimumFractionDigits: 2,
14+
maximumFractionDigits: 4,
15+
})}{" "}
16+
{price.sym1}
17+
</span>
18+
</div>
19+
);
20+
};
21+
22+
export { PriceCell };

0 commit comments

Comments
 (0)