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:
+ onItemsPerPageChange(Number(e.target.value))}
+ className="text-white bg-[#151618] cursor-pointer"
+ >
+ 5
+ 10
+ 15
+ 20
+ 30
+
+
+
+ {startIndex + 1}-{Math.min(endIndex, totalItems)} of {totalItems}{" "}
+ items
+
+
+
+
+ onPageChange(Math.max(currentPage - 1, 1))}
+ disabled={currentPage === 1}
+ className="px-3 text-white disabled:text-[#97979A] cursor-pointer disabled:cursor-not-allowed"
+ >
+ < Previous
+
+
+ {currentPage} of {totalPages} pages
+
+ onPageChange(Math.min(currentPage + 1, totalPages))}
+ disabled={currentPage === totalPages}
+ className="px-3 text-white disabled:text-[#97979A] cursor-pointer disabled:cursor-not-allowed"
+ >
+ Next >
+
+
+
+
onPageChange(Math.max(currentPage - 1, 1))}
+ disabled={currentPage === 1}
+ className="border-r border-l border-[#23252a] py-3 px-4 text-white disabled:text-[#97979A] cursor-pointer disabled:cursor-not-allowed"
+ >
+
+
+
onPageChange(Math.min(currentPage + 1, totalPages))}
+ disabled={currentPage === totalPages}
+ className="py-3 pr-4 text-white disabled:text-[#97979A] cursor-pointer disabled:cursor-not-allowed"
+ >
+
+
+
+
+
+ );
+};
+
+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)}
+
+
+
+ handleCopyAddress(token.address, token.address)
+ }
+ className="h-6 w-6 flex items-center justify-center"
+ >
+ {copiedId === token.address ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+ {getBurnStatus(token.address)}
+
+
+
+
+
+ {(tokenBurnProgress[token.address] ?? 0).toFixed(2)} %
+
+
+
+ ))}
+
+
+
+
+
+
+ Recovery Pools
+
+
+ View recovery pool prices and trading pairs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {poolColumns.map((column) => (
+
{column.label}
+ ))}
+
+
+ {currentPools.length > 0 ? (
+ currentPools.map((pool) => (
+
+
+ {pool.pair}
+
+
+
+
+ {getShortAddress(pool.address, 6, 4)}
+
+
+
+ handleCopyAddress(pool.address, pool.address)
+ }
+ className="h-6 w-6 flex items-center justify-center"
+ >
+ {copiedId === pool.address ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+ ))
+ ) : (
+
+
+ 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"