diff --git a/package.json b/package.json index f863df81..3fe6cbfa 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", diff --git a/public/ogs/markets/Core_Instance.png b/public/ogs/markets/Core_Instance.png new file mode 100644 index 00000000..c2b21f52 Binary files /dev/null and b/public/ogs/markets/Core_Instance.png differ diff --git a/public/ogs/pages/recovery.png b/public/ogs/pages/recovery.png new file mode 100644 index 00000000..3c63cd98 Binary files /dev/null and b/public/ogs/pages/recovery.png differ diff --git a/src/modules/DAO/index.tsx b/src/modules/DAO/index.tsx index 947b1660..6fe15fcf 100644 --- a/src/modules/DAO/index.tsx +++ b/src/modules/DAO/index.tsx @@ -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); @@ -72,7 +76,9 @@ const DAO = (): JSX.Element => { xSTBL instant exit fee - 80% + + {stabilityDAO?.params?.pvpFee}% +
diff --git a/src/modules/Market/hooks/useUserReservesData.ts b/src/modules/Market/hooks/useUserReservesData.ts index cf4e38fc..d5874ebb 100644 --- a/src/modules/Market/hooks/useUserReservesData.ts +++ b/src/modules/Market/hooks/useUserReservesData.ts @@ -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"; @@ -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"; @@ -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(); } @@ -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, }; } diff --git a/src/modules/RecoveryDashboard/components/Dashboard/Card.tsx b/src/modules/RecoveryDashboard/components/Dashboard/Card.tsx new file mode 100644 index 00000000..20cfe433 --- /dev/null +++ b/src/modules/RecoveryDashboard/components/Dashboard/Card.tsx @@ -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 ( +
+

{card.title}

+ +

+ {card.specialType === "averageBurnRate" + ? averageBurnRate !== null + ? averageBurnRate.toFixed(2) + "%" + : "—" + : card.value} +

+ + {card.subtitle && ( +

{card.subtitle}

+ )} + + {card.specialType === "averageBurnRate" ? ( +

+ {totalBurnPercent !== null && + totalBurnPercent.toFixed(2) + "% burned across all tokens"} +

+ ) : ( + card.change && ( +

+ {card.change} +

+ ) + )} +
+ ); +}; + +export { Card }; diff --git a/src/modules/RecoveryDashboard/components/Dashboard/index.tsx b/src/modules/RecoveryDashboard/components/Dashboard/index.tsx new file mode 100644 index 00000000..d695d989 --- /dev/null +++ b/src/modules/RecoveryDashboard/components/Dashboard/index.tsx @@ -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 ( +
+ {cards.map((card, index) => ( + + ))} +
+ ); +}; + +export { Dashboard }; diff --git a/src/modules/RecoveryDashboard/components/PriceCell.tsx b/src/modules/RecoveryDashboard/components/PriceCell.tsx new file mode 100644 index 00000000..b65dd1e8 --- /dev/null +++ b/src/modules/RecoveryDashboard/components/PriceCell.tsx @@ -0,0 +1,22 @@ +import { PriceCellProps } from "../types"; + +const PriceCell: React.FC = ({ price }) => { + if (!price) { + return
; + } + + return ( +
+ + 1 {price.sym0} ={" "} + {price.price_token1_per_token0.toLocaleString("en-US", { + minimumFractionDigits: 2, + maximumFractionDigits: 4, + })}{" "} + {price.sym1} + +
+ ); +}; + +export { PriceCell }; diff --git a/src/modules/RecoveryDashboard/components/TablePagination.tsx b/src/modules/RecoveryDashboard/components/TablePagination.tsx new file mode 100644 index 00000000..ed26ae56 --- /dev/null +++ b/src/modules/RecoveryDashboard/components/TablePagination.tsx @@ -0,0 +1,87 @@ +import { ArrowIcon } from "@ui"; + +interface Props { + currentPage: number; + totalPages: number; + itemsPerPage: number; + totalItems: number; + startIndex: number; + endIndex: number; + onPageChange: (page: number) => void; + onItemsPerPageChange: (value: number) => void; +} + +const TablePagination = ({ + currentPage, + totalPages, + itemsPerPage, + totalItems, + startIndex, + endIndex, + onPageChange, + onItemsPerPageChange, +}: Props): JSX.Element => { + return ( +
+
+
+ Items per page: + +
+ + {startIndex + 1}-{Math.min(endIndex, totalItems)} of {totalItems}{" "} + items + +
+
+
+ + + {currentPage} of {totalPages} pages + + +
+
+ + +
+
+
+ ); +}; + +export { TablePagination }; diff --git a/src/modules/RecoveryDashboard/components/index.ts b/src/modules/RecoveryDashboard/components/index.ts new file mode 100644 index 00000000..ad79846c --- /dev/null +++ b/src/modules/RecoveryDashboard/components/index.ts @@ -0,0 +1,5 @@ +import { Dashboard } from "./Dashboard"; +import { PriceCell } from "./PriceCell"; +import { TablePagination } from "./TablePagination"; + +export { Dashboard, PriceCell, TablePagination }; diff --git a/src/modules/RecoveryDashboard/constants/index.ts b/src/modules/RecoveryDashboard/constants/index.ts new file mode 100644 index 00000000..ef1f5aaf --- /dev/null +++ b/src/modules/RecoveryDashboard/constants/index.ts @@ -0,0 +1,120 @@ +import type { Column, DashboardCardData } from "../types"; + +const tokenColumns: Column[] = [ + { label: "Token", sortable: true }, + { label: "Token Address", sortable: false }, + { label: "Recovery Progress", sortable: true }, +]; + +const tokenData = [ + { + name: "Recovery Stability USD", + symbol: "RECmetaUSD", + address: "0x000078392f3cF4262500FFeB7d803F90477ECC11" as `0x${string}`, + initialSupply: 300836932902619695422070n, + decimals: 18, + }, + { + name: "Recovery Wrapped Stability USD", + symbol: "RECwmetaUSD", + address: "0x00001b2c60cD041a478521008CE6efeC475bb9Aa" as `0x${string}`, + initialSupply: 1571426158089874911823524n, + decimals: 18, + }, + { + name: "Recovery Wrapped Stability USDC", + symbol: "RECwmetaUSDC", + address: "0x0000a59C549b4250a2931ac6054e1426a87DA0EE" as `0x${string}`, + initialSupply: BigInt(101603724905), + decimals: 6, + }, + { + name: "Recovery Wrapped Stability scUSD", + symbol: "RECwmetascUSD", + address: "0x0000c3b22bbD290588361E4B5C424F3AB0d0a3cc" as `0x${string}`, + initialSupply: BigInt(1033496932628), + decimals: 6, + }, + { + name: "Recovery Stability S", + symbol: "RECmetaS", + address: "0x000006539BA0B4f5452186Af40aAB959bDEa4344" as `0x${string}`, + initialSupply: 1101293369505074707160718n, + decimals: 18, + }, + { + name: "Recovery Wrapped Stability S", + symbol: "RECwmetaS", + address: "0x0000Dd8cEa00EA3336f5849590d69bbfc93A85bb" as `0x${string}`, + initialSupply: 3166187266373775158998584n, + decimals: 18, + }, +]; + +const poolColumns: Column[] = [ + { label: "Pair", sortable: true }, + { label: "Pool Address", sortable: false }, + { label: "Price", sortable: true }, +]; + +const poolData = [ + { + name: "Shadow RECmetaUSD/wmetaUSD", + url: "https://www.shadow.so/liquidity/manage/0x1e2edba99efd08578460bd9a66f4f521ec861eb9", + pair: "RECmetaUSD/wmetaUSD", + address: "0x1e2edBa99efd08578460BD9A66f4f521EC861eb9" as `0x${string}`, + }, + { + name: "Shadow RECwmetaUSD/wmetaUSD", + url: "https://www.shadow.so/liquidity/manage/0xd473a0f23f61f63f4e736b16f6133317f0ae4c0a", + pair: "RECwmetaUSD/wmetaUSD", + address: "0xd473A0F23f61F63F4e736b16f6133317F0ae4c0a" as `0x${string}`, + }, + { + name: "Shadow RECwmetaUSDC/wmetaUSD", + url: "https://www.shadow.so/liquidity/manage/0x41fc91524e97678f81362f3703b16b07ace0ae23", + pair: "RECwmetaUSDC/wmetaUSD", + address: "0x41FC91524E97678F81362f3703B16b07Ace0ae23" as `0x${string}`, + }, + { + name: "Shadow RECwmetascUSD/wmetaUSD", + url: "https://www.shadow.so/liquidity/manage/0x64c52e6c35c77150b58dc947672c4da606528f85", + pair: "RECwmetascUSD/wmetaUSD", + address: "0x64c52E6C35C77150B58DC947672c4dA606528F85" as `0x${string}`, + }, + { + name: "Shadow RECmetaS/wmetaS", + url: "https://www.shadow.so/liquidity/manage/0xb7b6a318621eb0fda0893549ea4ee5da4cecf19e", + pair: "RECmetaS/wmetaS", + address: "0xB7B6A318621eb0FDA0893549Ea4eE5DA4CecF19E" as `0x${string}`, + }, + { + name: "Shadow RECwmetaS/wmetaS", + url: "https://www.shadow.so/liquidity/manage/0xc68fac41bfc940fb5126ba1e790456ae273de9e7", + pair: "RECwmetaS/wmetaS", + address: "0xc68FaC41Bfc940FB5126Ba1E790456ae273de9E7" as `0x${string}`, + }, +]; + +const dashboardData: DashboardCardData[] = [ + { + title: "Total Tokens", + value: tokenData.length, + subtitle: "Recovery tokens", + specialType: null, + }, + { + title: "Average Burn Rate", + value: null, + subtitle: null, + specialType: "averageBurnRate", + }, + { + title: "Total Pools", + value: poolData.length, + subtitle: "Recovery pools", + specialType: null, + }, +]; + +export { tokenColumns, tokenData, poolColumns, poolData, dashboardData }; diff --git a/src/modules/RecoveryDashboard/index.tsx b/src/modules/RecoveryDashboard/index.tsx new file mode 100644 index 00000000..5e32c77e --- /dev/null +++ b/src/modules/RecoveryDashboard/index.tsx @@ -0,0 +1,541 @@ +import { useState, useEffect, useMemo, useCallback } from "react"; + +import { Address, erc20Abi } from "viem"; +import { readContract, readContracts } from "@wagmi/core"; + +import { TablePagination, PriceCell, Dashboard } from "./components"; + +import { FullPageLoader } from "@ui"; + +import { getShortAddress } from "@utils"; + +import { + dashboardData, + tokenColumns, + tokenData, + poolColumns, + poolData, +} from "./constants"; + +import { wagmiConfig, RamsesV3PoolABI } from "@web3"; + +import { chains } from "@stabilitydao/stability"; + +import { GetPriceReturn, PriceCache, Token } from "./types"; + +const RecoveryDashboard = (): JSX.Element => { + const [copiedId, setCopiedId] = useState(null); + const [searchQuery, setSearchQuery] = useState(""); + const [currentPage, setCurrentPage] = useState(1); + const [itemsPerPage, setItemsPerPage] = useState(10); + + const [priceCache, setPriceCache] = useState({}); + const [tokenBurnProgress, setTokenBurnProgress] = useState< + Record + >({}); + const [averageBurnRate, setAverageBurnRate] = useState(null); + const [totalBurnPercent, setTotalBurnPercent] = useState(null); + + const [isTokensLoaded, setIsTokensLoaded] = useState(false); + const [isPoolsLoaded, setIsPoolsLoaded] = useState(false); + const [isDashboardLoaded, setIsDashboardLoaded] = useState(false); + + const isLoading = !( + isTokensLoaded && + isPoolsLoaded && + averageBurnRate !== null && + totalBurnPercent !== null && + isDashboardLoaded + ); + + const handleCopyAddress = useCallback((address: string, id: string) => { + navigator.clipboard.writeText(address).then(() => { + setCopiedId(id); + window.setTimeout(() => setCopiedId(null), 2000); + }); + }, []); + + const getPriceOnChain = useCallback( + async (poolAddress: Address): Promise => { + const token0 = (await readContract(wagmiConfig, { + address: poolAddress, + abi: RamsesV3PoolABI, + functionName: "token0", + })) as Address; + + const token1 = (await readContract(wagmiConfig, { + address: poolAddress, + abi: RamsesV3PoolABI, + functionName: "token1", + })) as Address; + + const d0 = (await readContract(wagmiConfig, { + address: token0, + abi: erc20Abi, + functionName: "decimals", + })) as number; + + const d1 = (await readContract(wagmiConfig, { + address: token1, + abi: erc20Abi, + functionName: "decimals", + })) as number; + + const sym0 = (await readContract(wagmiConfig, { + address: token0, + abi: erc20Abi, + functionName: "symbol", + })) as string; + + const sym1 = (await readContract(wagmiConfig, { + address: token1, + abi: erc20Abi, + functionName: "symbol", + })) as string; + + const slot0 = (await readContract(wagmiConfig, { + address: poolAddress, + abi: RamsesV3PoolABI, + functionName: "slot0", + })) as [bigint, number, number, number, number, number, boolean]; + + const sqrtPriceX96 = BigInt(slot0[0]); + const Q96 = BigInt(2n ** 96n); + + const p_raw = Number(sqrtPriceX96 * sqrtPriceX96) / Number(Q96 * Q96); + const decimalAdj = 10 ** (d0 - d1); + + const price_token1_per_token0 = p_raw * decimalAdj; + const price_token0_per_token1 = 1 / price_token1_per_token0; + + return { price_token1_per_token0, price_token0_per_token1, sym0, sym1 }; + }, + [] + ); + + const getBurnProgressOnChain = useCallback( + async (token: Token): Promise => { + try { + const [totalSupplyRes, multisigBalanceRes] = await readContracts( + wagmiConfig, + { + contracts: [ + { + address: token.address, + abi: erc20Abi, + functionName: "totalSupply", + }, + { + address: token.address, + abi: erc20Abi, + functionName: "balanceOf", + args: [chains[146].multisig as Address], + }, + ], + } + ); + + const initialSupplyRaw = token.initialSupply as bigint; + const totalSupplyRaw = totalSupplyRes.result as bigint; + const multisigBalanceRaw = multisigBalanceRes.result as bigint; + + const burntRaw = initialSupplyRaw - totalSupplyRaw + multisigBalanceRaw; + + const percent = Number((burntRaw * 100000n) / initialSupplyRaw) / 1000; + + return percent; + } catch { + return 0; + } + }, + [] + ); + + const computeAverageBurnRate = useCallback((map: Record) => { + const values = Object.values(map); + if (values.length === 0) return 0; + return values.reduce((s, v) => s + v, 0) / values.length; + }, []); + + const computeTotalBurnPercent = useCallback(async (tokens: Token[]) => { + let totalInitial = 0n; + let totalBurned = 0n; + + for (const token of tokens) { + try { + const currentSupply = (await readContract(wagmiConfig, { + address: token.address, + abi: erc20Abi, + functionName: "totalSupply", + })) as bigint; + + totalInitial += token.initialSupply; + totalBurned += token.initialSupply - currentSupply; + } catch {} + } + + if (totalInitial === 0n) return 0; + return Number((totalBurned * 10000n) / totalInitial) / 100; + }, []); + + useEffect(() => { + let mounted = true; + + const fetchAllTokenBurns = async () => { + const progressMap: Record = {}; + + const promises = (tokenData as Token[]).map((t) => + getBurnProgressOnChain(t) + .then((p) => ({ address: t.address, p })) + .catch(() => ({ address: t.address, p: 0 })) + ); + + const results = await Promise.all(promises); + results.forEach((r) => (progressMap[r.address] = r.p)); + + if (!mounted) return; + + setTokenBurnProgress(progressMap); + setIsTokensLoaded(true); + + const avg = computeAverageBurnRate(progressMap); + setAverageBurnRate(avg); + + const total = await computeTotalBurnPercent(tokenData as Token[]); + if (mounted) setTotalBurnPercent(total); + }; + + fetchAllTokenBurns(); + return () => { + mounted = false; + }; + }, [getBurnProgressOnChain, computeAverageBurnRate, computeTotalBurnPercent]); + + useEffect(() => { + let mounted = true; + + const fetchAllPoolPrices = async () => { + const cache: PriceCache = {}; + + const results = await Promise.all( + (poolData || []).map((pool) => + getPriceOnChain(pool.address) + .then((p) => ({ address: pool.address, p })) + .catch(() => ({ address: pool.address, p: null })) + ) + ); + + results.forEach((r) => { + if (r.p) cache[r.address] = r.p; + }); + + if (!mounted) return; + + setPriceCache(cache); + setIsPoolsLoaded(true); + }; + + fetchAllPoolPrices(); + return () => { + mounted = false; + }; + }, [getPriceOnChain]); + + useEffect(() => { + if ( + isTokensLoaded && + isPoolsLoaded && + averageBurnRate !== null && + totalBurnPercent !== null + ) { + setIsDashboardLoaded(true); + } + }, [isTokensLoaded, isPoolsLoaded, averageBurnRate, totalBurnPercent]); + + const filteredPools = useMemo(() => { + const query = searchQuery.trim().toLowerCase(); + if (!query) return poolData; + return poolData.filter( + (pool) => + pool.pair.toLowerCase().includes(query) || + pool.address.toLowerCase().includes(query) + ); + }, [searchQuery]); + + const totalPages = useMemo(() => { + return Math.max(1, Math.ceil(filteredPools.length / itemsPerPage)); + }, [filteredPools.length, itemsPerPage]); + + useEffect(() => { + setCurrentPage((prev) => Math.min(prev, totalPages)); + }, [totalPages]); + + const startIndex = (currentPage - 1) * itemsPerPage; + const endIndex = startIndex + itemsPerPage; + const currentPools = filteredPools.slice(startIndex, endIndex); + + const handleSearchChange = useCallback( + (e: React.ChangeEvent) => { + setSearchQuery(e.target.value); + setCurrentPage(1); + }, + [] + ); + + const getBurnStatus = useCallback( + (address: Address) => { + const progress = tokenBurnProgress[address] ?? 0; + return progress > 0 ? "Burned" : "No tokens burned"; + }, + [tokenBurnProgress] + ); + + return ( +
+
+

+ Recovery Dashboard +

+ +

+ Monitor recovery pool prices and token burn progress +

+ + {!isLoading && ( + + )} +
+ {isLoading ? ( +
+ +
+ ) : ( +
+
+

+ Recovery Tokens +

+

+ Monitor recovery token burn progress +

+ +
+
+
+
+ {tokenColumns.map((column) => ( +
{column.label}
+ ))} +
+ + {tokenData.map((token) => ( +
+ + {token.symbol} + + +
+ + {getShortAddress(token.address, 6, 4)} + + + +
+ +
+
+
+ {getBurnStatus(token.address)} +
+
+
+
+
+ + + {(tokenBurnProgress[token.address] ?? 0).toFixed(2)} % + +
+
+ ))} +
+
+
+
+
+

+ Recovery Pools +

+

+ View recovery pool prices and trading pairs +

+ +
+
+ + Search + + +
+
+ +
+
+
+
+ {poolColumns.map((column) => ( +
{column.label}
+ ))} +
+ + {currentPools.length > 0 ? ( + currentPools.map((pool) => ( +
+ + {pool.pair} + + +
+ + {getShortAddress(pool.address, 6, 4)} + + + + + external link + +
+ +
+ +
+
+ )) + ) : ( +
+

+ No pools found matching "{searchQuery}" +

+
+ )} +
+
+ + {filteredPools.length > 0 && ( + + )} +
+
+
+ )} +
+ ); +}; + +export { RecoveryDashboard }; diff --git a/src/modules/RecoveryDashboard/types/index.ts b/src/modules/RecoveryDashboard/types/index.ts new file mode 100644 index 00000000..e6f4b4f4 --- /dev/null +++ b/src/modules/RecoveryDashboard/types/index.ts @@ -0,0 +1,46 @@ +import { Address } from "viem"; + +interface DashboardCardData { + title: string; + value: string | number | null; + subtitle?: string | null; + change?: string | null; + changeType?: string | null; + specialType?: string | null; +} + +type GetPriceReturn = { + price_token1_per_token0: number; + price_token0_per_token1: number; + sym0: string; + sym1: string; +}; + +type PriceCache = Record; + +type Token = { + name: string; + symbol: string; + address: Address; + initialSupply: bigint; + decimals: number; +}; + +type PriceCellProps = { + price?: GetPriceReturn | null; +}; + +interface Column { + label: string; + sortable: boolean; + onSort?: () => void; +} + +export type { + Column, + PriceCellProps, + Token, + PriceCache, + GetPriceReturn, + DashboardCardData, +}; diff --git a/src/modules/Staking/components/ExitForms/InstantExit.tsx b/src/modules/Staking/components/ExitForms/InstantExit.tsx index c975e5d5..97695c2e 100644 --- a/src/modules/Staking/components/ExitForms/InstantExit.tsx +++ b/src/modules/Staking/components/ExitForms/InstantExit.tsx @@ -16,6 +16,8 @@ import { sonicClient, ERC20ABI, IXSTBLABI, wagmiConfig } from "@web3"; import { STABILITY_TOKENS } from "@constants"; +import { daos } from "@stabilitydao/stability"; + import type { TAddress } from "@types"; const InstantExit: React.FC = () => { @@ -23,6 +25,8 @@ const InstantExit: React.FC = () => { const $account = useStore(account); const $lastTx = useStore(lastTx); + const stabilityDAO = daos?.find(({ name }) => name === "Stability"); + const input = useRef(null); const [balance, setBalance] = useState("0"); @@ -126,8 +130,9 @@ const InstantExit: React.FC = () => { Instant Exit - Instantly exit to STBL, incurring a 80% penalty. This process cannot - be reverted, all forfeited STBL is streamed back to stakers. + Instantly exit to STBL, incurring a {stabilityDAO?.params?.pvpFee}% + penalty. This process cannot be reverted, all forfeited STBL is + streamed back to stakers.
diff --git a/src/modules/Staking/components/ExitForms/VestedExit.tsx b/src/modules/Staking/components/ExitForms/VestedExit.tsx index 17d5c0c8..f1f095ac 100644 --- a/src/modules/Staking/components/ExitForms/VestedExit.tsx +++ b/src/modules/Staking/components/ExitForms/VestedExit.tsx @@ -18,6 +18,8 @@ import { account, connected, lastTx } from "@store"; import { STABILITY_TOKENS } from "@constants"; +import { daos } from "@stabilitydao/stability"; + import type { TAddress, TVestPeriod } from "@types"; const VestedExit: React.FC = () => { @@ -25,6 +27,10 @@ const VestedExit: React.FC = () => { const $account = useStore(account); const $lastTx = useStore(lastTx); + const stabilityDAO = daos?.find(({ name }) => name === "Stability"); + + const ratio = (100 - Number(stabilityDAO?.params?.pvpFee)) / 100; + const input = useRef(null); const dropDownRef = useRef(null); @@ -280,8 +286,8 @@ const VestedExit: React.FC = () => { Redeem xSTBL over a vesting period. Choose a minimum vest of 15 days - (1:0.5 ratio) to maximum vest of 6 months (1:1 ratio). You can cancel - the vest in the first 14 days. + (1:{ratio} ratio) to maximum vest of 6 months (1:1 ratio). You can + cancel the vest in the first 14 days.
diff --git a/src/modules/index.ts b/src/modules/index.ts index 0c3f98a4..15bd7616 100644 --- a/src/modules/index.ts +++ b/src/modules/index.ts @@ -30,6 +30,7 @@ import { StabilityBuilder, StabilityOperator } from "./Platform"; import { DAO } from "./DAO"; import { Season1, Season2 } from "./Seasons"; +import { RecoveryDashboard } from "./RecoveryDashboard"; export { Factory, @@ -65,4 +66,5 @@ export { Season1, Season2, LeverageVault, + RecoveryDashboard, }; diff --git a/src/pages/lending/1/Core-Instance.astro b/src/pages/lending/1/Core-Instance.astro index 6d883e80..729f5e83 100644 --- a/src/pages/lending/1/Core-Instance.astro +++ b/src/pages/lending/1/Core-Instance.astro @@ -4,10 +4,9 @@ import Layout from '@layouts/Layout.astro'; import { Market } from "@modules" --- - + + \ No newline at end of file diff --git a/src/ui/Breadcrumbs.tsx b/src/ui/Breadcrumbs.tsx index e871f71d..ab3caac6 100644 --- a/src/ui/Breadcrumbs.tsx +++ b/src/ui/Breadcrumbs.tsx @@ -124,6 +124,8 @@ const Breadcrumbs = (): JSX.Element => { } else if (main === "builder") { add("Platform", "platform"); add("Builder"); + } else if (currentPath === "recovery") { + add("Recovery Dashboard", "recovery"); } setPaths(crumbs); diff --git a/src/web3/abi/RamsesV3PoolABI.ts b/src/web3/abi/RamsesV3PoolABI.ts new file mode 100644 index 00000000..5e72d88a --- /dev/null +++ b/src/web3/abi/RamsesV3PoolABI.ts @@ -0,0 +1,726 @@ +export default [ + { inputs: [], stateMutability: "nonpayable", type: "constructor" }, + { inputs: [], name: "AI", type: "error" }, + { inputs: [], name: "AS", type: "error" }, + { inputs: [], name: "F0", type: "error" }, + { inputs: [], name: "F1", type: "error" }, + { inputs: [], name: "I", type: "error" }, + { inputs: [], name: "IIA", type: "error" }, + { inputs: [], name: "L", type: "error" }, + { inputs: [], name: "LOK", type: "error" }, + { inputs: [], name: "M0", type: "error" }, + { inputs: [], name: "M1", type: "error" }, + { inputs: [], name: "NOT_AUTHORIZED", type: "error" }, + { inputs: [], name: "OLD", type: "error" }, + { inputs: [], name: "R", type: "error" }, + { inputs: [], name: "SPL", type: "error" }, + { inputs: [], name: "T", type: "error" }, + { inputs: [], name: "TF", type: "error" }, + { inputs: [], name: "TLM", type: "error" }, + { inputs: [], name: "TLU", type: "error" }, + { inputs: [], name: "TUM", type: "error" }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "int24", + name: "tickLower", + type: "int24", + }, + { + indexed: true, + internalType: "int24", + name: "tickUpper", + type: "int24", + }, + { + indexed: false, + internalType: "uint128", + name: "amount", + type: "uint128", + }, + { + indexed: false, + internalType: "uint256", + name: "amount0", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "amount1", + type: "uint256", + }, + ], + name: "Burn", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "recipient", + type: "address", + }, + { + indexed: true, + internalType: "int24", + name: "tickLower", + type: "int24", + }, + { + indexed: true, + internalType: "int24", + name: "tickUpper", + type: "int24", + }, + { + indexed: false, + internalType: "uint128", + name: "amount0", + type: "uint128", + }, + { + indexed: false, + internalType: "uint128", + name: "amount1", + type: "uint128", + }, + ], + name: "Collect", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "recipient", + type: "address", + }, + { + indexed: false, + internalType: "uint128", + name: "amount0", + type: "uint128", + }, + { + indexed: false, + internalType: "uint128", + name: "amount1", + type: "uint128", + }, + ], + name: "CollectProtocol", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "recipient", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount0", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "amount1", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "paid0", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "paid1", + type: "uint256", + }, + ], + name: "Flash", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint16", + name: "observationCardinalityNextOld", + type: "uint16", + }, + { + indexed: false, + internalType: "uint16", + name: "observationCardinalityNextNew", + type: "uint16", + }, + ], + name: "IncreaseObservationCardinalityNext", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint160", + name: "sqrtPriceX96", + type: "uint160", + }, + { indexed: false, internalType: "int24", name: "tick", type: "int24" }, + ], + name: "Initialize", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "sender", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "int24", + name: "tickLower", + type: "int24", + }, + { + indexed: true, + internalType: "int24", + name: "tickUpper", + type: "int24", + }, + { + indexed: false, + internalType: "uint128", + name: "amount", + type: "uint128", + }, + { + indexed: false, + internalType: "uint256", + name: "amount0", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "amount1", + type: "uint256", + }, + ], + name: "Mint", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint8", + name: "feeProtocol0Old", + type: "uint8", + }, + { + indexed: false, + internalType: "uint8", + name: "feeProtocol1Old", + type: "uint8", + }, + { + indexed: false, + internalType: "uint8", + name: "feeProtocol0New", + type: "uint8", + }, + { + indexed: false, + internalType: "uint8", + name: "feeProtocol1New", + type: "uint8", + }, + ], + name: "SetFeeProtocol", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "recipient", + type: "address", + }, + { + indexed: false, + internalType: "int256", + name: "amount0", + type: "int256", + }, + { + indexed: false, + internalType: "int256", + name: "amount1", + type: "int256", + }, + { + indexed: false, + internalType: "uint160", + name: "sqrtPriceX96", + type: "uint160", + }, + { + indexed: false, + internalType: "uint128", + name: "liquidity", + type: "uint128", + }, + { indexed: false, internalType: "int24", name: "tick", type: "int24" }, + ], + name: "Swap", + type: "event", + }, + { + inputs: [], + name: "_advancePeriod", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "index", type: "uint256" }, + { internalType: "int24", name: "tickLower", type: "int24" }, + { internalType: "int24", name: "tickUpper", type: "int24" }, + { internalType: "uint128", name: "amount", type: "uint128" }, + ], + name: "burn", + outputs: [ + { internalType: "uint256", name: "amount0", type: "uint256" }, + { internalType: "uint256", name: "amount1", type: "uint256" }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "uint256", name: "index", type: "uint256" }, + { internalType: "int24", name: "tickLower", type: "int24" }, + { internalType: "int24", name: "tickUpper", type: "int24" }, + { internalType: "uint128", name: "amount0Requested", type: "uint128" }, + { internalType: "uint128", name: "amount1Requested", type: "uint128" }, + ], + name: "collect", + outputs: [ + { internalType: "uint128", name: "amount0", type: "uint128" }, + { internalType: "uint128", name: "amount1", type: "uint128" }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "uint128", name: "amount0Requested", type: "uint128" }, + { internalType: "uint128", name: "amount1Requested", type: "uint128" }, + ], + name: "collectProtocol", + outputs: [ + { internalType: "uint128", name: "amount0", type: "uint128" }, + { internalType: "uint128", name: "amount1", type: "uint128" }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "factory", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "fee", + outputs: [{ internalType: "uint24", name: "", type: "uint24" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "feeGrowthGlobal0X128", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "feeGrowthGlobal1X128", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "uint256", name: "amount0", type: "uint256" }, + { internalType: "uint256", name: "amount1", type: "uint256" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + name: "flash", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint16", + name: "observationCardinalityNext", + type: "uint16", + }, + ], + name: "increaseObservationCardinalityNext", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint160", name: "sqrtPriceX96", type: "uint160" }, + ], + name: "initialize", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "lastPeriod", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "liquidity", + outputs: [{ internalType: "uint128", name: "", type: "uint128" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "maxLiquidityPerTick", + outputs: [{ internalType: "uint128", name: "", type: "uint128" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "uint256", name: "index", type: "uint256" }, + { internalType: "int24", name: "tickLower", type: "int24" }, + { internalType: "int24", name: "tickUpper", type: "int24" }, + { internalType: "uint128", name: "amount", type: "uint128" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + name: "mint", + outputs: [ + { internalType: "uint256", name: "amount0", type: "uint256" }, + { internalType: "uint256", name: "amount1", type: "uint256" }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "index", type: "uint256" }], + name: "observations", + outputs: [ + { internalType: "uint32", name: "blockTimestamp", type: "uint32" }, + { internalType: "int56", name: "tickCumulative", type: "int56" }, + { + internalType: "uint160", + name: "secondsPerLiquidityCumulativeX128", + type: "uint160", + }, + { internalType: "bool", name: "initialized", type: "bool" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint32[]", name: "secondsAgos", type: "uint32[]" }, + ], + name: "observe", + outputs: [ + { internalType: "int56[]", name: "tickCumulatives", type: "int56[]" }, + { + internalType: "uint160[]", + name: "secondsPerLiquidityCumulativeX128s", + type: "uint160[]", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "period", type: "uint256" }], + name: "periods", + outputs: [ + { internalType: "uint32", name: "previousPeriod", type: "uint32" }, + { internalType: "int24", name: "startTick", type: "int24" }, + { internalType: "int24", name: "lastTick", type: "int24" }, + { + internalType: "uint160", + name: "endSecondsPerLiquidityPeriodX128", + type: "uint160", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "period", type: "uint256" }, + { internalType: "address", name: "owner", type: "address" }, + { internalType: "uint256", name: "index", type: "uint256" }, + { internalType: "int24", name: "tickLower", type: "int24" }, + { internalType: "int24", name: "tickUpper", type: "int24" }, + ], + name: "positionPeriodSecondsInRange", + outputs: [ + { + internalType: "uint256", + name: "periodSecondsInsideX96", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "key", type: "bytes32" }], + name: "positions", + outputs: [ + { internalType: "uint128", name: "liquidity", type: "uint128" }, + { + internalType: "uint256", + name: "feeGrowthInside0LastX128", + type: "uint256", + }, + { + internalType: "uint256", + name: "feeGrowthInside1LastX128", + type: "uint256", + }, + { internalType: "uint128", name: "tokensOwed0", type: "uint128" }, + { internalType: "uint128", name: "tokensOwed1", type: "uint128" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "protocolFees", + outputs: [ + { internalType: "uint128", name: "", type: "uint128" }, + { internalType: "uint128", name: "", type: "uint128" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32[]", name: "slots", type: "bytes32[]" }], + name: "readStorage", + outputs: [ + { internalType: "bytes32[]", name: "returnData", type: "bytes32[]" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint24", name: "_fee", type: "uint24" }], + name: "setFee", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "setFeeProtocol", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "slot0", + outputs: [ + { internalType: "uint160", name: "sqrtPriceX96", type: "uint160" }, + { internalType: "int24", name: "tick", type: "int24" }, + { internalType: "uint16", name: "observationIndex", type: "uint16" }, + { + internalType: "uint16", + name: "observationCardinality", + type: "uint16", + }, + { + internalType: "uint16", + name: "observationCardinalityNext", + type: "uint16", + }, + { internalType: "uint8", name: "feeProtocol", type: "uint8" }, + { internalType: "bool", name: "unlocked", type: "bool" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "int24", name: "tickLower", type: "int24" }, + { internalType: "int24", name: "tickUpper", type: "int24" }, + ], + name: "snapshotCumulativesInside", + outputs: [ + { internalType: "int56", name: "tickCumulativeInside", type: "int56" }, + { + internalType: "uint160", + name: "secondsPerLiquidityInsideX128", + type: "uint160", + }, + { internalType: "uint32", name: "secondsInside", type: "uint32" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "bool", name: "zeroForOne", type: "bool" }, + { internalType: "int256", name: "amountSpecified", type: "int256" }, + { internalType: "uint160", name: "sqrtPriceLimitX96", type: "uint160" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + name: "swap", + outputs: [ + { internalType: "int256", name: "amount0", type: "int256" }, + { internalType: "int256", name: "amount1", type: "int256" }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "int16", name: "tick", type: "int16" }], + name: "tickBitmap", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "tickSpacing", + outputs: [{ internalType: "int24", name: "", type: "int24" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "int24", name: "tick", type: "int24" }], + name: "ticks", + outputs: [ + { internalType: "uint128", name: "liquidityGross", type: "uint128" }, + { internalType: "int128", name: "liquidityNet", type: "int128" }, + { + internalType: "uint256", + name: "feeGrowthOutside0X128", + type: "uint256", + }, + { + internalType: "uint256", + name: "feeGrowthOutside1X128", + type: "uint256", + }, + { internalType: "int56", name: "tickCumulativeOutside", type: "int56" }, + { + internalType: "uint160", + name: "secondsPerLiquidityOutsideX128", + type: "uint160", + }, + { internalType: "uint32", name: "secondsOutside", type: "uint32" }, + { internalType: "bool", name: "initialized", type: "bool" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "token0", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "token1", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, +] as const; diff --git a/src/web3/index.ts b/src/web3/index.ts index 58f1aeeb..17c7eb63 100644 --- a/src/web3/index.ts +++ b/src/web3/index.ts @@ -27,6 +27,7 @@ import AavePoolABI from "./abi/AavePoolABI.ts"; import AaveProtocolDataProviderABI from "./abi/AaveProtocolDataProviderABI.ts"; import StabilityDAOABI from "./abi/StabilityDAOABI.ts"; import VestingABI from "./abi/VestingABI.ts"; +import RamsesV3PoolABI from "./abi/RamsesV3PoolABI.ts"; import type { TAddress } from "@types"; @@ -166,4 +167,5 @@ export { AaveProtocolDataProviderABI, StabilityDAOABI, VestingABI, + RamsesV3PoolABI, }; diff --git a/yarn.lock b/yarn.lock index 5ae2743c..e6eec353 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2013,10 +2013,10 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== -"@stabilitydao/stability@=0.57.2": - version "0.57.2" - resolved "https://registry.yarnpkg.com/@stabilitydao/stability/-/stability-0.57.2.tgz#436a5bf99326c591ab29e36096d559e72f6f31f9" - integrity sha512-XxgNt9Vhviggmaeg1vlCtJ54HueheTUNgvB9pf4vbzUpij5SBCCHyQAGZ4M27mttfXtwClJ01ueJc1Skm5JSyA== +"@stabilitydao/stability@=0.58.2": + version "0.58.2" + resolved "https://registry.yarnpkg.com/@stabilitydao/stability/-/stability-0.58.2.tgz#53872999d3a9b03a453a848e461fb35c5b90cf81" + integrity sha512-e+VUOb+u23FOx2Hi9MXSqRHmgPrlmvgkK6Xb9KOw5Nd7IrJFEfctTCb6Cinb2xeLuggsxffeI037k+2HsIl07w== "@stablelib/aead@^1.0.1": version "1.0.1"