From 573393d0db0a10259fcd60a5793a32f50e48aa6d Mon Sep 17 00:00:00 2001 From: matzapata Date: Wed, 7 Aug 2024 21:25:05 +0300 Subject: [PATCH 001/116] aave page static components, mobile first --- .../src/app/5_pages/AavePage/AaavePage.tsx | 85 +++++++++++++++++++ .../BorrowingPositionsList.tsx | 35 ++++++++ .../LendingPositionsList.tsx | 35 ++++++++ .../AavePage/components/TopPanel/TopPanel.tsx | 67 +++++++++++++++ .../WalletStatCard/WalletStatCard.tsx | 39 +++++++++ .../frontend/src/locales/en/translations.json | 21 +++++ apps/frontend/src/router.tsx | 9 ++ 7 files changed, 291 insertions(+) create mode 100644 apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowingPositionsList/BorrowingPositionsList.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendingPositionsList/LendingPositionsList.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/WalletStatCard/WalletStatCard.tsx diff --git a/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx b/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx new file mode 100644 index 000000000..b8e681a63 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx @@ -0,0 +1,85 @@ +import React, { FC, useState } from 'react'; + +import classNames from 'classnames'; +import { t } from 'i18next'; +import { Helmet } from 'react-helmet-async'; + +import { Tabs, TabSize, TabType } from '@sovryn/ui'; + +import { useAccount } from '../../../hooks/useAccount'; +import { translations } from '../../../locales/i18n'; +import { BorrowingPositionsList } from './components/BorrowingPositionsList/BorrowingPositionsList'; +import { LendingPositionsList } from './components/LendingPositionsList/LendingPositionsList'; +import { TopPanel } from './components/TopPanel/TopPanel'; + +enum LiquidityTabs { + LEND = 0, + BORROW, +} + +const AavePage: FC = () => { + const { account } = useAccount(); + const [activeLiquidityTab, setActiveLiquidityTab] = useState( + LiquidityTabs.BORROW, + ); + + return ( +
+ + {t(translations.aavePage.meta.title)} + + + + +
+ {/* Tab selector */} +
+ setActiveLiquidityTab(e)} + size={TabSize.normal} + type={TabType.secondary} + /> +
+ + {/* Lending and borrowing columns */} +
+ {/* Lending column */} +
+ + {/* LendingAssetsList */} +
+ + {/* Borrowing column */} +
+ + + {/* BorrowAssetsList */} +
+
+
+
+ ); +}; +export default AavePage; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingPositionsList/BorrowingPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingPositionsList/BorrowingPositionsList.tsx new file mode 100644 index 000000000..4e6c2095a --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingPositionsList/BorrowingPositionsList.tsx @@ -0,0 +1,35 @@ +import React, { FC } from 'react'; + +import { t } from 'i18next'; + +import { Paragraph } from '@sovryn/ui'; + +import { translations } from '../../../../../locales/i18n'; + +const pageTranslations = translations.aavePage.borrowingPositionsList; + +type BorrowingPositionsListProps = { + account?: string; +}; + +export const BorrowingPositionsList: FC = ({ + account, +}) => { + return ( +
+
+ + {t(pageTranslations.title)} + +
+ + {account ? ( + <> + ) : ( + + {t(pageTranslations.connectWallet)} + + )} +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendingPositionsList/LendingPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendingPositionsList/LendingPositionsList.tsx new file mode 100644 index 000000000..ac3070896 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendingPositionsList/LendingPositionsList.tsx @@ -0,0 +1,35 @@ +import React, { FC } from 'react'; + +import { t } from 'i18next'; + +import { Paragraph } from '@sovryn/ui'; + +import { translations } from '../../../../../locales/i18n'; + +const pageTranslations = translations.aavePage.lendingPositionsList; + +type LendingPositionsListProps = { + account?: string; +}; + +export const LendingPositionsList: FC = ({ + account, +}) => { + return ( +
+
+ + {t(pageTranslations.title)} + +
+ + {account ? ( + <> + ) : ( + + {t(pageTranslations.connectWallet)} + + )} +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx new file mode 100644 index 000000000..677edfcd0 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx @@ -0,0 +1,67 @@ +import React, { FC } from 'react'; + +import { t } from 'i18next'; + +import { + Button, + ButtonSize, + ButtonStyle, + Heading, + Paragraph, + ParagraphSize, +} from '@sovryn/ui'; + +import { translations } from '../../../../../locales/i18n'; +import { WalletStatCard } from '../WalletStatCard/WalletStatCard'; + +const pageTranslations = translations.aavePage.topPanel; + +type TopPanelProps = { + account: string; +}; + +export const TopPanel: FC = ({ account }) => { + return ( +
+
+ + {t(pageTranslations.title)} + + + {t(pageTranslations.subtitle)} + +
+ +
+ +
+ + +
+
+ +
+
+
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/WalletStatCard/WalletStatCard.tsx b/apps/frontend/src/app/5_pages/AavePage/components/WalletStatCard/WalletStatCard.tsx new file mode 100644 index 000000000..89daed329 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/WalletStatCard/WalletStatCard.tsx @@ -0,0 +1,39 @@ +import React, { FC } from 'react'; + +import { Icon, Tooltip } from '@sovryn/ui'; + +type WalletStatCardProps = { + label: string; + value: string; + prefix?: string; + suffix?: string; + tooltipContent?: string; +}; + +export const WalletStatCard: FC = ({ + label, + value, + prefix, + suffix, + tooltipContent, +}) => { + return ( +
+
+ {label} + {tooltipContent && ( + {tooltipContent}}> +
+ +
+
+ )} +
+
+ {prefix && {prefix}} + {value} + {suffix && {suffix}} +
+
+ ); +}; diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index 0e4da0465..714516e4e 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -791,6 +791,27 @@ "collateralRatio": "collateral ratio" }, + "aavePage": { + "meta": { + "title": "Lend and borrow" + }, + "topPanel": { + "title": "Lend and borrow", + "subtitle": "Borrow and earn interest by providing liquidity", + "netWorth": "Net worth", + "netApy": "Net API", + "n/a": "N/A" + }, + "borrowingPositionsList": { + "title": "Your loans", + "connectWallet": "Connect wallet to see the available data" + }, + "lendingPositionsList": { + "title": "Your lending deposits", + "connectWallet": "Connect wallet to see the available data" + } + }, + "bitocracyPage": { "meta": { "title": "Bitocracy - Sovryn", diff --git a/apps/frontend/src/router.tsx b/apps/frontend/src/router.tsx index c8b20965d..af3fb5624 100644 --- a/apps/frontend/src/router.tsx +++ b/apps/frontend/src/router.tsx @@ -8,6 +8,7 @@ import { import { PageContainer } from './app/4_templates'; import { EmailVerificationStateContainer } from './app/4_templates/EmailVerificationStateContainer/EmailVerificationStateContainer'; +import AavePage from './app/5_pages/AavePage/AaavePage'; import { earnPageLoader } from './app/5_pages/EarnPage/loader'; import { EmailDuplicateVerifiedPage } from './app/5_pages/EmailDuplicateVerifiedPage/EmailDuplicateVerifiedPage'; import { EmailErrorPage } from './app/5_pages/EmailErrorPage/EmailErrorPage'; @@ -93,6 +94,10 @@ const routes = [ path: '/convert', element: , }, + { + path: '/borrow/aave', + element: , + }, { path: '/borrow/line-of-credit', element: , @@ -115,6 +120,10 @@ const routes = [ path: '/earn/lend', element: , }, + { + path: '/borrow/lend/aave', + element: , + }, { path: '/earn/market-making', element: , From 14aa7e5fd32386254be70b9a327d45f03013e82d Mon Sep 17 00:00:00 2001 From: matzapata Date: Thu, 8 Aug 2024 15:26:56 +0300 Subject: [PATCH 002/116] responsive panels --- .../AavePoolRowTitle/AavePoolRowTitle.tsx | 39 ++++++++++++ .../src/app/5_pages/AavePage/AaavePage.tsx | 60 +++++++++---------- .../BorrowingAssetsList.constants.tsx | 18 ++++++ .../BorrowingAssetsList.tsx | 50 ++++++++++++++++ .../BorrowingAssetsList.types.tsx | 3 + .../BorrowingPositionsList.tsx | 32 ++++++---- .../LendingAssetsList/LendingAssetsList.tsx | 33 ++++++++++ .../LendingPositionsList.tsx | 32 ++++++---- .../AavePage/components/TopPanel/TopPanel.tsx | 50 ++++++++-------- .../frontend/src/locales/en/translations.json | 53 +++++++--------- 10 files changed, 261 insertions(+), 109 deletions(-) create mode 100644 apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.constants.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendingAssetsList/LendingAssetsList.tsx diff --git a/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx b/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx new file mode 100644 index 000000000..9f07a50ca --- /dev/null +++ b/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx @@ -0,0 +1,39 @@ +import React, { FC } from 'react'; + +import { t } from 'i18next'; + +import { NextBorrowInterestRate } from '../../5_pages/BorrowPage/components/BorrowAssetsTable/components/NextBorrowInterestRate/NextBorrowInterestRate'; +import { NextSupplyInterestRate } from '../../5_pages/LendPage/components/NextSupplyInterestRate/NextSupplyInterestRate'; +import { translations } from '../../../locales/i18n'; +import { AssetRenderer } from '../AssetRenderer/AssetRenderer'; + +type AavePoolRowTitleProps = { + asset: string; + isBorrow?: boolean; +}; + +export const AavePoolRowTitle: FC = ({ + asset, + isBorrow = false, +}) => ( +
+ +
+ {isBorrow ? ( + + ) : ( + + )} + + {isBorrow + ? t(translations.fixedInterestPage.borrowAssetsTable.borrowAprMobile) + : t(translations.lendPage.table.lendAprMobile)} + +
+
+); diff --git a/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx b/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx index b8e681a63..5fe92b662 100644 --- a/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx @@ -8,7 +8,9 @@ import { Tabs, TabSize, TabType } from '@sovryn/ui'; import { useAccount } from '../../../hooks/useAccount'; import { translations } from '../../../locales/i18n'; +import { BorrowingAssetsList } from './components/BorrowingAssetsList/BorrowingAssetsList'; import { BorrowingPositionsList } from './components/BorrowingPositionsList/BorrowingPositionsList'; +import { LendingAssetsList } from './components/LendingAssetsList/LendingAssetsList'; import { LendingPositionsList } from './components/LendingPositionsList/LendingPositionsList'; import { TopPanel } from './components/TopPanel/TopPanel'; @@ -20,62 +22,60 @@ enum LiquidityTabs { const AavePage: FC = () => { const { account } = useAccount(); const [activeLiquidityTab, setActiveLiquidityTab] = useState( - LiquidityTabs.BORROW, + LiquidityTabs.LEND, ); return ( -
+
{t(translations.aavePage.meta.title)} -
+
{/* Tab selector */} -
- setActiveLiquidityTab(e)} - size={TabSize.normal} - type={TabType.secondary} - /> -
+ setActiveLiquidityTab(e)} + size={TabSize.normal} + type={TabType.secondary} + /> {/* Lending and borrowing columns */} -
- {/* Lending column */} +
- {/* LendingAssetsList */} +
{/* Borrowing column */}
- - {/* BorrowAssetsList */} +
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.constants.tsx new file mode 100644 index 000000000..43b255661 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.constants.tsx @@ -0,0 +1,18 @@ +export const COLUMNS_CONFIG = [ + { + id: 'apr', + title: 'APR', // TODO: transalations + // cellRenderer: (asset: AssetRowElement) => ( + // + // ), + }, + { + id: 'available', + title: 'Available', + }, +]; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.tsx new file mode 100644 index 000000000..2de296c5b --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.tsx @@ -0,0 +1,50 @@ +import React, { FC, useState } from 'react'; + +import { t } from 'i18next'; + +import { Accordion, Table } from '@sovryn/ui'; + +import { AavePoolRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; +import { translations } from '../../../../../locales/i18n'; +import { COLUMNS_CONFIG } from './BorrowingAssetsList.constants'; + +const pageTranslations = translations.aavePage.borrowingAssetsList; + +type BorrowingAssetsListProps = { + account?: string; +}; + +export const BorrowingAssetsList: FC = ({ + account, +}) => { + const [open, setOpen] = useState(true); + + return ( + + {t(pageTranslations.title)} + + } + className="bg-gray-70 px-4 py-3 rounded space-y-3 lg:bg-gray-90 lg:p-6" + labelClassName="justify-between h-7 flex items-center" + open={open} + onClick={setOpen} + > + } + rows={[ + { + asset: 'BTC', + apr: 2, + available: 12.34, + }, + ]} + /> + + ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx new file mode 100644 index 000000000..53eddd4dd --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx @@ -0,0 +1,3 @@ +export type AssetRowElement = { + asset: string; +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingPositionsList/BorrowingPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingPositionsList/BorrowingPositionsList.tsx index 4e6c2095a..f847c8dc7 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingPositionsList/BorrowingPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingPositionsList/BorrowingPositionsList.tsx @@ -1,8 +1,8 @@ -import React, { FC } from 'react'; +import React, { FC, useState } from 'react'; import { t } from 'i18next'; -import { Paragraph } from '@sovryn/ui'; +import { Accordion, Paragraph } from '@sovryn/ui'; import { translations } from '../../../../../locales/i18n'; @@ -15,21 +15,29 @@ type BorrowingPositionsListProps = { export const BorrowingPositionsList: FC = ({ account, }) => { + const [open, setOpen] = useState(true); + return ( -
-
- + {t(pageTranslations.title)} - -
- + + } + className="bg-gray-70 px-4 py-3 rounded space-y-3 lg:bg-gray-90 lg:p-6 lg:border lg:border-gray-60" + labelClassName="justify-between h-7 flex items-center" + open={open} + onClick={setOpen} + > {account ? ( <> ) : ( - - {t(pageTranslations.connectWallet)} - +
+ + {t(pageTranslations.connectWallet)} + +
)} -
+ ); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendingAssetsList/LendingAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendingAssetsList/LendingAssetsList.tsx new file mode 100644 index 000000000..18121ca0d --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendingAssetsList/LendingAssetsList.tsx @@ -0,0 +1,33 @@ +import React, { FC, useState } from 'react'; + +import { t } from 'i18next'; + +import { Accordion } from '@sovryn/ui'; + +import { translations } from '../../../../../locales/i18n'; + +const pageTranslations = translations.aavePage.lendingAssetsList; + +type LendingAssetsListProps = { + account?: string; +}; + +export const LendingAssetsList: FC = ({ account }) => { + const [open, setOpen] = useState(true); + + return ( + + {t(pageTranslations.title)} + + } + className="bg-gray-70 px-4 py-3 rounded space-y-3 lg:bg-gray-90 lg:p-6" + labelClassName="justify-between h-7 flex items-center" + open={open} + onClick={setOpen} + > +
Content
+
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendingPositionsList/LendingPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendingPositionsList/LendingPositionsList.tsx index ac3070896..1af604c1c 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendingPositionsList/LendingPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendingPositionsList/LendingPositionsList.tsx @@ -1,8 +1,8 @@ -import React, { FC } from 'react'; +import React, { FC, useState } from 'react'; import { t } from 'i18next'; -import { Paragraph } from '@sovryn/ui'; +import { Accordion, Paragraph } from '@sovryn/ui'; import { translations } from '../../../../../locales/i18n'; @@ -15,21 +15,29 @@ type LendingPositionsListProps = { export const LendingPositionsList: FC = ({ account, }) => { + const [open, setOpen] = useState(true); + return ( -
-
- + {t(pageTranslations.title)} - -
- + + } + className="bg-gray-70 px-4 py-3 rounded space-y-3 lg:bg-gray-90 lg:p-6 lg:border lg:border-gray-60" + labelClassName="justify-between h-7 flex items-center" + open={open} + onClick={setOpen} + > {account ? ( <> ) : ( - - {t(pageTranslations.connectWallet)} - +
+ + {t(pageTranslations.connectWallet)} + +
)} -
+ ); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx index 677edfcd0..0caa05a11 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx @@ -23,7 +23,7 @@ type TopPanelProps = { export const TopPanel: FC = ({ account }) => { return (
-
+
{t(pageTranslations.title)} @@ -32,35 +32,37 @@ export const TopPanel: FC = ({ account }) => {
-
- -
+
+
- +
+ + +
-
-
-
); diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index 714516e4e..72b43784e 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -185,7 +185,6 @@ "resetValues": "Reset values", "notEnoughBalance": "Estimated gas fee exceeds {{asset}} balance" }, - "zeroPage": { "meta": { "title": "Zero line of credit - Sovryn", @@ -271,17 +270,17 @@ "withdrawCollateral": "Withdraw collateral" }, "redeem": { - "title": "Redeem", - "cta": "Redeem", - "form": { - "description": "You can redeem your ZUSD for RBTC here. The corresponding RBTC is drawn from the lowest-collateralized loans, reducing the borrower's debt while you receive an equivalent value in RBTC.", - "link": "Learn more about Redemptions.", - "minimum": "Minimum {{value}} {{symbol}}", - "confirm": "Confirm" - }, - "tx": { - "title": "Redeem {{symbol}}" - } + "title": "Redeem", + "cta": "Redeem", + "form": { + "description": "You can redeem your ZUSD for RBTC here. The corresponding RBTC is drawn from the lowest-collateralized loans, reducing the borrower's debt while you receive an equivalent value in RBTC.", + "link": "Learn more about Redemptions.", + "minimum": "Minimum {{value}} {{symbol}}", + "confirm": "Confirm" + }, + "tx": { + "title": "Redeem {{symbol}}" + } } }, "stats": { @@ -385,7 +384,6 @@ "inputLabel": "Maximum origination fee rate", "errorMessage": "Value entered must be between {{min}} and {{max}}" }, - "earnPage": { "meta": { "title": "Stability pool - Sovryn", @@ -411,7 +409,6 @@ }, "apr": "APR" }, - "convertPage": { "meta": { "title": "Sovryn - DeFi for bitcoin", @@ -436,7 +433,6 @@ "minimumReceived": "Minimum received", "maximumPrice": "Maximum price" }, - "landingPage": { "meta": { "title": "Welcome to Sovryn", @@ -579,7 +575,6 @@ "dataComingSoon": "Data coming soon" } }, - "stakePage": { "meta": { "title": "Staking - Sovryn" @@ -674,7 +669,6 @@ "signUp": "Sign up" } }, - "fixedInterestPage": { "meta": { "title": "Fixed-interest loans - Sovryn", @@ -790,7 +784,6 @@ }, "collateralRatio": "collateral ratio" }, - "aavePage": { "meta": { "title": "Lend and borrow" @@ -806,12 +799,18 @@ "title": "Your loans", "connectWallet": "Connect wallet to see the available data" }, + "borrowingAssetsList": { + "title": "Assets to borrow" + }, "lendingPositionsList": { "title": "Your lending deposits", "connectWallet": "Connect wallet to see the available data" - } + }, + "lendingAssetsList": { + "title": "Assets to lend" + }, + "assetCard": {} }, - "bitocracyPage": { "meta": { "title": "Bitocracy - Sovryn", @@ -886,7 +885,6 @@ "accountBalance": "Account balance:" } }, - "proposalPage": { "meta": { "title": "Bitocracy - Sovryn" @@ -952,7 +950,6 @@ "calldata": "Calldata" } }, - "leaderboardPage": { "headerLink": "POWA Points", "headerDescription": "Earn Points, Earn POWA", @@ -1154,7 +1151,6 @@ } } }, - "fastBtc": { "mainScreen": { "title": "Funding", @@ -1238,7 +1234,6 @@ } } }, - "rewardPage": { "meta": { "title": "Rewards - Sovryn", @@ -1351,7 +1346,6 @@ } } }, - "historyPage": { "meta": { "title": "History - Sovryn", @@ -1399,7 +1393,6 @@ }, "apr": "APR" }, - "marketMakingPage": { "meta": { "title": "Market Making - Sovryn" @@ -1468,7 +1461,6 @@ "24hVolume": "24h volume (BTC)" } }, - "bobMarketMakingPage": { "depositModal": { "title": "Deposit liquidity", @@ -1525,7 +1517,6 @@ }, "loading": "Loading..." }, - "borrowHistory": { "types": { "lineOfCredit": "Line of credit", @@ -2057,7 +2048,7 @@ } }, "unclaimedVestings": { - "text": "You have {{value}} LM SOV rewards vested stakes that you need to withdraw before claiming more.", - "cta": "Open Rewards page" + "text": "You have {{value}} LM SOV rewards vested stakes that you need to withdraw before claiming more.", + "cta": "Open Rewards page" } -} +} \ No newline at end of file From c26c396b8688915c05af28e8471e8110d5e64fe5 Mon Sep 17 00:00:00 2001 From: matzapata Date: Thu, 8 Aug 2024 16:49:08 +0300 Subject: [PATCH 003/116] functional static borrow asset list --- .../AavePoolRowTitle/AavePoolRowTitle.tsx | 2 +- .../BorrowingAssetsList.constants.tsx | 49 ++++++++-- .../BorrowingAssetsList.tsx | 97 +++++++++++++++++-- .../BorrowingAssetsList.types.tsx | 2 + .../AavePage/components/TopPanel/TopPanel.tsx | 9 +- .../WalletStatCard/WalletStatCard.tsx | 14 +-- .../ui/src/2_molecules/Table/Table.types.ts | 1 + .../components/TableMobile/TableMobile.tsx | 2 + .../TableMobile/components/TableMobileRow.tsx | 5 +- 9 files changed, 149 insertions(+), 32 deletions(-) rename apps/frontend/src/app/5_pages/AavePage/components/{ => TopPanel/components}/WalletStatCard/WalletStatCard.tsx (65%) diff --git a/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx b/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx index 9f07a50ca..76d8d5b0a 100644 --- a/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx +++ b/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx @@ -16,7 +16,7 @@ export const AavePoolRowTitle: FC = ({ asset, isBorrow = false, }) => ( -
+
( - // - // ), + id: 'asset', + title: 'Asset', // TODO: transalations + cellRenderer: (asset: AssetRowElement) => ( + + ), + align: Align.center, + sortable: true, }, { id: 'available', title: 'Available', + sortable: true, + align: Align.center, + }, + { + id: 'apr', + title: 'APR', // TODO: transalations + sortable: true, + align: Align.center, + }, + { + id: 'actions', + align: Align.center, + title: ' ', + // TODO: create component here + cellRenderer: (asset: AssetRowElement) => ( +
+
+ ), }, ]; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.tsx index 2de296c5b..87c33a9ce 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.tsx @@ -2,11 +2,19 @@ import React, { FC, useState } from 'react'; import { t } from 'i18next'; -import { Accordion, Table } from '@sovryn/ui'; +import { + Accordion, + Align, + Button, + ButtonStyle, + HelperButton, + Table, +} from '@sovryn/ui'; import { AavePoolRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; +import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { translations } from '../../../../../locales/i18n'; -import { COLUMNS_CONFIG } from './BorrowingAssetsList.constants'; +import { AssetRowElement } from './BorrowingAssetsList.types'; const pageTranslations = translations.aavePage.borrowingAssetsList; @@ -32,10 +40,48 @@ export const BorrowingAssetsList: FC = ({ onClick={setOpen} >
( + + ), + align: Align.center, + sortable: true, + }, + { + id: 'available', + title: 'Available', + sortable: true, + align: Align.center, + }, + { + id: 'apr', + title: 'APR', // TODO: transalations + sortable: true, + align: Align.center, + }, + { + id: 'actions', + align: Align.center, + title: ' ', + // TODO: create component here + cellRenderer: (asset: AssetRowElement) => ( +
+
+ ), + }, + ]} + rowClassName="bg-gray-80" + accordionClassName="bg-gray-60 border border-gray-70" rowTitle={r => } rows={[ { @@ -43,7 +89,46 @@ export const BorrowingAssetsList: FC = ({ apr: 2, available: 12.34, }, + { + asset: 'ETH', + apr: 2, + available: 12.34, + }, ]} + mobileRenderer={r => ( +
+
+ {/* APR */} +
+
+ APR + +
+
+ {r.apr} +
+
+ + {/* Available */} +
+
+ + Available + +
+
+ {r.available} {r.asset} +
+
+
+ + {/* Actions TODO: same component as above */} +
+
+
+ )} /> ); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx index 53eddd4dd..d8ce64275 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx @@ -1,3 +1,5 @@ export type AssetRowElement = { asset: string; + apr: number; + available: number; }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx index 0caa05a11..7d4009968 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx @@ -12,7 +12,7 @@ import { } from '@sovryn/ui'; import { translations } from '../../../../../locales/i18n'; -import { WalletStatCard } from '../WalletStatCard/WalletStatCard'; +import { WalletStatCard } from './components/WalletStatCard/WalletStatCard'; const pageTranslations = translations.aavePage.topPanel; @@ -38,20 +38,21 @@ export const TopPanel: FC = ({ account }) => { label={t(pageTranslations.netWorth)} prefix="$" value="1,234,567.58" - tooltipContent="Net APY is the combined effect of all supply and borrow positions on net worth, including incentives. It is possible to have a negative net APY if debt APY is higher than supply APY." + // TODO: update content and translations + helperContent="Net APY is the combined effect of all supply and borrow positions on net worth, including incentives. It is possible to have a negative net APY if debt APY is higher than supply APY." />
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/WalletStatCard/WalletStatCard.tsx b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/components/WalletStatCard/WalletStatCard.tsx similarity index 65% rename from apps/frontend/src/app/5_pages/AavePage/components/WalletStatCard/WalletStatCard.tsx rename to apps/frontend/src/app/5_pages/AavePage/components/TopPanel/components/WalletStatCard/WalletStatCard.tsx index 89daed329..e262d2ee1 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/WalletStatCard/WalletStatCard.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/components/WalletStatCard/WalletStatCard.tsx @@ -1,13 +1,13 @@ import React, { FC } from 'react'; -import { Icon, Tooltip } from '@sovryn/ui'; +import { HelperButton } from '@sovryn/ui'; type WalletStatCardProps = { label: string; value: string; prefix?: string; suffix?: string; - tooltipContent?: string; + helperContent?: string; }; export const WalletStatCard: FC = ({ @@ -15,19 +15,13 @@ export const WalletStatCard: FC = ({ value, prefix, suffix, - tooltipContent, + helperContent, }) => { return (
{label} - {tooltipContent && ( - {tooltipContent}}> -
- -
-
- )} + {helperContent && }
{prefix && {prefix}} diff --git a/packages/ui/src/2_molecules/Table/Table.types.ts b/packages/ui/src/2_molecules/Table/Table.types.ts index 9853694e8..941438686 100644 --- a/packages/ui/src/2_molecules/Table/Table.types.ts +++ b/packages/ui/src/2_molecules/Table/Table.types.ts @@ -19,6 +19,7 @@ export type ColumnOptions = { export type TableProps = { className?: string; rowClassName?: string; + accordionClassName?: string; columns: ColumnOptions[]; rows?: RowType[]; rowComponent?: FC; diff --git a/packages/ui/src/2_molecules/Table/components/TableMobile/TableMobile.tsx b/packages/ui/src/2_molecules/Table/components/TableMobile/TableMobile.tsx index e8304432c..b31c02e0b 100644 --- a/packages/ui/src/2_molecules/Table/components/TableMobile/TableMobile.tsx +++ b/packages/ui/src/2_molecules/Table/components/TableMobile/TableMobile.tsx @@ -12,6 +12,7 @@ export const TableMobile = ({ columns, rows, rowComponent, + accordionClassName, rowKey, rowTitle, onRowClick, @@ -36,6 +37,7 @@ export const TableMobile = ({ titleRenderer={rowTitle} columns={columns} row={row} + accordionClassName={accordionClassName} index={index} onRowClick={onRowClick} dataAttribute={dataAttribute} diff --git a/packages/ui/src/2_molecules/Table/components/TableMobile/components/TableMobileRow.tsx b/packages/ui/src/2_molecules/Table/components/TableMobile/components/TableMobileRow.tsx index 5858116f1..0aa1bd0c5 100644 --- a/packages/ui/src/2_molecules/Table/components/TableMobile/components/TableMobileRow.tsx +++ b/packages/ui/src/2_molecules/Table/components/TableMobile/components/TableMobileRow.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useCallback, useMemo, useState } from 'react'; +import React, { ReactNode, useCallback, useMemo, useState } from 'react'; import { Accordion, AccordionStyle } from '../../../../../1_atoms'; import { noop } from '../../../../../utils'; @@ -9,6 +9,7 @@ import styles from './TableMobileRow.module.css'; type TableMobileRowProps = { columns: ColumnOptions[]; row: RowType; + accordionClassName?: string; onRowClick?: (row: RowType) => void; dataAttribute?: string; titleRenderer: @@ -24,6 +25,7 @@ type TableMobileRowProps = { export const TableMobileRow = ({ columns, row, + accordionClassName, onRowClick = noop, dataAttribute, titleRenderer, @@ -55,6 +57,7 @@ export const TableMobileRow = ({ dataAttribute={dataAttribute} style={AccordionStyle.secondary} flatMode={flatMode} + className={accordionClassName} >
{!renderer && From 1ba08384af2157f2d87f624845276c28bc530033 Mon Sep 17 00:00:00 2001 From: matzapata Date: Thu, 8 Aug 2024 17:51:39 +0300 Subject: [PATCH 004/116] cleanup --- .../BorrowingAssetsList.constants.tsx | 40 +++++--- .../BorrowingAssetsList.tsx | 91 ++----------------- .../BorrowingAssetsList.types.tsx | 3 +- .../BorrowAssetAction/BorrowAssetAction.tsx | 29 ++++++ .../BorrowAssetDetails/BorrowAssetDetails.tsx | 51 +++++++++++ .../frontend/src/locales/en/translations.json | 9 +- 6 files changed, 124 insertions(+), 99 deletions(-) create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.constants.tsx index f88d858c8..52001d650 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.constants.tsx @@ -1,19 +1,23 @@ import React from 'react'; -import { Align, Button, ButtonStyle } from '@sovryn/ui'; +import { t } from 'i18next'; + +import { Align, HelperButton } from '@sovryn/ui'; import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; -import { AssetRowElement } from './BorrowingAssetsList.types'; +import { translations } from '../../../../../locales/i18n'; +import { BorrowPoolAssetDetails } from './BorrowingAssetsList.types'; +import { BorrowAssetAction } from './components/BorrowAssetAction/BorrowAssetAction'; export const COLUMNS_CONFIG = [ { id: 'asset', - title: 'Asset', // TODO: transalations - cellRenderer: (asset: AssetRowElement) => ( + title: t(translations.aavePage.borrowingAssetsList.asset), + cellRenderer: (pool: BorrowPoolAssetDetails) => ( ), @@ -22,13 +26,27 @@ export const COLUMNS_CONFIG = [ }, { id: 'available', - title: 'Available', + title: ( + + {t(translations.aavePage.borrowingAssetsList.available)}{' '} + + + ), sortable: true, align: Align.center, }, { id: 'apr', - title: 'APR', // TODO: transalations + title: ( + + {t(translations.aavePage.borrowingAssetsList.apr)}{' '} + + + ), sortable: true, align: Align.center, }, @@ -36,12 +54,8 @@ export const COLUMNS_CONFIG = [ id: 'actions', align: Align.center, title: ' ', - // TODO: create component here - cellRenderer: (asset: AssetRowElement) => ( -
-
+ cellRenderer: (pool: BorrowPoolAssetDetails) => ( + ), }, ]; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.tsx index 87c33a9ce..f5a7f4d65 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.tsx @@ -2,19 +2,12 @@ import React, { FC, useState } from 'react'; import { t } from 'i18next'; -import { - Accordion, - Align, - Button, - ButtonStyle, - HelperButton, - Table, -} from '@sovryn/ui'; +import { Accordion, Table } from '@sovryn/ui'; import { AavePoolRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; -import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { translations } from '../../../../../locales/i18n'; -import { AssetRowElement } from './BorrowingAssetsList.types'; +import { COLUMNS_CONFIG } from './BorrowingAssetsList.constants'; +import { BorrowAssetDetails } from './components/BorrowAssetDetails/BorrowAssetDetails'; const pageTranslations = translations.aavePage.borrowingAssetsList; @@ -40,46 +33,7 @@ export const BorrowingAssetsList: FC = ({ onClick={setOpen} >
( - - ), - align: Align.center, - sortable: true, - }, - { - id: 'available', - title: 'Available', - sortable: true, - align: Align.center, - }, - { - id: 'apr', - title: 'APR', // TODO: transalations - sortable: true, - align: Align.center, - }, - { - id: 'actions', - align: Align.center, - title: ' ', - // TODO: create component here - cellRenderer: (asset: AssetRowElement) => ( -
-
- ), - }, - ]} + columns={COLUMNS_CONFIG} rowClassName="bg-gray-80" accordionClassName="bg-gray-60 border border-gray-70" rowTitle={r => } @@ -88,47 +42,16 @@ export const BorrowingAssetsList: FC = ({ asset: 'BTC', apr: 2, available: 12.34, + availableUsd: 100, }, { asset: 'ETH', apr: 2, available: 12.34, + availableUsd: 100, }, ]} - mobileRenderer={r => ( -
-
- {/* APR */} -
-
- APR - -
-
- {r.apr} -
-
- - {/* Available */} -
-
- - Available - -
-
- {r.available} {r.asset} -
-
-
- - {/* Actions TODO: same component as above */} -
-
-
- )} + mobileRenderer={p => } /> ); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx index d8ce64275..423a5519e 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx @@ -1,5 +1,6 @@ -export type AssetRowElement = { +export type BorrowPoolAssetDetails = { asset: string; apr: number; available: number; + availableUsd: number; }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx new file mode 100644 index 000000000..5b19cbdcd --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx @@ -0,0 +1,29 @@ +import React, { FC } from 'react'; + +import { t } from 'i18next'; + +import { Button, ButtonStyle } from '@sovryn/ui'; + +import { translations } from '../../../../../../../locales/i18n'; +import { BorrowPoolAssetDetails } from '../../BorrowingAssetsList.types'; + +type BorrowAssetActionProps = { + pool: BorrowPoolAssetDetails; +}; + +export const BorrowAssetAction: FC = () => { + return ( +
+ {/* TODO: these should be modal triggers */} +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx new file mode 100644 index 000000000..f06c5206b --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx @@ -0,0 +1,51 @@ +import React, { FC } from 'react'; + +import { t } from 'i18next'; + +import { HelperButton } from '@sovryn/ui'; + +import { translations } from '../../../../../../../locales/i18n'; +import { BorrowPoolAssetDetails } from '../../BorrowingAssetsList.types'; +import { BorrowAssetAction } from '../BorrowAssetAction/BorrowAssetAction'; + +type BorrowAssetDetailsProps = { + pool: BorrowPoolAssetDetails; +}; + +export const BorrowAssetDetails: FC = ({ pool }) => { + return ( +
+
+ {/* APR */} +
+
+ + {t(translations.aavePage.borrowingAssetsList.apr)} + + +
+
+ {pool.apr} +
+
+ + {/* Available */} +
+
+ + {t(translations.aavePage.borrowingAssetsList.available)} + +
+
+ {pool.available} {pool.asset} +
+
+
+ + +
+ ); +}; diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index 72b43784e..5b2b432da 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -800,7 +800,14 @@ "connectWallet": "Connect wallet to see the available data" }, "borrowingAssetsList": { - "title": "Assets to borrow" + "title": "Assets to borrow", + "asset": "Asset", + "apr": "APR", + "available": "Available", + "borrow": "Borrow", + "details": "Details", + "availableInfo": "TODO:", + "aprInfo": "APR is estimated based on previous fees earned at the defined price range, if no trades were made previously or you have recently claimed fees/withdrawn liquidity, you may see 0 value" }, "lendingPositionsList": { "title": "Your lending deposits", From c31694ba16bc7089979260ca4c2c5a97353dfd51 Mon Sep 17 00:00:00 2001 From: matzapata Date: Thu, 8 Aug 2024 18:05:36 +0300 Subject: [PATCH 005/116] renaming --- .../src/app/5_pages/AavePage/AaavePage.tsx | 16 +++--- .../BorrowAssetsList.constants.tsx} | 12 +++-- .../BorrowAssetsList.tsx} | 52 +++++++++---------- .../BorrowAssetsList.types.tsx} | 0 .../BorrowAssetAction/BorrowAssetAction.tsx | 2 +- .../BorrowAssetDetails/BorrowAssetDetails.tsx | 2 +- .../BorrowPositionsList.tsx} | 4 +- .../LendAssetsList.tsx} | 4 +- .../LendPositionsList.tsx} | 6 +-- 9 files changed, 50 insertions(+), 48 deletions(-) rename apps/frontend/src/app/5_pages/AavePage/components/{BorrowingAssetsList/BorrowingAssetsList.constants.tsx => BorrowAssetsList/BorrowAssetsList.constants.tsx} (81%) rename apps/frontend/src/app/5_pages/AavePage/components/{BorrowingAssetsList/BorrowingAssetsList.tsx => BorrowAssetsList/BorrowAssetsList.tsx} (51%) rename apps/frontend/src/app/5_pages/AavePage/components/{BorrowingAssetsList/BorrowingAssetsList.types.tsx => BorrowAssetsList/BorrowAssetsList.types.tsx} (100%) rename apps/frontend/src/app/5_pages/AavePage/components/{BorrowingAssetsList => BorrowAssetsList}/components/BorrowAssetAction/BorrowAssetAction.tsx (91%) rename apps/frontend/src/app/5_pages/AavePage/components/{BorrowingAssetsList => BorrowAssetsList}/components/BorrowAssetDetails/BorrowAssetDetails.tsx (95%) rename apps/frontend/src/app/5_pages/AavePage/components/{BorrowingPositionsList/BorrowingPositionsList.tsx => BorrowPositionsList/BorrowPositionsList.tsx} (90%) rename apps/frontend/src/app/5_pages/AavePage/components/{LendingAssetsList/LendingAssetsList.tsx => LendAssetsList/LendAssetsList.tsx} (86%) rename apps/frontend/src/app/5_pages/AavePage/components/{LendingPositionsList/LendingPositionsList.tsx => LendPositionsList/LendPositionsList.tsx} (89%) diff --git a/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx b/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx index 5fe92b662..bcc087065 100644 --- a/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx @@ -8,10 +8,10 @@ import { Tabs, TabSize, TabType } from '@sovryn/ui'; import { useAccount } from '../../../hooks/useAccount'; import { translations } from '../../../locales/i18n'; -import { BorrowingAssetsList } from './components/BorrowingAssetsList/BorrowingAssetsList'; -import { BorrowingPositionsList } from './components/BorrowingPositionsList/BorrowingPositionsList'; -import { LendingAssetsList } from './components/LendingAssetsList/LendingAssetsList'; -import { LendingPositionsList } from './components/LendingPositionsList/LendingPositionsList'; +import { BorrowAssetsList } from './components/BorrowAssetsList/BorrowAssetsList'; +import { BorrowPositionsList } from './components/BorrowPositionsList/BorrowPositionsList'; +import { LendAssetsList } from './components/LendAssetsList/LendAssetsList'; +import { LendPositionsList } from './components/LendPositionsList/LendPositionsList'; import { TopPanel } from './components/TopPanel/TopPanel'; enum LiquidityTabs { @@ -63,8 +63,8 @@ const AavePage: FC = () => { 'lg:block space-y-4', )} > - - + + {/* Borrowing column */} @@ -74,8 +74,8 @@ const AavePage: FC = () => { 'lg:block space-y-4', )} > - - + + diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx similarity index 81% rename from apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.constants.tsx rename to apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx index 52001d650..101497eea 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx @@ -6,13 +6,17 @@ import { Align, HelperButton } from '@sovryn/ui'; import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { translations } from '../../../../../locales/i18n'; -import { BorrowPoolAssetDetails } from './BorrowingAssetsList.types'; +import { BorrowPoolAssetDetails } from './BorrowAssetsList.types'; import { BorrowAssetAction } from './components/BorrowAssetAction/BorrowAssetAction'; export const COLUMNS_CONFIG = [ { id: 'asset', - title: t(translations.aavePage.borrowingAssetsList.asset), + title: ( + + {t(translations.aavePage.borrowingAssetsList.asset)} + + ), cellRenderer: (pool: BorrowPoolAssetDetails) => ( + {t(translations.aavePage.borrowingAssetsList.available)}{' '} + {t(translations.aavePage.borrowingAssetsList.apr)}{' '} = ({ - account, -}) => { +export const BorrowAssetsList: FC = ({ account }) => { const [open, setOpen] = useState(true); return ( @@ -32,27 +30,29 @@ export const BorrowingAssetsList: FC = ({ open={open} onClick={setOpen} > -
} - rows={[ - { - asset: 'BTC', - apr: 2, - available: 12.34, - availableUsd: 100, - }, - { - asset: 'ETH', - apr: 2, - available: 12.34, - availableUsd: 100, - }, - ]} - mobileRenderer={p => } - /> +
+
} + rows={[ + { + asset: 'BTC', + apr: 2, + available: 12.34, + availableUsd: 100, + }, + { + asset: 'ETH', + apr: 2, + available: 12.34, + availableUsd: 100, + }, + ]} + mobileRenderer={p => } + /> + ); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.types.tsx similarity index 100% rename from apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/BorrowingAssetsList.types.tsx rename to apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.types.tsx diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx similarity index 91% rename from apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx rename to apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx index 5b19cbdcd..6d2c0ef55 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx @@ -5,7 +5,7 @@ import { t } from 'i18next'; import { Button, ButtonStyle } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; -import { BorrowPoolAssetDetails } from '../../BorrowingAssetsList.types'; +import { BorrowPoolAssetDetails } from '../../BorrowAssetsList.types'; type BorrowAssetActionProps = { pool: BorrowPoolAssetDetails; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx similarity index 95% rename from apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx rename to apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx index f06c5206b..bd993b76e 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx @@ -5,7 +5,7 @@ import { t } from 'i18next'; import { HelperButton } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; -import { BorrowPoolAssetDetails } from '../../BorrowingAssetsList.types'; +import { BorrowPoolAssetDetails } from '../../BorrowAssetsList.types'; import { BorrowAssetAction } from '../BorrowAssetAction/BorrowAssetAction'; type BorrowAssetDetailsProps = { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingPositionsList/BorrowingPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx similarity index 90% rename from apps/frontend/src/app/5_pages/AavePage/components/BorrowingPositionsList/BorrowingPositionsList.tsx rename to apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index f847c8dc7..b2195726f 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowingPositionsList/BorrowingPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -8,11 +8,11 @@ import { translations } from '../../../../../locales/i18n'; const pageTranslations = translations.aavePage.borrowingPositionsList; -type BorrowingPositionsListProps = { +type BorrowPositionsListProps = { account?: string; }; -export const BorrowingPositionsList: FC = ({ +export const BorrowPositionsList: FC = ({ account, }) => { const [open, setOpen] = useState(true); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendingAssetsList/LendingAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx similarity index 86% rename from apps/frontend/src/app/5_pages/AavePage/components/LendingAssetsList/LendingAssetsList.tsx rename to apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx index 18121ca0d..ba87968d8 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendingAssetsList/LendingAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx @@ -8,11 +8,11 @@ import { translations } from '../../../../../locales/i18n'; const pageTranslations = translations.aavePage.lendingAssetsList; -type LendingAssetsListProps = { +type LendAssetsListProps = { account?: string; }; -export const LendingAssetsList: FC = ({ account }) => { +export const LendAssetsList: FC = ({ account }) => { const [open, setOpen] = useState(true); return ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendingPositionsList/LendingPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx similarity index 89% rename from apps/frontend/src/app/5_pages/AavePage/components/LendingPositionsList/LendingPositionsList.tsx rename to apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx index 1af604c1c..d065b438e 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendingPositionsList/LendingPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx @@ -8,13 +8,11 @@ import { translations } from '../../../../../locales/i18n'; const pageTranslations = translations.aavePage.lendingPositionsList; -type LendingPositionsListProps = { +type LendPositionsListProps = { account?: string; }; -export const LendingPositionsList: FC = ({ - account, -}) => { +export const LendPositionsList: FC = ({ account }) => { const [open, setOpen] = useState(true); return ( From 9678cebbf944588868b8302236b4dd3bf7a3cb7b Mon Sep 17 00:00:00 2001 From: matzapata Date: Thu, 8 Aug 2024 19:09:30 +0300 Subject: [PATCH 006/116] lend assets list --- .../BorrowAssetsList.constants.tsx | 10 +-- .../BorrowAssetsList/BorrowAssetsList.tsx | 2 +- .../BorrowAssetAction/BorrowAssetAction.tsx | 9 +-- .../BorrowAssetDetails/BorrowAssetDetails.tsx | 8 +- .../BorrowPositionsList.tsx | 2 +- .../LendAssetsList.constants.tsx | 79 +++++++++++++++++++ .../LendAssetsList/LendAssetsList.tsx | 33 +++++++- .../LendAssetsList/LendAssetsList.types.tsx | 6 ++ .../LendAssetAction/LendAssetAction.tsx | 29 +++++++ .../LendAssetDetails/LendAssetDetails.tsx | 67 ++++++++++++++++ .../LendPositionsList/LendPositionsList.tsx | 2 +- .../frontend/src/locales/en/translations.json | 17 ++-- 12 files changed, 240 insertions(+), 24 deletions(-) create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.types.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx index 101497eea..050b2b2da 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx @@ -14,7 +14,7 @@ export const COLUMNS_CONFIG = [ id: 'asset', title: ( - {t(translations.aavePage.borrowingAssetsList.asset)} + {t(translations.aavePage.borrowAssetsList.asset)} ), cellRenderer: (pool: BorrowPoolAssetDetails) => ( @@ -32,9 +32,9 @@ export const COLUMNS_CONFIG = [ id: 'available', title: ( - {t(translations.aavePage.borrowingAssetsList.available)}{' '} + {t(translations.aavePage.borrowAssetsList.available)}{' '} ), @@ -45,9 +45,9 @@ export const COLUMNS_CONFIG = [ id: 'apr', title: ( - {t(translations.aavePage.borrowingAssetsList.apr)}{' '} + {t(translations.aavePage.borrowAssetsList.apr)}{' '} ), diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx index f92823df7..a88b3bfa0 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx @@ -9,7 +9,7 @@ import { translations } from '../../../../../locales/i18n'; import { COLUMNS_CONFIG } from './BorrowAssetsList.constants'; import { BorrowAssetDetails } from './components/BorrowAssetDetails/BorrowAssetDetails'; -const pageTranslations = translations.aavePage.borrowingAssetsList; +const pageTranslations = translations.aavePage.borrowAssetsList; type BorrowAssetsListProps = { account?: string; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx index 6d2c0ef55..c87455507 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx @@ -7,6 +7,8 @@ import { Button, ButtonStyle } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; import { BorrowPoolAssetDetails } from '../../BorrowAssetsList.types'; +const pageTranslations = translations.aavePage.borrowAssetsList; + type BorrowAssetActionProps = { pool: BorrowPoolAssetDetails; }; @@ -15,13 +17,10 @@ export const BorrowAssetAction: FC = () => { return (
{/* TODO: these should be modal triggers */} +
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx index bd993b76e..a210fc808 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx @@ -8,6 +8,8 @@ import { translations } from '../../../../../../../locales/i18n'; import { BorrowPoolAssetDetails } from '../../BorrowAssetsList.types'; import { BorrowAssetAction } from '../BorrowAssetAction/BorrowAssetAction'; +const pageTranslations = translations.aavePage.borrowAssetsList; + type BorrowAssetDetailsProps = { pool: BorrowPoolAssetDetails; }; @@ -20,10 +22,10 @@ export const BorrowAssetDetails: FC = ({ pool }) => {
- {t(translations.aavePage.borrowingAssetsList.apr)} + {t(pageTranslations.apr)}
@@ -36,7 +38,7 @@ export const BorrowAssetDetails: FC = ({ pool }) => {
- {t(translations.aavePage.borrowingAssetsList.available)} + {t(pageTranslations.available)}
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index b2195726f..2a9531451 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -6,7 +6,7 @@ import { Accordion, Paragraph } from '@sovryn/ui'; import { translations } from '../../../../../locales/i18n'; -const pageTranslations = translations.aavePage.borrowingPositionsList; +const pageTranslations = translations.aavePage.borrowPositionsList; type BorrowPositionsListProps = { account?: string; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx new file mode 100644 index 000000000..3fbeab6b3 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx @@ -0,0 +1,79 @@ +import React from 'react'; + +import { t } from 'i18next'; + +import { Align, HelperButton, Icon } from '@sovryn/ui'; + +import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; +import { translations } from '../../../../../locales/i18n'; +import { LendPoolAssetDetails } from './LendAssetsList.types'; +import { LendAssetAction } from './components/LendAssetAction/LendAssetAction'; + +export const COLUMNS_CONFIG = [ + { + id: 'asset', + title: ( + + {t(translations.aavePage.lendAssetsList.asset)} + + ), + cellRenderer: (pool: LendPoolAssetDetails) => ( + + ), + align: Align.center, + sortable: true, + }, + { + id: 'walletBalance', + title: ( + + {t(translations.aavePage.lendAssetsList.walletBalance)}{' '} + + ), + sortable: true, + align: Align.center, + }, + { + id: 'apy', + title: ( + + {t(translations.aavePage.lendAssetsList.apy)}{' '} + + + ), + sortable: true, + align: Align.center, + }, + { + id: 'canBeCollateral', + title: ( + + {t(translations.aavePage.lendAssetsList.walletBalance)}{' '} + + ), + sortable: true, + align: Align.center, + cellRenderer: (pool: LendPoolAssetDetails) => ( +
+ {pool.canBeCollateral && ( + + )} +
+ ), + }, + { + id: 'actions', + align: Align.center, + title: ' ', + cellRenderer: (pool: LendPoolAssetDetails) => ( + + ), + }, +]; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx index ba87968d8..1eee75dce 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx @@ -2,11 +2,14 @@ import React, { FC, useState } from 'react'; import { t } from 'i18next'; -import { Accordion } from '@sovryn/ui'; +import { Accordion, Checkbox, Table } from '@sovryn/ui'; +import { AavePoolRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { translations } from '../../../../../locales/i18n'; +import { COLUMNS_CONFIG } from './LendAssetsList.constants'; +import { LendAssetDetails } from './components/LendAssetDetails/LendAssetDetails'; -const pageTranslations = translations.aavePage.lendingAssetsList; +const pageTranslations = translations.aavePage.lendAssetsList; type LendAssetsListProps = { account?: string; @@ -27,7 +30,31 @@ export const LendAssetsList: FC = ({ account }) => { open={open} onClick={setOpen} > -
Content
+
+ +
+ +
} + rows={[ + { + asset: 'BTC', + apy: 2, + walletBalance: 12.34, + canBeCollateral: true, + }, + { + asset: 'ETH', + apy: 2, + walletBalance: 12.34, + canBeCollateral: false, + }, + ]} + mobileRenderer={p => } + /> ); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.types.tsx new file mode 100644 index 000000000..d6ddd4fef --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.types.tsx @@ -0,0 +1,6 @@ +export type LendPoolAssetDetails = { + asset: string; + walletBalance: number; + apy: number; + canBeCollateral: boolean; +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx new file mode 100644 index 000000000..84eebc7c8 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx @@ -0,0 +1,29 @@ +import React, { FC } from 'react'; + +import { t } from 'i18next'; + +import { Button, ButtonStyle } from '@sovryn/ui'; + +import { translations } from '../../../../../../../locales/i18n'; +import { LendPoolAssetDetails } from '../../LendAssetsList.types'; + +type LendAssetActionProps = { + pool: LendPoolAssetDetails; +}; + +export const LendAssetAction: FC = () => { + return ( +
+ {/* TODO: these should be modal triggers */} +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx new file mode 100644 index 000000000..cc44c8f5d --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx @@ -0,0 +1,67 @@ +import React, { FC } from 'react'; + +import { t } from 'i18next'; + +import { HelperButton, Icon } from '@sovryn/ui'; + +import { translations } from '../../../../../../../locales/i18n'; +import { LendPoolAssetDetails } from '../../LendAssetsList.types'; +import { LendAssetAction } from '../LendAssetAction/LendAssetAction'; + +type LendAssetDetailsProps = { + pool: LendPoolAssetDetails; +}; + +export const LendAssetDetails: FC = ({ pool }) => { + return ( +
+
+ {/* Available */} +
+
+ + {t(translations.aavePage.lendAssetsList.walletBalance)} + +
+ + {/* TODO: review amount renderer component */} +
+ {pool.walletBalance} {pool.asset} +
+
+ + {/* APR */} +
+
+ + {t(translations.aavePage.lendAssetsList.apy)} + + +
+
+ {pool.apy} +
+
+ + {/* Can be collateral */} +
+
+ + {t(translations.aavePage.lendAssetsList.canBeCollateral)} + +
+
+ {pool.canBeCollateral ? ( + + ) : null} +
+
+
+ + +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx index d065b438e..119a53afb 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx @@ -6,7 +6,7 @@ import { Accordion, Paragraph } from '@sovryn/ui'; import { translations } from '../../../../../locales/i18n'; -const pageTranslations = translations.aavePage.lendingPositionsList; +const pageTranslations = translations.aavePage.lendPositionsList; type LendPositionsListProps = { account?: string; diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index 5b2b432da..da354cfe5 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -795,11 +795,11 @@ "netApy": "Net API", "n/a": "N/A" }, - "borrowingPositionsList": { + "borrowPositionsList": { "title": "Your loans", "connectWallet": "Connect wallet to see the available data" }, - "borrowingAssetsList": { + "borrowAssetsList": { "title": "Assets to borrow", "asset": "Asset", "apr": "APR", @@ -809,12 +809,19 @@ "availableInfo": "TODO:", "aprInfo": "APR is estimated based on previous fees earned at the defined price range, if no trades were made previously or you have recently claimed fees/withdrawn liquidity, you may see 0 value" }, - "lendingPositionsList": { + "lendPositionsList": { "title": "Your lending deposits", "connectWallet": "Connect wallet to see the available data" }, - "lendingAssetsList": { - "title": "Assets to lend" + "lendAssetsList": { + "title": "Assets to lend", + "asset": "Asset", + "apy": "APY", + "apyInfo": "TODO:", + "walletBalance": "Wallet balance", + "lend": "Lend", + "details": "Details", + "canBeCollateral": "Can be collateral" }, "assetCard": {} }, From c67474ffe81555e5269cd6d9e12e5b07d1e7bac8 Mon Sep 17 00:00:00 2001 From: matzapata Date: Thu, 8 Aug 2024 20:55:27 +0300 Subject: [PATCH 007/116] Lend positions list --- .../AmountRenderer/AmountRenderer.tsx | 44 +++++------ .../BorrowAssetsList.constants.tsx | 8 +- .../BorrowAssetAction/BorrowAssetAction.tsx | 6 +- .../BorrowAssetDetails/BorrowAssetDetails.tsx | 8 +- .../BorrowPositionsList.tsx | 6 +- .../LendAssetsList.constants.tsx | 14 ++-- .../LendAssetAction/LendAssetAction.tsx | 4 +- .../LendAssetDetails/LendAssetDetails.tsx | 4 +- .../LendPositionsList.constants.tsx | 74 +++++++++++++++++++ .../LendPositionsList/LendPositionsList.tsx | 64 +++++++++++++--- .../LendPositionsList.types.tsx | 6 ++ .../LendPositionAction/LendPositionAction.tsx | 25 +++++++ .../LendPositionDetails.tsx | 65 ++++++++++++++++ .../LendPositionStat/LendPositionStat.tsx | 34 +++++++++ .../frontend/src/locales/en/translations.json | 29 +++++--- 15 files changed, 320 insertions(+), 71 deletions(-) create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.types.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionStat/LendPositionStat.tsx diff --git a/apps/frontend/src/app/2_molecules/AmountRenderer/AmountRenderer.tsx b/apps/frontend/src/app/2_molecules/AmountRenderer/AmountRenderer.tsx index 6350188fd..0994b2fcd 100644 --- a/apps/frontend/src/app/2_molecules/AmountRenderer/AmountRenderer.tsx +++ b/apps/frontend/src/app/2_molecules/AmountRenderer/AmountRenderer.tsx @@ -29,7 +29,7 @@ import { const { decimal, thousand } = getLocaleSeparators(); -type AmountRendererProps = { +export type AmountRendererProps = { value: Decimalish; precision?: number; className?: string; @@ -142,33 +142,29 @@ export const AmountRenderer: FC = ({ } - className={classNames({ - 'cursor-pointer': shouldShowTooltip, - })} + className={classNames({ 'cursor-pointer': shouldShowTooltip }, className)} disabled={!shouldShowTooltip} trigger={trigger} dataAttribute={dataAttribute} > - - {isAnimated ? ( - - ) : ( - <> - {shouldShowRoundingPrefix ? '~ ' : ''} - {prefix} - {localeFormattedValue} {suffix} - - )} - + {isAnimated ? ( + + ) : ( + + {shouldShowRoundingPrefix ? '~ ' : ''} + {prefix} + {localeFormattedValue} {suffix} + + )} ); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx index 050b2b2da..e50ce1b08 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx @@ -14,7 +14,7 @@ export const COLUMNS_CONFIG = [ id: 'asset', title: ( - {t(translations.aavePage.borrowAssetsList.asset)} + {t(translations.aavePage.common.asset)} ), cellRenderer: (pool: BorrowPoolAssetDetails) => ( @@ -45,10 +45,8 @@ export const COLUMNS_CONFIG = [ id: 'apr', title: ( - {t(translations.aavePage.borrowAssetsList.apr)}{' '} - + {t(translations.aavePage.common.apr)}{' '} + ), sortable: true, diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx index c87455507..6ce196f3f 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx @@ -7,7 +7,7 @@ import { Button, ButtonStyle } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; import { BorrowPoolAssetDetails } from '../../BorrowAssetsList.types'; -const pageTranslations = translations.aavePage.borrowAssetsList; +const pageTranslations = translations.aavePage; type BorrowAssetActionProps = { pool: BorrowPoolAssetDetails; @@ -17,10 +17,10 @@ export const BorrowAssetAction: FC = () => { return (
{/* TODO: these should be modal triggers */} -
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx index a210fc808..3ebb8311d 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx @@ -8,7 +8,7 @@ import { translations } from '../../../../../../../locales/i18n'; import { BorrowPoolAssetDetails } from '../../BorrowAssetsList.types'; import { BorrowAssetAction } from '../BorrowAssetAction/BorrowAssetAction'; -const pageTranslations = translations.aavePage.borrowAssetsList; +const pageTranslations = translations.aavePage; type BorrowAssetDetailsProps = { pool: BorrowPoolAssetDetails; @@ -22,10 +22,10 @@ export const BorrowAssetDetails: FC = ({ pool }) => {
- {t(pageTranslations.apr)} + {t(pageTranslations.common.apr)}
@@ -38,7 +38,7 @@ export const BorrowAssetDetails: FC = ({ pool }) => {
- {t(pageTranslations.available)} + {t(pageTranslations.borrowAssetsList.available)}
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index 2a9531451..fab3cd9c7 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -6,7 +6,7 @@ import { Accordion, Paragraph } from '@sovryn/ui'; import { translations } from '../../../../../locales/i18n'; -const pageTranslations = translations.aavePage.borrowPositionsList; +const pageTranslations = translations.aavePage; type BorrowPositionsListProps = { account?: string; @@ -21,7 +21,7 @@ export const BorrowPositionsList: FC = ({ - {t(pageTranslations.title)} + {t(pageTranslations.borrowPositionsList.title)} } className="bg-gray-70 px-4 py-3 rounded space-y-3 lg:bg-gray-90 lg:p-6 lg:border lg:border-gray-60" @@ -34,7 +34,7 @@ export const BorrowPositionsList: FC = ({ ) : (
- {t(pageTranslations.connectWallet)} + {t(pageTranslations.common.connectWallet)}
)} diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx index 3fbeab6b3..f5c1c17bb 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx @@ -9,12 +9,14 @@ import { translations } from '../../../../../locales/i18n'; import { LendPoolAssetDetails } from './LendAssetsList.types'; import { LendAssetAction } from './components/LendAssetAction/LendAssetAction'; +const pageTranslations = translations.aavePage; + export const COLUMNS_CONFIG = [ { id: 'asset', title: ( - {t(translations.aavePage.lendAssetsList.asset)} + {t(pageTranslations.lendAssetsList.asset)} ), cellRenderer: (pool: LendPoolAssetDetails) => ( @@ -32,7 +34,7 @@ export const COLUMNS_CONFIG = [ id: 'walletBalance', title: ( - {t(translations.aavePage.lendAssetsList.walletBalance)}{' '} + {t(pageTranslations.lendAssetsList.walletBalance)}{' '} ), sortable: true, @@ -42,10 +44,8 @@ export const COLUMNS_CONFIG = [ id: 'apy', title: ( - {t(translations.aavePage.lendAssetsList.apy)}{' '} - + {t(pageTranslations.lendAssetsList.apy)}{' '} + ), sortable: true, @@ -55,7 +55,7 @@ export const COLUMNS_CONFIG = [ id: 'canBeCollateral', title: ( - {t(translations.aavePage.lendAssetsList.walletBalance)}{' '} + {t(pageTranslations.lendAssetsList.walletBalance)}{' '} ), sortable: true, diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx index 84eebc7c8..640f03df1 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx @@ -17,11 +17,11 @@ export const LendAssetAction: FC = () => { {/* TODO: these should be modal triggers */}
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx index cc44c8f5d..5b0267196 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx @@ -34,10 +34,10 @@ export const LendAssetDetails: FC = ({ pool }) => {
- {t(translations.aavePage.lendAssetsList.apy)} + {t(translations.aavePage.common.apy)}
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx new file mode 100644 index 000000000..5ba81e563 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx @@ -0,0 +1,74 @@ +import React from 'react'; + +import { t } from 'i18next'; + +import { Align, HelperButton, Toggle } from '@sovryn/ui'; + +import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; +import { translations } from '../../../../../locales/i18n'; +import { LendPosition } from './LendPositionsList.types'; +import { LendPositionAction } from './components/LendPositionAction/LendPositionAction'; + +const pageTranslations = translations.aavePage; + +export const COLUMNS_CONFIG = [ + { + id: 'asset', + title: ( + {t(pageTranslations.common.asset)} + ), + cellRenderer: (pool: LendPosition) => ( + + ), + align: Align.center, + sortable: true, + }, + { + id: 'balance', + title: ( + + {t(pageTranslations.lendAssetsList.walletBalance)}{' '} + + ), + sortable: true, + align: Align.center, + }, + { + id: 'apy', + title: ( + + {t(translations.aavePage.common.apy)}{' '} + + + ), + sortable: true, + align: Align.center, + }, + { + id: 'collateral', + title: ( + + {t(pageTranslations.common.collateral)}{' '} + + ), + sortable: true, + align: Align.center, + cellRenderer: (pool: LendPosition) => ( +
+ {/* TODO: This should actually be a switch */} + 'TODO:'} /> +
+ ), + }, + { + id: 'actions', + align: Align.center, + title: ' ', + cellRenderer: (pool: LendPosition) => , + }, +]; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx index 119a53afb..0327fcb43 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx @@ -2,24 +2,28 @@ import React, { FC, useState } from 'react'; import { t } from 'i18next'; -import { Accordion, Paragraph } from '@sovryn/ui'; +import { Accordion, Paragraph, Table } from '@sovryn/ui'; +import { AavePoolRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; +import { useAccount } from '../../../../../hooks/useAccount'; import { translations } from '../../../../../locales/i18n'; +import { COLUMNS_CONFIG } from './LendPositionsList.constants'; +import { LendPositionDetails } from './components/LendPositionDetails/LendPositionDetails'; +import { LendPositionStat } from './components/LendPositionStat/LendPositionStat'; -const pageTranslations = translations.aavePage.lendPositionsList; +const pageTranslations = translations.aavePage; -type LendPositionsListProps = { - account?: string; -}; +type LendPositionsListProps = {}; -export const LendPositionsList: FC = ({ account }) => { +export const LendPositionsList: FC = () => { + const { account } = useAccount(); const [open, setOpen] = useState(true); return ( - {t(pageTranslations.title)} + {t(pageTranslations.lendPositionsList.title)} } className="bg-gray-70 px-4 py-3 rounded space-y-3 lg:bg-gray-90 lg:p-6 lg:border lg:border-gray-60" @@ -28,11 +32,53 @@ export const LendPositionsList: FC = ({ account }) => { onClick={setOpen} > {account ? ( - <> + <> +
+ + + +
+
} + rows={[ + { + asset: 'BTC', + apy: 2, + balance: 12.34, + collateral: true, + }, + { + asset: 'ETH', + apy: 2, + balance: 12.34, + collateral: false, + }, + ]} + mobileRenderer={p => } + /> + ) : (
- {t(pageTranslations.connectWallet)} + {t(pageTranslations.common.connectWallet)}
)} diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.types.tsx new file mode 100644 index 000000000..9a43a4bfb --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.types.tsx @@ -0,0 +1,6 @@ +export type LendPosition = { + asset: string; + balance: number; + apy: number; + collateral: boolean; +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx new file mode 100644 index 000000000..03a95fb05 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx @@ -0,0 +1,25 @@ +import React, { FC } from 'react'; + +import { t } from 'i18next'; + +import { Button, ButtonStyle } from '@sovryn/ui'; + +import { translations } from '../../../../../../../locales/i18n'; +import { LendPosition } from '../../LendPositionsList.types'; + +type LendPositionActionProps = { + pool: LendPosition; +}; + +export const LendPositionAction: FC = () => { + return ( +
+ {/* TODO: these should be modal triggers */} +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx new file mode 100644 index 000000000..7ba96fb60 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx @@ -0,0 +1,65 @@ +import React, { FC } from 'react'; + +import { t } from 'i18next'; + +import { HelperButton, Toggle } from '@sovryn/ui'; + +import { translations } from '../../../../../../../locales/i18n'; +import { LendPosition } from '../../LendPositionsList.types'; +import { LendPositionAction } from '../LendPositionAction/LendPositionAction'; + +type LendPositionDetailsProps = { + pool: LendPosition; +}; + +export const LendPositionDetails: FC = ({ pool }) => { + return ( +
+
+ {/* Available */} +
+
+ + {t(translations.aavePage.common.balance)} + +
+ + {/* TODO: review amount renderer component */} +
+ {pool.balance} {pool.asset} +
+
+ + {/* APR */} +
+
+ + {t(translations.aavePage.lendAssetsList.apy)} + + +
+
+ {pool.apy} +
+
+ + {/* Can be collateral */} +
+
+ + {t(translations.aavePage.lendAssetsList.canBeCollateral)} + +
+
+ 'TODO:'} /> +
+
+
+ + +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionStat/LendPositionStat.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionStat/LendPositionStat.tsx new file mode 100644 index 000000000..eab9d1944 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionStat/LendPositionStat.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +import classNames from 'classnames'; + +import { + AmountRenderer, + AmountRendererProps, +} from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; + +type LendPositionStatProps = AmountRendererProps & { + label: string; + labelInfo?: string; +}; + +export const LendPositionStat: React.FC = ({ + label, + className, + ...props +}) => { + return ( +
+ {label} + +
+ ); +}; diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index da354cfe5..f0214ba2a 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -788,6 +788,19 @@ "meta": { "title": "Lend and borrow" }, + "common": { + "connectWallet": "Connect wallet to see the available data", + "asset": "Asset", + "collateral": "Collateral", + "balance": "Balance", + "apy": "APY", + "apyInfo": "TODO:", + "apr": "APR", + "aprInfo": "APR is estimated based on previous fees earned at the defined price range, if no trades were made previously or you have recently claimed fees/withdrawn liquidity, you may see 0 value", + "details": "Details", + "borrow": "Borrow", + "lend": "Lend" + }, "topPanel": { "title": "Lend and borrow", "subtitle": "Borrow and earn interest by providing liquidity", @@ -796,31 +809,23 @@ "n/a": "N/A" }, "borrowPositionsList": { - "title": "Your loans", - "connectWallet": "Connect wallet to see the available data" + "title": "Your loans" }, "borrowAssetsList": { "title": "Assets to borrow", - "asset": "Asset", - "apr": "APR", "available": "Available", - "borrow": "Borrow", - "details": "Details", - "availableInfo": "TODO:", - "aprInfo": "APR is estimated based on previous fees earned at the defined price range, if no trades were made previously or you have recently claimed fees/withdrawn liquidity, you may see 0 value" + "availableInfo": "TODO:" }, "lendPositionsList": { "title": "Your lending deposits", - "connectWallet": "Connect wallet to see the available data" + "usedAsCollateral": "Used as collateral", + "withdraw": "Withdraw" }, "lendAssetsList": { "title": "Assets to lend", "asset": "Asset", "apy": "APY", - "apyInfo": "TODO:", "walletBalance": "Wallet balance", - "lend": "Lend", - "details": "Details", "canBeCollateral": "Can be collateral" }, "assetCard": {} From 6813ccc78e1dd266a3b21a465c2391ad98afbe08 Mon Sep 17 00:00:00 2001 From: matzapata Date: Thu, 8 Aug 2024 22:45:44 +0300 Subject: [PATCH 008/116] fixes and cleanup --- .../BorrowAssetsList.constants.tsx | 2 + .../BorrowPositionsList.constants.tsx | 77 +++++++++++++++++++ .../BorrowPositionsList.tsx | 60 +++++++++++++-- .../BorrowPositionsList.types.tsx | 6 ++ .../BorrowPositionAction.tsx | 25 ++++++ .../BorrowPositionDetails.tsx | 67 ++++++++++++++++ .../LendAssetsList.constants.tsx | 2 +- .../LendPositionsList.constants.tsx | 17 ++-- .../LendPositionsList/LendPositionsList.tsx | 9 ++- .../LendPositionStat/LendPositionStat.tsx | 34 -------- .../PoolPositionStat/PoolPositionStat.tsx | 49 ++++++++++++ .../AavePage/components/TopPanel/TopPanel.tsx | 10 +-- .../frontend/src/locales/en/translations.json | 13 +++- .../OrderDirectionIcon.module.css | 2 +- 14 files changed, 309 insertions(+), 64 deletions(-) create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionAction/BorrowPositionAction.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx delete mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionStat/LendPositionStat.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/PoolPositionStat/PoolPositionStat.tsx diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx index e50ce1b08..0ff3be836 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx @@ -40,6 +40,7 @@ export const COLUMNS_CONFIG = [ ), sortable: true, align: Align.center, + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head }, { id: 'apr', @@ -51,6 +52,7 @@ export const COLUMNS_CONFIG = [ ), sortable: true, align: Align.center, + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head }, { id: 'actions', diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx new file mode 100644 index 000000000..56676e820 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx @@ -0,0 +1,77 @@ +import React from 'react'; + +import { t } from 'i18next'; + +import { Align, HelperButton } from '@sovryn/ui'; + +import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; +import { translations } from '../../../../../locales/i18n'; +import { BorrowPosition } from './BorrowPositionsList.types'; +import { BorrowPositionAction } from './components/BorrowPositionAction/BorrowPositionAction'; + +const pageTranslations = translations.aavePage; + +export const COLUMNS_CONFIG = [ + { + id: 'asset', + title: ( + {t(pageTranslations.common.asset)} + ), + cellRenderer: (pool: BorrowPosition) => ( + + ), + align: Align.center, + sortable: true, + }, + { + id: 'balance', + title: ( + + {t(pageTranslations.common.balance)}{' '} + + ), + sortable: true, + align: Align.center, + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head + }, + { + id: 'apr', + title: ( + + {t(translations.aavePage.common.apr)}{' '} + + + ), + sortable: true, + align: Align.center, + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head + }, + { + id: 'apyType', + title: ( + + {t(pageTranslations.common.apyType)}{' '} + + + ), + sortable: true, + align: Align.center, + cellRenderer: (pos: BorrowPosition) => ( + {t(pageTranslations.common[pos.apyType])} + ), + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head + }, + { + id: 'actions', + align: Align.center, + title: ' ', + cellRenderer: (pool: BorrowPosition) => ( + + ), + }, +]; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index fab3cd9c7..dee208209 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -2,19 +2,21 @@ import React, { FC, useState } from 'react'; import { t } from 'i18next'; -import { Accordion, Paragraph } from '@sovryn/ui'; +import { Accordion, Paragraph, Table } from '@sovryn/ui'; +import { AavePoolRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; +import { useAccount } from '../../../../../hooks/useAccount'; import { translations } from '../../../../../locales/i18n'; +import { PoolPositionStat } from '../PoolPositionStat/PoolPositionStat'; +import { COLUMNS_CONFIG } from './BorrowPositionsList.constants'; +import { BorrowPositionDetails } from './components/BorrowPositionDetails/BorrowPositionDetails'; const pageTranslations = translations.aavePage; -type BorrowPositionsListProps = { - account?: string; -}; +type BorrowPositionsListProps = {}; -export const BorrowPositionsList: FC = ({ - account, -}) => { +export const BorrowPositionsList: FC = () => { + const { account } = useAccount(); const [open, setOpen] = useState(true); return ( @@ -30,7 +32,49 @@ export const BorrowPositionsList: FC = ({ onClick={setOpen} > {account ? ( - <> + <> +
+ + + +
+
} + rows={[ + { + asset: 'BTC', + apr: 2, + balance: 12.34, + apyType: 'variable', + }, + { + asset: 'ETH', + apr: 2, + balance: 12.34, + apyType: 'fixed', + }, + ]} + mobileRenderer={p => } + /> + ) : (
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx new file mode 100644 index 000000000..e56b6b1ea --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx @@ -0,0 +1,6 @@ +export type BorrowPosition = { + asset: string; + balance: number; + apr: number; + apyType: 'variable' | 'fixed'; +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionAction/BorrowPositionAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionAction/BorrowPositionAction.tsx new file mode 100644 index 000000000..0ac73034f --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionAction/BorrowPositionAction.tsx @@ -0,0 +1,25 @@ +import React, { FC } from 'react'; + +import { t } from 'i18next'; + +import { Button, ButtonStyle } from '@sovryn/ui'; + +import { translations } from '../../../../../../../locales/i18n'; +import { BorrowPosition } from '../../BorrowPositionsList.types'; + +type BorrowPositionActionProps = { + pool: BorrowPosition; +}; + +export const BorrowPositionAction: FC = () => { + return ( +
+ {/* TODO: these should be modal triggers */} +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx new file mode 100644 index 000000000..602b01e29 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx @@ -0,0 +1,67 @@ +import React, { FC } from 'react'; + +import { t } from 'i18next'; + +import { HelperButton } from '@sovryn/ui'; + +import { translations } from '../../../../../../../locales/i18n'; +import { BorrowPosition } from '../../BorrowPositionsList.types'; +import { BorrowPositionAction } from '../BorrowPositionAction/BorrowPositionAction'; + +type BorrowPositionDetailsProps = { + pool: BorrowPosition; +}; + +export const BorrowPositionDetails: FC = ({ + pool, +}) => { + return ( +
+
+ {/* Available */} +
+
+ + {t(translations.aavePage.common.balance)} + +
+ + {/* TODO: review amount renderer component */} +
+ {pool.balance} {pool.asset} +
+
+ + {/* APR */} +
+
+ + {t(translations.aavePage.common.apr)} + + +
+
+ {pool.apr} +
+
+ + {/* Apy type */} +
+
+ + {t(translations.aavePage.common.apyType)} + +
+
+ {pool.apyType} +
+
+
+ + +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx index f5c1c17bb..8d5b4a514 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx @@ -55,7 +55,7 @@ export const COLUMNS_CONFIG = [ id: 'canBeCollateral', title: ( - {t(pageTranslations.lendAssetsList.walletBalance)}{' '} + {t(pageTranslations.lendAssetsList.canBeCollateral)}{' '} ), sortable: true, diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx index 5ba81e563..e2fc90185 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx @@ -31,12 +31,11 @@ export const COLUMNS_CONFIG = [ { id: 'balance', title: ( - - {t(pageTranslations.lendAssetsList.walletBalance)}{' '} - + {t(pageTranslations.common.balance)} ), sortable: true, align: Align.center, + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head }, { id: 'apy', @@ -48,6 +47,7 @@ export const COLUMNS_CONFIG = [ ), sortable: true, align: Align.center, + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head }, { id: 'collateral', @@ -56,14 +56,15 @@ export const COLUMNS_CONFIG = [ {t(pageTranslations.common.collateral)}{' '} ), - sortable: true, align: Align.center, cellRenderer: (pool: LendPosition) => ( -
- {/* TODO: This should actually be a switch */} - 'TODO:'} /> -
+ 'TODO:'} + /> ), + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head }, { id: 'actions', diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx index 0327fcb43..c133f6bc9 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx @@ -7,9 +7,9 @@ import { Accordion, Paragraph, Table } from '@sovryn/ui'; import { AavePoolRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { useAccount } from '../../../../../hooks/useAccount'; import { translations } from '../../../../../locales/i18n'; +import { PoolPositionStat } from '../PoolPositionStat/PoolPositionStat'; import { COLUMNS_CONFIG } from './LendPositionsList.constants'; import { LendPositionDetails } from './components/LendPositionDetails/LendPositionDetails'; -import { LendPositionStat } from './components/LendPositionStat/LendPositionStat'; const pageTranslations = translations.aavePage; @@ -34,19 +34,20 @@ export const LendPositionsList: FC = () => { {account ? ( <>
- - - = ({ - label, - className, - ...props -}) => { - return ( -
- {label} - -
- ); -}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/PoolPositionStat/PoolPositionStat.tsx b/apps/frontend/src/app/5_pages/AavePage/components/PoolPositionStat/PoolPositionStat.tsx new file mode 100644 index 000000000..3bd14700a --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/PoolPositionStat/PoolPositionStat.tsx @@ -0,0 +1,49 @@ +import React from 'react'; + +import classNames from 'classnames'; + +import { HelperButton } from '@sovryn/ui'; + +import { + AmountRenderer, + AmountRendererProps, +} from '../../../../2_molecules/AmountRenderer/AmountRenderer'; + +type PoolPositionStatProps = AmountRendererProps & { + label: string; + labelInfo?: string; +}; + +export const PoolPositionStat: React.FC = ({ + label, + labelInfo, + className, + ...props +}) => { + return ( +
+ + {label}{' '} + {labelInfo && ( + + )} + + +
+ + + {labelInfo && ( + + )} +
+
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx index 7d4009968..6bfabe1fa 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx @@ -38,21 +38,19 @@ export const TopPanel: FC = ({ account }) => { label={t(pageTranslations.netWorth)} prefix="$" value="1,234,567.58" - // TODO: update content and translations - helperContent="Net APY is the combined effect of all supply and borrow positions on net worth, including incentives. It is possible to have a negative net APY if debt APY is higher than supply APY." />
diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index f0214ba2a..6f223ffd7 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -795,21 +795,30 @@ "balance": "Balance", "apy": "APY", "apyInfo": "TODO:", + "apyType": "APY type", + "apyTypeInfo": "APY type info", "apr": "APR", "aprInfo": "APR is estimated based on previous fees earned at the defined price range, if no trades were made previously or you have recently claimed fees/withdrawn liquidity, you may see 0 value", "details": "Details", "borrow": "Borrow", - "lend": "Lend" + "lend": "Lend", + "variable": "Variable", + "fixed": "Fixed" }, "topPanel": { "title": "Lend and borrow", "subtitle": "Borrow and earn interest by providing liquidity", "netWorth": "Net worth", "netApy": "Net API", + "netApyInfo": "Net apy info", + "collateralRatio": "Collateral ratio", + "collateralRatioInfo": "Collateral ratio info", "n/a": "N/A" }, "borrowPositionsList": { - "title": "Your loans" + "title": "Your loans", + "repay": "Repay", + "borrowPowerUsed": "Borrow power used" }, "borrowAssetsList": { "title": "Assets to borrow", diff --git a/packages/ui/src/2_molecules/Table/components/TableDesktop/components/OrderDirectionIcon/OrderDirectionIcon.module.css b/packages/ui/src/2_molecules/Table/components/TableDesktop/components/OrderDirectionIcon/OrderDirectionIcon.module.css index 2bb7ef031..c0f1d668c 100644 --- a/packages/ui/src/2_molecules/Table/components/TableDesktop/components/OrderDirectionIcon/OrderDirectionIcon.module.css +++ b/packages/ui/src/2_molecules/Table/components/TableDesktop/components/OrderDirectionIcon/OrderDirectionIcon.module.css @@ -1,5 +1,5 @@ .icon { - @apply ml-2 opacity-0 hover:opacity-100 transition-opacity; + @apply ml-2 opacity-30 hover:opacity-100 transition-opacity; &.active { @apply opacity-100; From ba9030866f257b9dfd8920b5a4973467bf65d38a Mon Sep 17 00:00:00 2001 From: matzapata Date: Thu, 8 Aug 2024 23:09:53 +0300 Subject: [PATCH 009/116] cleanup --- .../src/app/5_pages/AavePage/AaavePage.tsx | 10 +++-- .../BorrowAssetsList.constants.tsx | 36 ++++++++--------- .../BorrowAssetsList/BorrowAssetsList.tsx | 1 + .../BorrowAssetsList.types.tsx | 2 +- .../BorrowAssetAction/BorrowAssetAction.tsx | 4 +- .../BorrowAssetDetails/BorrowAssetDetails.tsx | 8 +++- .../BorrowPositionsList.constants.tsx | 35 +++++++++-------- .../BorrowPositionsList.tsx | 4 +- .../BorrowPositionAction.tsx | 2 +- .../BorrowPositionDetails.tsx | 12 +++--- .../LendAssetsList.constants.tsx | 38 ++++++++---------- .../LendAssetsList/LendAssetsList.tsx | 12 ++++-- .../LendAssetsList/LendAssetsList.types.tsx | 2 +- .../LendAssetAction/LendAssetAction.tsx | 4 +- .../LendAssetDetails/LendAssetDetails.tsx | 7 +++- .../LendPositionsList.constants.tsx | 39 +++++++++---------- .../LendPositionsList/LendPositionsList.tsx | 4 +- .../LendPositionAction/LendPositionAction.tsx | 2 +- .../LendPositionDetails.tsx | 22 ++++++----- .../ToggleCollateralAction.tsx | 21 ++++++++++ .../frontend/src/locales/en/translations.json | 3 +- 21 files changed, 154 insertions(+), 114 deletions(-) create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/ToggleCollateralAction/ToggleCollateralAction.tsx diff --git a/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx b/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx index bcc087065..783d0eda0 100644 --- a/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx @@ -14,6 +14,8 @@ import { LendAssetsList } from './components/LendAssetsList/LendAssetsList'; import { LendPositionsList } from './components/LendPositionsList/LendPositionsList'; import { TopPanel } from './components/TopPanel/TopPanel'; +const pageTranslations = translations.aavePage; + enum LiquidityTabs { LEND = 0, BORROW, @@ -26,9 +28,9 @@ const AavePage: FC = () => { ); return ( -
+
- {t(translations.aavePage.meta.title)} + {t(pageTranslations.meta.title)} @@ -42,12 +44,12 @@ const AavePage: FC = () => { { activeClassName: 'text-primary-20', dataAttribute: 'lending', - label: 'Lend', // TODO: translations + label: t(pageTranslations.common.lend), }, { activeClassName: 'text-primary-20', dataAttribute: 'borrowing', - label: 'Borrow', // TODO: translations + label: t(pageTranslations.common.borrow), }, ]} onChange={e => setActiveLiquidityTab(e)} diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx index 0ff3be836..eeebdb859 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx @@ -6,18 +6,20 @@ import { Align, HelperButton } from '@sovryn/ui'; import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { translations } from '../../../../../locales/i18n'; -import { BorrowPoolAssetDetails } from './BorrowAssetsList.types'; +import { BorrowPoolDetails } from './BorrowAssetsList.types'; import { BorrowAssetAction } from './components/BorrowAssetAction/BorrowAssetAction'; +const pageTranslations = translations.aavePage; + export const COLUMNS_CONFIG = [ { id: 'asset', + sortable: true, + align: Align.center, title: ( - - {t(translations.aavePage.common.asset)} - + {t(pageTranslations.common.asset)} ), - cellRenderer: (pool: BorrowPoolAssetDetails) => ( + cellRenderer: (pool: BorrowPoolDetails) => ( ), - align: Align.center, - sortable: true, }, { id: 'available', + sortable: true, + align: Align.center, + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head title: ( - {t(translations.aavePage.borrowAssetsList.available)}{' '} + {t(pageTranslations.borrowAssetsList.available)}{' '} ), - sortable: true, - align: Align.center, - className: '[&_*]:mx-auto [&_*]:space-x-2', // center head }, { id: 'apr', + sortable: true, + align: Align.center, + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head title: ( - {t(translations.aavePage.common.apr)}{' '} - + {t(pageTranslations.common.apr)}{' '} + ), - sortable: true, - align: Align.center, - className: '[&_*]:mx-auto [&_*]:space-x-2', // center head }, { id: 'actions', align: Align.center, title: ' ', - cellRenderer: (pool: BorrowPoolAssetDetails) => ( + cellRenderer: (pool: BorrowPoolDetails) => ( ), }, diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx index a88b3bfa0..410be8736 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx @@ -37,6 +37,7 @@ export const BorrowAssetsList: FC = ({ account }) => { accordionClassName="bg-gray-60 border border-gray-70" rowTitle={r => } rows={[ + // TODO: just a mock for now { asset: 'BTC', apr: 2, diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.types.tsx index 423a5519e..ee3e1b4d3 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.types.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.types.tsx @@ -1,4 +1,4 @@ -export type BorrowPoolAssetDetails = { +export type BorrowPoolDetails = { asset: string; apr: number; available: number; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx index 6ce196f3f..d3258355c 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx @@ -5,12 +5,12 @@ import { t } from 'i18next'; import { Button, ButtonStyle } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; -import { BorrowPoolAssetDetails } from '../../BorrowAssetsList.types'; +import { BorrowPoolDetails } from '../../BorrowAssetsList.types'; const pageTranslations = translations.aavePage; type BorrowAssetActionProps = { - pool: BorrowPoolAssetDetails; + pool: BorrowPoolDetails; }; export const BorrowAssetAction: FC = () => { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx index 3ebb8311d..658fedebe 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx @@ -5,13 +5,13 @@ import { t } from 'i18next'; import { HelperButton } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; -import { BorrowPoolAssetDetails } from '../../BorrowAssetsList.types'; +import { BorrowPoolDetails } from '../../BorrowAssetsList.types'; import { BorrowAssetAction } from '../BorrowAssetAction/BorrowAssetAction'; const pageTranslations = translations.aavePage; type BorrowAssetDetailsProps = { - pool: BorrowPoolAssetDetails; + pool: BorrowPoolDetails; }; export const BorrowAssetDetails: FC = ({ pool }) => { @@ -29,6 +29,8 @@ export const BorrowAssetDetails: FC = ({ pool }) => { className="text-gray-30" />
+ + {/* TODO: amount renderer */}
{pool.apr}
@@ -41,6 +43,8 @@ export const BorrowAssetDetails: FC = ({ pool }) => { {t(pageTranslations.borrowAssetsList.available)}
+ + {/* TODO: amount renderer */}
{pool.available} {pool.asset}
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx index 56676e820..d606e5c6a 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx @@ -14,64 +14,65 @@ const pageTranslations = translations.aavePage; export const COLUMNS_CONFIG = [ { id: 'asset', + sortable: true, + align: Align.center, title: ( {t(pageTranslations.common.asset)} ), - cellRenderer: (pool: BorrowPosition) => ( + cellRenderer: (position: BorrowPosition) => ( ), - align: Align.center, - sortable: true, }, { id: 'balance', + sortable: true, + align: Align.center, + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head title: ( {t(pageTranslations.common.balance)}{' '} ), - sortable: true, - align: Align.center, - className: '[&_*]:mx-auto [&_*]:space-x-2', // center head }, { id: 'apr', + sortable: true, + align: Align.center, + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head title: ( {t(translations.aavePage.common.apr)}{' '} ), - sortable: true, - align: Align.center, - className: '[&_*]:mx-auto [&_*]:space-x-2', // center head }, { id: 'apyType', + sortable: true, + align: Align.center, + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head title: ( {t(pageTranslations.common.apyType)}{' '} ), - sortable: true, - align: Align.center, - cellRenderer: (pos: BorrowPosition) => ( - {t(pageTranslations.common[pos.apyType])} + + cellRenderer: (position: BorrowPosition) => ( + {t(pageTranslations.common[position.apyType])} ), - className: '[&_*]:mx-auto [&_*]:space-x-2', // center head }, { id: 'actions', align: Align.center, title: ' ', - cellRenderer: (pool: BorrowPosition) => ( - + cellRenderer: (position: BorrowPosition) => ( + ), }, ]; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index dee208209..afe3cab07 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -34,6 +34,7 @@ export const BorrowPositionsList: FC = () => { {account ? ( <>
+ {/* TODO: mocked values */} = () => { accordionClassName="bg-gray-60 border border-gray-70" rowTitle={r => } rows={[ + // TODO: mocked values { asset: 'BTC', apr: 2, @@ -72,7 +74,7 @@ export const BorrowPositionsList: FC = () => { apyType: 'fixed', }, ]} - mobileRenderer={p => } + mobileRenderer={p => } /> ) : ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionAction/BorrowPositionAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionAction/BorrowPositionAction.tsx index 0ac73034f..ff177e904 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionAction/BorrowPositionAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionAction/BorrowPositionAction.tsx @@ -8,7 +8,7 @@ import { translations } from '../../../../../../../locales/i18n'; import { BorrowPosition } from '../../BorrowPositionsList.types'; type BorrowPositionActionProps = { - pool: BorrowPosition; + position: BorrowPosition; }; export const BorrowPositionAction: FC = () => { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx index 602b01e29..3a6612cf6 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx @@ -9,11 +9,11 @@ import { BorrowPosition } from '../../BorrowPositionsList.types'; import { BorrowPositionAction } from '../BorrowPositionAction/BorrowPositionAction'; type BorrowPositionDetailsProps = { - pool: BorrowPosition; + position: BorrowPosition; }; export const BorrowPositionDetails: FC = ({ - pool, + position, }) => { return (
@@ -28,7 +28,7 @@ export const BorrowPositionDetails: FC = ({ {/* TODO: review amount renderer component */}
- {pool.balance} {pool.asset} + {position.balance} {position.asset}
@@ -44,7 +44,7 @@ export const BorrowPositionDetails: FC = ({ />
- {pool.apr} + {position.apr}
@@ -56,12 +56,12 @@ export const BorrowPositionDetails: FC = ({
- {pool.apyType} + {position.apyType}
- + ); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx index 8d5b4a514..8b2cff090 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx @@ -6,7 +6,7 @@ import { Align, HelperButton, Icon } from '@sovryn/ui'; import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { translations } from '../../../../../locales/i18n'; -import { LendPoolAssetDetails } from './LendAssetsList.types'; +import { LendPoolDetails } from './LendAssetsList.types'; import { LendAssetAction } from './components/LendAssetAction/LendAssetAction'; const pageTranslations = translations.aavePage; @@ -14,12 +14,12 @@ const pageTranslations = translations.aavePage; export const COLUMNS_CONFIG = [ { id: 'asset', + sortable: true, + align: Align.center, title: ( - - {t(pageTranslations.lendAssetsList.asset)} - + {t(pageTranslations.common.asset)} ), - cellRenderer: (pool: LendPoolAssetDetails) => ( + cellRenderer: (pool: LendPoolDetails) => ( ), - align: Align.center, - sortable: true, }, { id: 'walletBalance', + sortable: true, + align: Align.center, title: ( - + {t(pageTranslations.lendAssetsList.walletBalance)}{' '} ), - sortable: true, - align: Align.center, }, { id: 'apy', + sortable: true, + align: Align.center, title: ( - {t(pageTranslations.lendAssetsList.apy)}{' '} + {t(pageTranslations.common.apy)}{' '} ), - sortable: true, - align: Align.center, }, { id: 'canBeCollateral', + sortable: true, + align: Align.center, title: ( - - {t(pageTranslations.lendAssetsList.canBeCollateral)}{' '} + + {t(pageTranslations.lendAssetsList.canBeCollateral)} ), - sortable: true, - align: Align.center, - cellRenderer: (pool: LendPoolAssetDetails) => ( + cellRenderer: (pool: LendPoolDetails) => (
{pool.canBeCollateral && ( @@ -72,8 +70,6 @@ export const COLUMNS_CONFIG = [ id: 'actions', align: Align.center, title: ' ', - cellRenderer: (pool: LendPoolAssetDetails) => ( - - ), + cellRenderer: (pool: LendPoolDetails) => , }, ]; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx index 1eee75dce..52d29ad0e 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx @@ -9,7 +9,7 @@ import { translations } from '../../../../../locales/i18n'; import { COLUMNS_CONFIG } from './LendAssetsList.constants'; import { LendAssetDetails } from './components/LendAssetDetails/LendAssetDetails'; -const pageTranslations = translations.aavePage.lendAssetsList; +const pageTranslations = translations.aavePage; type LendAssetsListProps = { account?: string; @@ -17,12 +17,13 @@ type LendAssetsListProps = { export const LendAssetsList: FC = ({ account }) => { const [open, setOpen] = useState(true); + const [showZeroBalances, setShowZeroBalances] = useState(true); return ( - {t(pageTranslations.title)} + {t(pageTranslations.lendAssetsList.title)} } className="bg-gray-70 px-4 py-3 rounded space-y-3 lg:bg-gray-90 lg:p-6" @@ -31,7 +32,11 @@ export const LendAssetsList: FC = ({ account }) => { onClick={setOpen} >
- + setShowZeroBalances(s)} + />
= ({ account }) => { accordionClassName="bg-gray-60 border border-gray-70" rowTitle={r => } rows={[ + // TODO: mocked values { asset: 'BTC', apy: 2, diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.types.tsx index d6ddd4fef..ecd5a1814 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.types.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.types.tsx @@ -1,4 +1,4 @@ -export type LendPoolAssetDetails = { +export type LendPoolDetails = { asset: string; walletBalance: number; apy: number; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx index 640f03df1..65282959c 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx @@ -5,10 +5,10 @@ import { t } from 'i18next'; import { Button, ButtonStyle } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; -import { LendPoolAssetDetails } from '../../LendAssetsList.types'; +import { LendPoolDetails } from '../../LendAssetsList.types'; type LendAssetActionProps = { - pool: LendPoolAssetDetails; + pool: LendPoolDetails; }; export const LendAssetAction: FC = () => { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx index 5b0267196..186f10549 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx @@ -5,11 +5,11 @@ import { t } from 'i18next'; import { HelperButton, Icon } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; -import { LendPoolAssetDetails } from '../../LendAssetsList.types'; +import { LendPoolDetails } from '../../LendAssetsList.types'; import { LendAssetAction } from '../LendAssetAction/LendAssetAction'; type LendAssetDetailsProps = { - pool: LendPoolAssetDetails; + pool: LendPoolDetails; }; export const LendAssetDetails: FC = ({ pool }) => { @@ -41,6 +41,8 @@ export const LendAssetDetails: FC = ({ pool }) => { className="text-gray-30" /> + + {/* TODO: review amount renderer component */}
{pool.apy}
@@ -53,6 +55,7 @@ export const LendAssetDetails: FC = ({ pool }) => { {t(translations.aavePage.lendAssetsList.canBeCollateral)} +
{pool.canBeCollateral ? ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx index e2fc90185..f3407fd87 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx @@ -2,74 +2,73 @@ import React from 'react'; import { t } from 'i18next'; -import { Align, HelperButton, Toggle } from '@sovryn/ui'; +import { Align, HelperButton } from '@sovryn/ui'; import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { translations } from '../../../../../locales/i18n'; import { LendPosition } from './LendPositionsList.types'; import { LendPositionAction } from './components/LendPositionAction/LendPositionAction'; +import { ToggleCollateralAction } from './components/ToggleCollateralAction/ToggleCollateralAction'; const pageTranslations = translations.aavePage; export const COLUMNS_CONFIG = [ { id: 'asset', + sortable: true, + align: Align.center, title: ( {t(pageTranslations.common.asset)} ), - cellRenderer: (pool: LendPosition) => ( + cellRenderer: (position: LendPosition) => ( ), - align: Align.center, - sortable: true, }, { id: 'balance', - title: ( - {t(pageTranslations.common.balance)} - ), sortable: true, align: Align.center, className: '[&_*]:mx-auto [&_*]:space-x-2', // center head + title: ( + {t(pageTranslations.common.balance)} + ), }, { id: 'apy', + sortable: true, + align: Align.center, + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head title: ( {t(translations.aavePage.common.apy)}{' '} ), - sortable: true, - align: Align.center, - className: '[&_*]:mx-auto [&_*]:space-x-2', // center head }, { id: 'collateral', + align: Align.center, + className: '[&_*]:mx-auto [&_*]:space-x-2', // center head title: ( {t(pageTranslations.common.collateral)}{' '} ), - align: Align.center, - cellRenderer: (pool: LendPosition) => ( - 'TODO:'} - /> + cellRenderer: (position: LendPosition) => ( + ), - className: '[&_*]:mx-auto [&_*]:space-x-2', // center head }, { id: 'actions', align: Align.center, title: ' ', - cellRenderer: (pool: LendPosition) => , + cellRenderer: (position: LendPosition) => ( + + ), }, ]; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx index c133f6bc9..1075cc69e 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx @@ -34,6 +34,7 @@ export const LendPositionsList: FC = () => { {account ? ( <>
+ {/* TODO: mock data */} = () => { accordionClassName="bg-gray-60 border border-gray-70" rowTitle={r => } rows={[ + // TODO: mocked data { asset: 'BTC', apy: 2, @@ -73,7 +75,7 @@ export const LendPositionsList: FC = () => { collateral: false, }, ]} - mobileRenderer={p => } + mobileRenderer={p => } /> ) : ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx index 03a95fb05..bbba9103e 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx @@ -8,7 +8,7 @@ import { translations } from '../../../../../../../locales/i18n'; import { LendPosition } from '../../LendPositionsList.types'; type LendPositionActionProps = { - pool: LendPosition; + position: LendPosition; }; export const LendPositionAction: FC = () => { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx index 7ba96fb60..7792f76e4 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx @@ -2,17 +2,20 @@ import React, { FC } from 'react'; import { t } from 'i18next'; -import { HelperButton, Toggle } from '@sovryn/ui'; +import { HelperButton } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; import { LendPosition } from '../../LendPositionsList.types'; import { LendPositionAction } from '../LendPositionAction/LendPositionAction'; +import { ToggleCollateralAction } from '../ToggleCollateralAction/ToggleCollateralAction'; type LendPositionDetailsProps = { - pool: LendPosition; + position: LendPosition; }; -export const LendPositionDetails: FC = ({ pool }) => { +export const LendPositionDetails: FC = ({ + position, +}) => { return (
@@ -26,7 +29,7 @@ export const LendPositionDetails: FC = ({ pool }) => { {/* TODO: review amount renderer component */}
- {pool.balance} {pool.asset} + {position.balance} {position.asset}
@@ -34,7 +37,7 @@ export const LendPositionDetails: FC = ({ pool }) => {
- {t(translations.aavePage.lendAssetsList.apy)} + {t(translations.aavePage.common.apy)} = ({ pool }) => { />
- {pool.apy} + {position.apy}
@@ -53,13 +56,14 @@ export const LendPositionDetails: FC = ({ pool }) => { {t(translations.aavePage.lendAssetsList.canBeCollateral)}
-
- 'TODO:'} /> + +
+
- +
); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/ToggleCollateralAction/ToggleCollateralAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/ToggleCollateralAction/ToggleCollateralAction.tsx new file mode 100644 index 000000000..1697c2de7 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/ToggleCollateralAction/ToggleCollateralAction.tsx @@ -0,0 +1,21 @@ +import React, { FC } from 'react'; + +import { Toggle } from '@sovryn/ui'; + +import { LendPosition } from '../../LendPositionsList.types'; + +type ToggleCollateralActionProps = { + position: LendPosition; +}; + +export const ToggleCollateralAction: FC = ({ + position, +}) => { + return ( + 'TODO: implement'} + /> + ); +}; diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index 6f223ffd7..191b8926f 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -832,8 +832,7 @@ }, "lendAssetsList": { "title": "Assets to lend", - "asset": "Asset", - "apy": "APY", + "showZeroBalances": "Show assets with 0 balance", "walletBalance": "Wallet balance", "canBeCollateral": "Can be collateral" }, From e42e0e2d0e5eebc3d3019f2b08dff90999c69564 Mon Sep 17 00:00:00 2001 From: matzapata Date: Thu, 8 Aug 2024 23:28:17 +0300 Subject: [PATCH 010/116] cleanup --- .../src/app/5_pages/AavePage/AaavePage.tsx | 4 +-- .../BorrowAssetDetails/BorrowAssetDetails.tsx | 7 +++--- .../BorrowPositionDetails.tsx | 8 +++--- .../LendAssetDetails/LendAssetDetails.tsx | 9 +++---- .../LendPositionDetails.tsx | 11 ++++---- .../AavePage/components/TopPanel/TopPanel.tsx | 16 ++++++------ .../WalletStatCard/WalletStatCard.tsx | 25 ++++++++++++------- 7 files changed, 44 insertions(+), 36 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx b/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx index 783d0eda0..3298ff574 100644 --- a/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/AaavePage.tsx @@ -6,7 +6,6 @@ import { Helmet } from 'react-helmet-async'; import { Tabs, TabSize, TabType } from '@sovryn/ui'; -import { useAccount } from '../../../hooks/useAccount'; import { translations } from '../../../locales/i18n'; import { BorrowAssetsList } from './components/BorrowAssetsList/BorrowAssetsList'; import { BorrowPositionsList } from './components/BorrowPositionsList/BorrowPositionsList'; @@ -22,7 +21,6 @@ enum LiquidityTabs { } const AavePage: FC = () => { - const { account } = useAccount(); const [activeLiquidityTab, setActiveLiquidityTab] = useState( LiquidityTabs.LEND, ); @@ -33,7 +31,7 @@ const AavePage: FC = () => { {t(pageTranslations.meta.title)} - +
{/* Tab selector */} diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx index 658fedebe..95879ae0e 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx @@ -4,6 +4,7 @@ import { t } from 'i18next'; import { HelperButton } from '@sovryn/ui'; +import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { translations } from '../../../../../../../locales/i18n'; import { BorrowPoolDetails } from '../../BorrowAssetsList.types'; import { BorrowAssetAction } from '../BorrowAssetAction/BorrowAssetAction'; @@ -30,9 +31,8 @@ export const BorrowAssetDetails: FC = ({ pool }) => { />
- {/* TODO: amount renderer */}
- {pool.apr} +
@@ -44,9 +44,8 @@ export const BorrowAssetDetails: FC = ({ pool }) => { - {/* TODO: amount renderer */}
- {pool.available} {pool.asset} +
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx index 3a6612cf6..bf51ec087 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx @@ -4,6 +4,7 @@ import { t } from 'i18next'; import { HelperButton } from '@sovryn/ui'; +import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { translations } from '../../../../../../../locales/i18n'; import { BorrowPosition } from '../../BorrowPositionsList.types'; import { BorrowPositionAction } from '../BorrowPositionAction/BorrowPositionAction'; @@ -26,9 +27,8 @@ export const BorrowPositionDetails: FC = ({ - {/* TODO: review amount renderer component */}
- {position.balance} {position.asset} +
@@ -43,8 +43,9 @@ export const BorrowPositionDetails: FC = ({ className="text-gray-30" /> +
- {position.apr} +
@@ -55,6 +56,7 @@ export const BorrowPositionDetails: FC = ({ {t(translations.aavePage.common.apyType)} +
{position.apyType}
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx index 186f10549..c8139219a 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx @@ -4,6 +4,7 @@ import { t } from 'i18next'; import { HelperButton, Icon } from '@sovryn/ui'; +import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { translations } from '../../../../../../../locales/i18n'; import { LendPoolDetails } from '../../LendAssetsList.types'; import { LendAssetAction } from '../LendAssetAction/LendAssetAction'; @@ -24,13 +25,12 @@ export const LendAssetDetails: FC = ({ pool }) => { - {/* TODO: review amount renderer component */}
- {pool.walletBalance} {pool.asset} +
- {/* APR */} + {/* APY */}
@@ -42,9 +42,8 @@ export const LendAssetDetails: FC = ({ pool }) => { />
- {/* TODO: review amount renderer component */}
- {pool.apy} +
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx index 7792f76e4..16c799548 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx @@ -4,6 +4,7 @@ import { t } from 'i18next'; import { HelperButton } from '@sovryn/ui'; +import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { translations } from '../../../../../../../locales/i18n'; import { LendPosition } from '../../LendPositionsList.types'; import { LendPositionAction } from '../LendPositionAction/LendPositionAction'; @@ -19,7 +20,7 @@ export const LendPositionDetails: FC = ({ return (
- {/* Available */} + {/* Balance */}
@@ -27,13 +28,12 @@ export const LendPositionDetails: FC = ({
- {/* TODO: review amount renderer component */}
- {position.balance} {position.asset} +
- {/* APR */} + {/* APY */}
@@ -44,8 +44,9 @@ export const LendPositionDetails: FC = ({ className="text-gray-30" />
+
- {position.apy} +
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx index 6bfabe1fa..5a414f014 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx @@ -11,16 +11,17 @@ import { ParagraphSize, } from '@sovryn/ui'; +import { useAccount } from '../../../../../hooks/useAccount'; import { translations } from '../../../../../locales/i18n'; import { WalletStatCard } from './components/WalletStatCard/WalletStatCard'; const pageTranslations = translations.aavePage.topPanel; -type TopPanelProps = { - account: string; -}; +type TopPanelProps = {}; + +export const TopPanel: FC = () => { + const { account } = useAccount(); -export const TopPanel: FC = ({ account }) => { return (
@@ -34,21 +35,22 @@ export const TopPanel: FC = ({ account }) => {
+ {/* TODO: mock values */}
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/components/WalletStatCard/WalletStatCard.tsx b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/components/WalletStatCard/WalletStatCard.tsx index e262d2ee1..2dc6d7c37 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/components/WalletStatCard/WalletStatCard.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/components/WalletStatCard/WalletStatCard.tsx @@ -1,21 +1,25 @@ import React, { FC } from 'react'; +import { t } from 'i18next'; + import { HelperButton } from '@sovryn/ui'; -type WalletStatCardProps = { +import { + AmountRenderer, + AmountRendererProps, +} from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; +import { translations } from '../../../../../../../locales/i18n'; + +type WalletStatCardProps = Partial & { label: string; - value: string; - prefix?: string; - suffix?: string; helperContent?: string; }; export const WalletStatCard: FC = ({ label, value, - prefix, - suffix, helperContent, + ...props }) => { return (
@@ -23,10 +27,13 @@ export const WalletStatCard: FC = ({ {label} {helperContent && }
+
- {prefix && {prefix}} - {value} - {suffix && {suffix}} + {value ? ( + + ) : ( + {t(translations.aavePage.topPanel['n/a'])} + )}
); From bcc105c8bc77b2ffd7691f99573f341c87f3c37c Mon Sep 17 00:00:00 2001 From: matzapata Date: Fri, 9 Aug 2024 00:00:04 +0300 Subject: [PATCH 011/116] cleanup --- .../AavePoolRowTitle/AavePoolRowTitle.tsx | 39 +++++++------------ .../BorrowAssetsList/BorrowAssetsList.tsx | 15 +++++-- .../BorrowAssetDetails/BorrowAssetDetails.tsx | 2 +- .../BorrowPositionsList.tsx | 14 +++++-- .../BorrowPositionDetails.tsx | 6 +-- .../LendAssetsList.constants.tsx | 8 ++-- .../LendAssetsList/LendAssetsList.tsx | 10 +++-- .../LendAssetDetails/LendAssetDetails.tsx | 15 ++++--- .../LendPositionsList/LendPositionsList.tsx | 14 +++++-- .../LendPositionDetails.tsx | 4 +- 10 files changed, 72 insertions(+), 55 deletions(-) diff --git a/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx b/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx index 76d8d5b0a..99466a757 100644 --- a/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx +++ b/apps/frontend/src/app/2_molecules/AavePoolRowTitle/AavePoolRowTitle.tsx @@ -1,39 +1,28 @@ import React, { FC } from 'react'; -import { t } from 'i18next'; - -import { NextBorrowInterestRate } from '../../5_pages/BorrowPage/components/BorrowAssetsTable/components/NextBorrowInterestRate/NextBorrowInterestRate'; -import { NextSupplyInterestRate } from '../../5_pages/LendPage/components/NextSupplyInterestRate/NextSupplyInterestRate'; -import { translations } from '../../../locales/i18n'; +import { + AmountRenderer, + AmountRendererProps, +} from '../AmountRenderer/AmountRenderer'; import { AssetRenderer } from '../AssetRenderer/AssetRenderer'; -type AavePoolRowTitleProps = { +type AaveRowTitleProps = AmountRendererProps & { asset: string; - isBorrow?: boolean; + label?: string; }; -export const AavePoolRowTitle: FC = ({ +export const AaveRowTitle: FC = ({ asset, - isBorrow = false, + label, + ...props }) => (
- -
- {isBorrow ? ( - - ) : ( - + +
+ + {label && ( + {label} )} - - {isBorrow - ? t(translations.fixedInterestPage.borrowAssetsTable.borrowAprMobile) - : t(translations.lendPage.table.lendAprMobile)} -
); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx index 410be8736..765004b05 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx @@ -4,7 +4,7 @@ import { t } from 'i18next'; import { Accordion, Table } from '@sovryn/ui'; -import { AavePoolRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; +import { AaveRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { translations } from '../../../../../locales/i18n'; import { COLUMNS_CONFIG } from './BorrowAssetsList.constants'; import { BorrowAssetDetails } from './components/BorrowAssetDetails/BorrowAssetDetails'; @@ -35,18 +35,25 @@ export const BorrowAssetsList: FC = ({ account }) => { columns={COLUMNS_CONFIG} rowClassName="bg-gray-80" accordionClassName="bg-gray-60 border border-gray-70" - rowTitle={r => } + rowTitle={r => ( + + )} rows={[ // TODO: just a mock for now { asset: 'BTC', - apr: 2, + apr: 2.01, available: 12.34, availableUsd: 100, }, { asset: 'ETH', - apr: 2, + apr: 2.01, available: 12.34, availableUsd: 100, }, diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx index 95879ae0e..194a2c977 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx @@ -32,7 +32,7 @@ export const BorrowAssetDetails: FC = ({ pool }) => {
- +
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index afe3cab07..7ce9237f0 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -4,7 +4,7 @@ import { t } from 'i18next'; import { Accordion, Paragraph, Table } from '@sovryn/ui'; -import { AavePoolRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; +import { AaveRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { useAccount } from '../../../../../hooks/useAccount'; import { translations } from '../../../../../locales/i18n'; import { PoolPositionStat } from '../PoolPositionStat/PoolPositionStat'; @@ -58,18 +58,24 @@ export const BorrowPositionsList: FC = () => { columns={COLUMNS_CONFIG} rowClassName="bg-gray-80" accordionClassName="bg-gray-60 border border-gray-70" - rowTitle={r => } + rowTitle={r => ( + + )} rows={[ // TODO: mocked values { asset: 'BTC', - apr: 2, + apr: 2.24, balance: 12.34, apyType: 'variable', }, { asset: 'ETH', - apr: 2, + apr: 2.33, balance: 12.34, apyType: 'fixed', }, diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx index bf51ec087..8fa4e46ab 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx @@ -45,7 +45,7 @@ export const BorrowPositionDetails: FC = ({
- +
@@ -57,8 +57,8 @@ export const BorrowPositionDetails: FC = ({
-
- {position.apyType} +
+ {t(translations.aavePage.common[position.apyType])}
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx index 8b2cff090..4a681dd58 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { t } from 'i18next'; -import { Align, HelperButton, Icon } from '@sovryn/ui'; +import { Align, HelperButton, Icon, IconNames } from '@sovryn/ui'; import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { translations } from '../../../../../locales/i18n'; @@ -60,8 +60,10 @@ export const COLUMNS_CONFIG = [ ), cellRenderer: (pool: LendPoolDetails) => (
- {pool.canBeCollateral && ( - + {pool.canBeCollateral ? ( + + ) : ( + )}
), diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx index 52d29ad0e..ab4cf58d8 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx @@ -4,7 +4,7 @@ import { t } from 'i18next'; import { Accordion, Checkbox, Table } from '@sovryn/ui'; -import { AavePoolRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; +import { AaveRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { translations } from '../../../../../locales/i18n'; import { COLUMNS_CONFIG } from './LendAssetsList.constants'; import { LendAssetDetails } from './components/LendAssetDetails/LendAssetDetails'; @@ -43,18 +43,20 @@ export const LendAssetsList: FC = ({ account }) => { columns={COLUMNS_CONFIG} rowClassName="bg-gray-80" accordionClassName="bg-gray-60 border border-gray-70" - rowTitle={r => } + rowTitle={r => ( + + )} rows={[ // TODO: mocked values { asset: 'BTC', - apy: 2, + apy: 2.02, walletBalance: 12.34, canBeCollateral: true, }, { asset: 'ETH', - apy: 2, + apy: 2.02, walletBalance: 12.34, canBeCollateral: false, }, diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx index c8139219a..988716e6a 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx @@ -2,7 +2,7 @@ import React, { FC } from 'react'; import { t } from 'i18next'; -import { HelperButton, Icon } from '@sovryn/ui'; +import { HelperButton, Icon, IconNames } from '@sovryn/ui'; import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { translations } from '../../../../../../../locales/i18n'; @@ -43,7 +43,7 @@ export const LendAssetDetails: FC = ({ pool }) => {
- +
@@ -55,10 +55,15 @@ export const LendAssetDetails: FC = ({ pool }) => { -
+
{pool.canBeCollateral ? ( - - ) : null} + + ) : ( + + )}
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx index 1075cc69e..234cff3b0 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx @@ -4,7 +4,7 @@ import { t } from 'i18next'; import { Accordion, Paragraph, Table } from '@sovryn/ui'; -import { AavePoolRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; +import { AaveRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { useAccount } from '../../../../../hooks/useAccount'; import { translations } from '../../../../../locales/i18n'; import { PoolPositionStat } from '../PoolPositionStat/PoolPositionStat'; @@ -59,18 +59,24 @@ export const LendPositionsList: FC = () => { columns={COLUMNS_CONFIG} rowClassName="bg-gray-80" accordionClassName="bg-gray-60 border border-gray-70" - rowTitle={r => } + rowTitle={r => ( + + )} rows={[ // TODO: mocked data { asset: 'BTC', - apy: 2, + apy: 2.01, balance: 12.34, collateral: true, }, { asset: 'ETH', - apy: 2, + apy: 2.04, balance: 12.34, collateral: false, }, diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx index 16c799548..4ba524a5b 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx @@ -46,7 +46,7 @@ export const LendPositionDetails: FC = ({
- +
@@ -54,7 +54,7 @@ export const LendPositionDetails: FC = ({
- {t(translations.aavePage.lendAssetsList.canBeCollateral)} + {t(translations.aavePage.lendPositionsList.usedAsCollateral)}
From 0c02b7010f29157f000db2128fe424a35c9ccc7d Mon Sep 17 00:00:00 2001 From: matzapata Date: Fri, 9 Aug 2024 15:59:41 +0300 Subject: [PATCH 012/116] static ui done. missing validations --- .../LendPositionAction/LendPositionAction.tsx | 21 ++- .../components/WithdrawModal/WithdrawForm.tsx | 123 ++++++++++++++++++ .../WithdrawModal/WithdrawModalContainer.tsx | 30 +++++ 3 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawForm.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawModalContainer.tsx diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx index bbba9103e..b86d1a8ad 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react'; +import React, { FC, useCallback, useState } from 'react'; import { t } from 'i18next'; @@ -6,19 +6,36 @@ import { Button, ButtonStyle } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; import { LendPosition } from '../../LendPositionsList.types'; +import { WithdrawModalContainer } from '../WithdrawModal/WithdrawModalContainer'; type LendPositionActionProps = { position: LendPosition; }; export const LendPositionAction: FC = () => { + const [isWithdrawModalOpen, setIsWithdrawModalOpen] = + useState(false); + + const handleWithdrawClick = useCallback(() => { + setIsWithdrawModalOpen(true); + }, []); + + const handleWithdrawClose = useCallback(() => { + setIsWithdrawModalOpen(false); + }, []); + return (
- {/* TODO: these should be modal triggers */}
); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawForm.tsx new file mode 100644 index 000000000..2d2fcfad3 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawForm.tsx @@ -0,0 +1,123 @@ +import React, { FC, useCallback, useMemo, useState } from 'react'; + +import { t } from 'i18next'; + +import { + AmountInput, + Button, + Select, + SimpleTable, + SimpleTableRow, + Tabs, + TabType, +} from '@sovryn/ui'; + +import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; +import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; +import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; +import { translations } from '../../../../../../../locales/i18n'; + +type WithdrawFormProps = {}; + +export const WithdrawForm: FC = () => { + const withdrawableAssets = useMemo(() => ['BTC', 'SOV'], []); // TODO: Mock data + const [withdrawAsset, setWithdrawAsset] = useState( + withdrawableAssets[0], + ); + const [maxWithdrawableAmount] = useState(10); + const [withdrawAmount, setWithdrawAmount] = useDecimalAmountInput(''); + + const onWithdrawAssetChange = useCallback(v => { + setWithdrawAsset(v); + }, []); + + const withdrawableAssetsOptions = useMemo( + () => + withdrawableAssets.map(token => ({ + value: token, + label: ( + + ), + })), + [withdrawableAssets], + ); + + return ( +
+
+
+ + + (Max{' '} + + ) + +
+ +
+
+ +
+ +
+
+ + = () => { options={withdrawableAssetsOptions} labelRenderer={({ value }) => ( )} className="min-w-[6.7rem]" menuClassName="max-h-[10rem] sm:max-h-[20rem]" - dataAttribute="new-loan-collateral-select" + dataAttribute="withdraw-asset-select" />
@@ -111,13 +138,18 @@ export const WithdrawForm: FC = () => {
} + label={t(translations.aavePage.withdrawForm.remainingSupply)} + value={ + + } />
-
- - {!isValidWithdrawAmount && ( - - )} ( + + )} + className="min-w-[6.7rem]" + menuClassName="max-h-[10rem] sm:max-h-[20rem]" + dataAttribute="borrow-asset-select" + /> + + +
+ {!isValidBorrowAmount && ( + + )} +
+ + + + + } + /> + + +
+
+
+ {t(translations.aavePage.borrowForm.collateralRatio)} +
+
+ +
+
+ + +
+ + + + } + /> + + + + {t(translations.aavePage.borrowForm.acknowledge)}{' '} + {' '} + {/* TODO: Add proper learn more href */} + + } + /> + +
( - - )} - rows={[ - // TODO: just a mock for now - { - asset: 'BTC', - apr: 2.01, - available: 12.34, - availableUsd: 100, - }, - { - asset: 'ETH', - apr: 2.01, - available: 12.34, - availableUsd: 100, - }, - ]} - mobileRenderer={p => } - /> - +
); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx index 683323d97..118fdbb15 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx @@ -1,4 +1,4 @@ -import React, { FC, useCallback, useState } from 'react'; +import React, { FC, useState } from 'react'; import { t } from 'i18next'; import { useNavigate } from 'react-router-dom'; @@ -19,13 +19,13 @@ export const BorrowAssetAction: FC = () => { const navigate = useNavigate(); const [isBorrowModalOpen, setIsBorrowModalOpen] = useState(false); - const handleBorrowClick = useCallback(() => { + const handleBorrowClick = () => { setIsBorrowModalOpen(true); - }, []); + }; - const handleBorrowClose = useCallback(() => { + const handleBorrowClose = () => { setIsBorrowModalOpen(false); - }, []); + }; return (
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx index 06b8f967e..9b9f6aac2 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx @@ -32,9 +32,10 @@ export const BorrowForm: FC = () => { const totalBorrow = Decimal.from(10); // TODO: this is mocked data. Replace with proper hook const collateralToLoanRate = Decimal.from(10); // TODO: this is mocked data. Replace with proper hook const collateralSize = Decimal.from(10); // TODO: this is mockd data. Replace with proper hook - const borrowableAssets = useMemo(() => ['BTC', 'SOV'], []); // TODO: this is mocked data. Replace with proper hook + const availablePools = useMemo(() => ['BTC', 'SOV'], []); // TODO: this is mocked data. Replace with proper hook const [maximumBorrowAmount] = useState(Decimal.from(10)); // TODO: this is mocked data. Replace with proper hook - const [borrowAsset, setBorrowAsset] = useState(borrowableAssets[0]); + const [borrowApr] = useState(2); + const [borrowAsset, setBorrowAsset] = useState(availablePools[0]); const [borrowAmount, setBorrowAmount, borrowSize] = useDecimalAmountInput(''); const [acknowledge, setAcknowledge] = useState(false); @@ -44,7 +45,7 @@ export const BorrowForm: FC = () => { const borrowableAssetsOptions = useMemo( () => - borrowableAssets.map(token => ({ + availablePools.map(token => ({ value: token, label: ( = () => { /> ), })), - [borrowableAssets], + [availablePools], ); const isValidBorrowAmount = useMemo( @@ -62,11 +63,6 @@ export const BorrowForm: FC = () => { [borrowSize, maximumBorrowAmount], ); - const remainingSupply = useMemo( - () => maximumBorrowAmount.sub(borrowSize), - [borrowSize, maximumBorrowAmount], - ); - const collateralRatioThresholds = useMemo( () => getCollateralRatioThresholds(), [], @@ -80,6 +76,7 @@ export const BorrowForm: FC = () => { return collateralSize.mul(collateralToLoanRate).div(totalBorrow).mul(100); }, [collateralSize, totalBorrow, borrowSize, collateralToLoanRate]); + // TODO: expand validations const submitButtonDisabled = useMemo( () => !isValidBorrowAmount || borrowSize.lte(0) || !acknowledge, [isValidBorrowAmount, borrowSize, acknowledge], @@ -112,12 +109,7 @@ export const BorrowForm: FC = () => { - } + value={} /> @@ -126,9 +118,7 @@ export const BorrowForm: FC = () => {
{t(translations.aavePage.borrowForm.collateralRatio)}
-
- -
+
= () => { {t(translations.common.na)}} /> = () => { label={ {t(translations.aavePage.borrowForm.acknowledge)}{' '} - {' '} + {/* TODO: Add proper learn more href */} } diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx index b4d5d7240..23a4cc203 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx @@ -36,8 +36,8 @@ export const COLUMNS_CONFIG = [ align: Align.center, className: '[&_*]:mx-auto [&_*]:space-x-2', // center head title: ( - - {t(pageTranslations.common.balance)}{' '} + + {t(pageTranslations.common.balance)} ), cellRenderer: (pool: BorrowPosition) => ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index 7ce9237f0..5fb93bcbe 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, { FC, useCallback, useState } from 'react'; import { t } from 'i18next'; @@ -9,6 +9,7 @@ import { useAccount } from '../../../../../hooks/useAccount'; import { translations } from '../../../../../locales/i18n'; import { PoolPositionStat } from '../PoolPositionStat/PoolPositionStat'; import { COLUMNS_CONFIG } from './BorrowPositionsList.constants'; +import { BorrowPosition } from './BorrowPositionsList.types'; import { BorrowPositionDetails } from './components/BorrowPositionDetails/BorrowPositionDetails'; const pageTranslations = translations.aavePage; @@ -18,6 +19,35 @@ type BorrowPositionsListProps = {}; export const BorrowPositionsList: FC = () => { const { account } = useAccount(); const [open, setOpen] = useState(true); + const [balance] = useState(123.45); // TODO: mock + const [apy] = useState(2.05); // TODO: mock + const [borrowPowerUsed] = useState(2.05); // TODO: mock + + const rowTitleRenderer = useCallback( + r => , + [], + ); + + const mobileRenderer = useCallback( + p => , + [], + ); + + // TODO: mocked values + const borrowPositions: BorrowPosition[] = [ + { + asset: 'BTC', + apr: 2.24, + balance: 12.34, + apyType: 'variable', + }, + { + asset: 'ETH', + apr: 2.33, + balance: 12.34, + apyType: 'fixed', + }, + ]; return ( = () => { {account ? ( <>
- {/* TODO: mocked values */} @@ -58,29 +87,9 @@ export const BorrowPositionsList: FC = () => { columns={COLUMNS_CONFIG} rowClassName="bg-gray-80" accordionClassName="bg-gray-60 border border-gray-70" - rowTitle={r => ( - - )} - rows={[ - // TODO: mocked values - { - asset: 'BTC', - apr: 2.24, - balance: 12.34, - apyType: 'variable', - }, - { - asset: 'ETH', - apr: 2.33, - balance: 12.34, - apyType: 'fixed', - }, - ]} - mobileRenderer={p => } + rowTitle={rowTitleRenderer} + mobileRenderer={mobileRenderer} + rows={borrowPositions} /> ) : ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx new file mode 100644 index 000000000..970c86fdd --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx @@ -0,0 +1,15 @@ +import React, { FC } from 'react'; + +import { Icon } from '@sovryn/ui'; + +import { EModeIcon } from '../../../../../../1_atoms/Icons/Icons'; + +type EfficiencyModeCardProps = {}; + +export const EfficiencyModeCard: FC = () => { + return ( +
+ +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx index 9024a5913..146c4effc 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx @@ -21,7 +21,7 @@ type RepayModalContainerProps = { handleCloseModal: () => unknown; }; -enum PaymentTabs { +enum RepayWith { BALANCE = 0, COLLATERAL, } @@ -30,7 +30,7 @@ export const RepayModalContainer: FC = ({ isOpen, handleCloseModal, }) => { - const [activeTab, setActiveTab] = useState(PaymentTabs.BALANCE); + const [activeTab, setActiveTab] = useState(RepayWith.BALANCE); return ( @@ -66,7 +66,7 @@ export const RepayModalContainer: FC = ({ type={TabType.secondary} /> - {activeTab === PaymentTabs.BALANCE ? ( + {activeTab === RepayWith.BALANCE ? ( ) : ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx index cdb366b3f..9dda5864d 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx @@ -63,6 +63,7 @@ export const RepayWithWalletBalanceForm: FC< return collateralSize.mul(collateralToLoanRate).div(totalBorrowed).mul(100); }, [collateralSize, totalBorrowed, repaySize, collateralToLoanRate]); + // TODO: Add validations const submitButtonDisabled = useMemo( () => !isValidRepayAmount || repaySize.lte(0), [isValidRepayAmount, repaySize], diff --git a/apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.tsx b/apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.tsx index 6510a791e..1c430c2f6 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.tsx @@ -34,9 +34,7 @@ export const CollateralRatioHealthBar: FC = ({
{t(translations.aavePage.common.collateralRatio)}
-
- -
+
- {t(pageTranslations.lendAssetsList.walletBalance)}{' '} + {t(pageTranslations.lendAssetsList.walletBalance)}
), cellRenderer: (pool: LendPoolDetails) => ( @@ -49,7 +49,7 @@ export const COLUMNS_CONFIG = [ align: Align.center, title: ( - {t(pageTranslations.common.apy)}{' '} + {t(pageTranslations.common.apy)} ), diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx index ab4cf58d8..47a086621 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, { FC, useCallback, useState } from 'react'; import { t } from 'i18next'; @@ -7,6 +7,7 @@ import { Accordion, Checkbox, Table } from '@sovryn/ui'; import { AaveRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { translations } from '../../../../../locales/i18n'; import { COLUMNS_CONFIG } from './LendAssetsList.constants'; +import { LendPoolDetails } from './LendAssetsList.types'; import { LendAssetDetails } from './components/LendAssetDetails/LendAssetDetails'; const pageTranslations = translations.aavePage; @@ -19,6 +20,28 @@ export const LendAssetsList: FC = ({ account }) => { const [open, setOpen] = useState(true); const [showZeroBalances, setShowZeroBalances] = useState(true); + const mobileRenderer = useCallback(p => , []); + const rowTitleRenderer = useCallback( + r => , + [], + ); + + // TODO: mocked values + const lendPools: LendPoolDetails[] = [ + { + asset: 'BTC', + apy: 2.02, + walletBalance: 12.34, + canBeCollateral: true, + }, + { + asset: 'ETH', + apy: 2.02, + walletBalance: 12.34, + canBeCollateral: false, + }, + ]; + return ( = ({ account }) => { open={open} onClick={setOpen} > -
- setShowZeroBalances(s)} - /> -
+ setShowZeroBalances(s)} + />
( - - )} - rows={[ - // TODO: mocked values - { - asset: 'BTC', - apy: 2.02, - walletBalance: 12.34, - canBeCollateral: true, - }, - { - asset: 'ETH', - apy: 2.02, - walletBalance: 12.34, - canBeCollateral: false, - }, - ]} - mobileRenderer={p => } + rowTitle={rowTitleRenderer} + mobileRenderer={mobileRenderer} + rows={lendPools} /> ); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx index b0da59974..5f875685a 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx @@ -54,7 +54,7 @@ export const COLUMNS_CONFIG = [ className: '[&_*]:mx-auto [&_*]:space-x-2', // center head title: ( - {t(translations.aavePage.common.apy)}{' '} + {t(translations.aavePage.common.apy)} ), @@ -67,8 +67,8 @@ export const COLUMNS_CONFIG = [ align: Align.center, className: '[&_*]:mx-auto [&_*]:space-x-2', // center head title: ( - - {t(pageTranslations.common.collateral)}{' '} + + {t(pageTranslations.common.collateral)} ), cellRenderer: (position: LendPosition) => ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx index 234cff3b0..37ab576ac 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, { FC, useCallback, useState } from 'react'; import { t } from 'i18next'; @@ -9,6 +9,7 @@ import { useAccount } from '../../../../../hooks/useAccount'; import { translations } from '../../../../../locales/i18n'; import { PoolPositionStat } from '../PoolPositionStat/PoolPositionStat'; import { COLUMNS_CONFIG } from './LendPositionsList.constants'; +import { LendPosition } from './LendPositionsList.types'; import { LendPositionDetails } from './components/LendPositionDetails/LendPositionDetails'; const pageTranslations = translations.aavePage; @@ -18,6 +19,34 @@ type LendPositionsListProps = {}; export const LendPositionsList: FC = () => { const { account } = useAccount(); const [open, setOpen] = useState(true); + const [balance] = useState(100); // TODO: mocked data + const [apy] = useState(2.3); // TODO: mocked data + const [collateral] = useState(3); // TODO: mocked data + + const rowTitleRenderer = useCallback( + r => , + [], + ); + const mobileRenderer = useCallback( + p => , + [], + ); + + // TODO: mocked data + const lendPositions: LendPosition[] = [ + { + asset: 'BTC', + apy: 2.01, + balance: 12.34, + collateral: true, + }, + { + asset: 'ETH', + apy: 2.04, + balance: 12.34, + collateral: false, + }, + ]; return ( = () => { {account ? ( <>
- {/* TODO: mock data */} @@ -59,29 +87,9 @@ export const LendPositionsList: FC = () => { columns={COLUMNS_CONFIG} rowClassName="bg-gray-80" accordionClassName="bg-gray-60 border border-gray-70" - rowTitle={r => ( - - )} - rows={[ - // TODO: mocked data - { - asset: 'BTC', - apy: 2.01, - balance: 12.34, - collateral: true, - }, - { - asset: 'ETH', - apy: 2.04, - balance: 12.34, - collateral: false, - }, - ]} - mobileRenderer={p => } + rowTitle={rowTitleRenderer} + mobileRenderer={mobileRenderer} + rows={lendPositions} /> ) : ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/PoolPositionStat/PoolPositionStat.tsx b/apps/frontend/src/app/5_pages/AavePage/components/PoolPositionStat/PoolPositionStat.tsx index 3bd14700a..87aeb177b 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/PoolPositionStat/PoolPositionStat.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/PoolPositionStat/PoolPositionStat.tsx @@ -28,9 +28,9 @@ export const PoolPositionStat: React.FC = ({ )} > - {label}{' '} + {label} {labelInfo && ( - + )} diff --git a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx index 1700cb85e..23a4685fe 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react'; +import React, { FC, useState } from 'react'; import { t } from 'i18next'; @@ -21,6 +21,9 @@ type TopPanelProps = {}; export const TopPanel: FC = () => { const { account } = useAccount(); + const [netWorth] = useState(1234567.58); // TODO: mock + const [netApy] = useState(2.69); // TODO: mock + const [collateralRatio] = useState(11); // TODO: mock return (
@@ -35,22 +38,21 @@ export const TopPanel: FC = () => {
- {/* TODO: mock values */}
@@ -59,7 +61,7 @@ export const TopPanel: FC = () => {
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx index 6850774d7..02ab684fb 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx @@ -4,6 +4,7 @@ import { t } from 'i18next'; import { Accordion, Icon, Link, Paragraph } from '@sovryn/ui'; +import { EModeIcon } from '../../../../1_atoms/Icons/Icons'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; @@ -40,26 +41,7 @@ export const EModeDetails: FC = () => { {t(pageTranslations.category)} - - - - } - /> + {t(pageTranslations.ethCorrelatedCategory)}
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx index 7757eea41..7603321ea 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx @@ -13,12 +13,12 @@ import { translations } from '../../../../../locales/i18n'; const pageTranslations = translations.aaveReserveOverviewPage.topPanel; type TopPanelProps = { + asset: string; className?: string; }; -export const TopPanel: FC = ({ className }) => { +export const TopPanel: FC = ({ asset, className }) => { // TODO: Mocked data - const asset = 'BTC'; const assetName = 'Bitcoin'; const reserveSizeInM = 1234.58; const availableLiquidityM = 1234.58; @@ -60,7 +60,6 @@ export const TopPanel: FC = ({ className }) => {
- {/* TODO: mock values */} Date: Tue, 13 Aug 2024 19:32:08 +0300 Subject: [PATCH 035/116] ui fixes --- .../AssetAmountInput/AssetAmountInput.tsx | 13 ++----------- .../LendAssetDetails/LendAssetDetails.tsx | 1 + 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx b/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx index 35304e1ba..0d3abb9a6 100644 --- a/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx +++ b/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx @@ -1,12 +1,6 @@ import React, { FC, useCallback, useState } from 'react'; -import { - AmountInput, - Paragraph, - ParagraphSize, - Select, - SelectOption, -} from '@sovryn/ui'; +import { AmountInput, Paragraph, Select, SelectOption } from '@sovryn/ui'; import { Decimal, Decimalish } from '@sovryn/utils'; import { AmountRenderer } from '../AmountRenderer/AmountRenderer'; @@ -48,10 +42,7 @@ export const AssetAmountInput: FC = ({ return (
{label && ( - + {label} )} diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx index 79afdc3ff..9c15b6589 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx @@ -40,6 +40,7 @@ export const LendAssetDetails: FC = ({ pool }) => { {/* Can be collateral */} Date: Tue, 13 Aug 2024 19:37:43 +0300 Subject: [PATCH 036/116] ui fixes, usd prices --- .../AssetAmountPriceRenderer/AssetAmountPriceRenderer.tsx | 2 +- .../components/BorrowAssetDetails/BorrowAssetDetails.tsx | 8 +++++++- .../BorrowPositionDetails/BorrowPositionDetails.tsx | 6 +++++- .../components/LendAssetDetails/LendAssetDetails.tsx | 6 +++++- .../LendPositionDetails/LendPositionDetails.tsx | 6 +++++- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/apps/frontend/src/app/2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer.tsx b/apps/frontend/src/app/2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer.tsx index 8d47bc779..201798a8c 100644 --- a/apps/frontend/src/app/2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer.tsx +++ b/apps/frontend/src/app/2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer.tsx @@ -25,7 +25,7 @@ export const AssetAmountPriceRenderer: FC = ({ const usdPrice = value; return ( -
+
= ({ pool }) => { {/* Available */} } + value={ + + } />
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx index 9c219c711..feb9afa33 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx @@ -5,6 +5,7 @@ import { t } from 'i18next'; import { HelperButton, SimpleTableRow } from '@sovryn/ui'; import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; +import { AssetAmountPriceRenderer } from '../../../../../../2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer'; import { translations } from '../../../../../../../locales/i18n'; import { BorrowPosition } from '../../BorrowPositionsList.types'; import { BorrowPositionAction } from '../BorrowPositionAction/BorrowPositionAction'; @@ -23,7 +24,10 @@ export const BorrowPositionDetails: FC = ({ + } /> diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx index 9c15b6589..f02aea902 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx @@ -6,6 +6,7 @@ import { t } from 'i18next'; import { HelperButton, Icon, IconNames, SimpleTableRow } from '@sovryn/ui'; import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; +import { AssetAmountPriceRenderer } from '../../../../../../2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer'; import { translations } from '../../../../../../../locales/i18n'; import { LendPoolDetails } from '../../LendAssetsList.types'; import { LendAssetAction } from '../LendAssetAction/LendAssetAction'; @@ -22,7 +23,10 @@ export const LendAssetDetails: FC = ({ pool }) => { + } /> diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx index 250b549d2..d1bcd4c36 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx @@ -5,6 +5,7 @@ import { t } from 'i18next'; import { HelperButton, SimpleTableRow } from '@sovryn/ui'; import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; +import { AssetAmountPriceRenderer } from '../../../../../../2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer'; import { translations } from '../../../../../../../locales/i18n'; import { LendPosition } from '../../LendPositionsList.types'; import { LendPositionAction } from '../LendPositionAction/LendPositionAction'; @@ -24,7 +25,10 @@ export const LendPositionDetails: FC = ({ + } /> From 488223e4aad5c5b6775a00eb566bba4088271aa3 Mon Sep 17 00:00:00 2001 From: matzapata Date: Tue, 13 Aug 2024 20:16:36 +0300 Subject: [PATCH 037/116] Efficiency card --- .../BorrowPositionsList.tsx | 14 ++-- .../BorrowPositionDetails.tsx | 2 +- .../EfficiencyModeCard/EfficiencyModeCard.tsx | 65 +++++++++++++++++-- .../frontend/src/locales/en/translations.json | 10 ++- 4 files changed, 78 insertions(+), 13 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index 5fb93bcbe..bd2331850 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -11,6 +11,7 @@ import { PoolPositionStat } from '../PoolPositionStat/PoolPositionStat'; import { COLUMNS_CONFIG } from './BorrowPositionsList.constants'; import { BorrowPosition } from './BorrowPositionsList.types'; import { BorrowPositionDetails } from './components/BorrowPositionDetails/BorrowPositionDetails'; +import { EfficiencyModeCard } from './components/EfficiencyModeCard/EfficiencyModeCard'; const pageTranslations = translations.aavePage; @@ -52,17 +53,22 @@ export const BorrowPositionsList: FC = () => { return ( - {t(pageTranslations.borrowPositionsList.title)} - +
+ {t(pageTranslations.borrowPositionsList.title)} +
+ E-Mode + +
+
} className="bg-gray-70 px-4 py-3 rounded space-y-3 lg:bg-gray-90 lg:p-6 lg:border lg:border-gray-60" - labelClassName="justify-between h-7 flex items-center" + labelClassName="justify-between lg:h-7 flex items-center" open={open} onClick={setOpen} > {account ? ( <> +
= ({ return (
- {/* Available */} + {/* Balance */} = ({ + className, +}) => { + const [enabled] = useState(false); -export const EfficiencyModeCard: FC = () => { return ( -
- -
+ +
+ + {t(translations.aavePage.eModeCard.title)} + + + {t(translations.aavePage.eModeCard.description)}{' '} + + +
+
+ } + > +
+ + + {enabled + ? t(translations.aavePage.eModeCard.enabled) + : t(translations.aavePage.eModeCard.disabled)} + + +
+ ); }; diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index b2570292b..5b5c6d737 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -22,7 +22,9 @@ "close": "Close", "retry": "Retry", "skip": "Skip", - "back": "Back" + "back": "Back", + "enable": "Enable", + "disable": "Disable" }, "na": "N/A", "tables": { @@ -871,6 +873,12 @@ "lendApy": "Lend APY", "deposit": "Deposit", "enabled": "Enabled" + }, + "eModeCard": { + "title": "Efficiency mode (E-Mode)", + "description": "E-Mode increases your LTV for a selected category of assets up to 97%.", + "enabled": "Enabled", + "disabled": "Disabled" } }, "aaveReserveOverviewPage": { From bbfe45f1a17de1cb17c47df80a22458de3e7027f Mon Sep 17 00:00:00 2001 From: matzapata Date: Tue, 13 Aug 2024 20:52:11 +0300 Subject: [PATCH 038/116] ordering --- .../BorrowAssetsList/BorrowAssetsList.tsx | 5 ++++- .../BorrowPositionsList/BorrowPositionsList.tsx | 5 ++++- .../components/LendAssetsList/LendAssetsList.tsx | 11 ++++++----- .../LendPositionsList/LendPositionsList.tsx | 16 ++++++++++++++-- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx index 00d80ac45..76a2c27f1 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx @@ -2,7 +2,7 @@ import React, { FC, useCallback, useState } from 'react'; import { t } from 'i18next'; -import { Accordion, Table } from '@sovryn/ui'; +import { Accordion, OrderOptions, Table } from '@sovryn/ui'; import { AaveRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { translations } from '../../../../../locales/i18n'; @@ -16,6 +16,7 @@ type BorrowAssetsListProps = {}; export const BorrowAssetsList: FC = () => { const [open, setOpen] = useState(true); + const [orderOptions, setOrderOptions] = useState(); const rowTitleRenderer = useCallback( r => , @@ -59,6 +60,8 @@ export const BorrowAssetsList: FC = () => { rowTitle={rowTitleRenderer} mobileRenderer={mobileRenderer} rows={borrowPools} + orderOptions={orderOptions} + setOrderOptions={setOrderOptions} /> ); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index bd2331850..7e3a7824d 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -2,7 +2,7 @@ import React, { FC, useCallback, useState } from 'react'; import { t } from 'i18next'; -import { Accordion, Paragraph, Table } from '@sovryn/ui'; +import { Accordion, OrderOptions, Paragraph, Table } from '@sovryn/ui'; import { AaveRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { useAccount } from '../../../../../hooks/useAccount'; @@ -23,6 +23,7 @@ export const BorrowPositionsList: FC = () => { const [balance] = useState(123.45); // TODO: mock const [apy] = useState(2.05); // TODO: mock const [borrowPowerUsed] = useState(2.05); // TODO: mock + const [orderOptions, setOrderOptions] = useState(); const rowTitleRenderer = useCallback( r => , @@ -96,6 +97,8 @@ export const BorrowPositionsList: FC = () => { rowTitle={rowTitleRenderer} mobileRenderer={mobileRenderer} rows={borrowPositions} + orderOptions={orderOptions} + setOrderOptions={setOrderOptions} /> ) : ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx index 47a086621..95180ca00 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx @@ -2,7 +2,7 @@ import React, { FC, useCallback, useState } from 'react'; import { t } from 'i18next'; -import { Accordion, Checkbox, Table } from '@sovryn/ui'; +import { Accordion, Checkbox, OrderOptions, Table } from '@sovryn/ui'; import { AaveRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { translations } from '../../../../../locales/i18n'; @@ -12,13 +12,12 @@ import { LendAssetDetails } from './components/LendAssetDetails/LendAssetDetails const pageTranslations = translations.aavePage; -type LendAssetsListProps = { - account?: string; -}; +type LendAssetsListProps = {}; -export const LendAssetsList: FC = ({ account }) => { +export const LendAssetsList: FC = () => { const [open, setOpen] = useState(true); const [showZeroBalances, setShowZeroBalances] = useState(true); + const [orderOptions, setOrderOptions] = useState(); const mobileRenderer = useCallback(p => , []); const rowTitleRenderer = useCallback( @@ -68,6 +67,8 @@ export const LendAssetsList: FC = ({ account }) => { rowTitle={rowTitleRenderer} mobileRenderer={mobileRenderer} rows={lendPools} + orderOptions={orderOptions} + setOrderOptions={setOrderOptions} /> ); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx index 37ab576ac..a0ab8d43f 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx @@ -2,7 +2,13 @@ import React, { FC, useCallback, useState } from 'react'; import { t } from 'i18next'; -import { Accordion, Paragraph, Table } from '@sovryn/ui'; +import { + Accordion, + OrderDirection, + OrderOptions, + Paragraph, + Table, +} from '@sovryn/ui'; import { AaveRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { useAccount } from '../../../../../hooks/useAccount'; @@ -22,6 +28,10 @@ export const LendPositionsList: FC = () => { const [balance] = useState(100); // TODO: mocked data const [apy] = useState(2.3); // TODO: mocked data const [collateral] = useState(3); // TODO: mocked data + const [orderOptions, setOrderOptions] = useState({ + orderBy: 'balance', + orderDirection: OrderDirection.Asc, + }); const rowTitleRenderer = useCallback( r => , @@ -43,7 +53,7 @@ export const LendPositionsList: FC = () => { { asset: 'ETH', apy: 2.04, - balance: 12.34, + balance: 1.34, collateral: false, }, ]; @@ -90,6 +100,8 @@ export const LendPositionsList: FC = () => { rowTitle={rowTitleRenderer} mobileRenderer={mobileRenderer} rows={lendPositions} + orderOptions={orderOptions} + setOrderOptions={setOrderOptions} /> ) : ( From 0a1a91ea6a43e69c5a1567e71d7d7c9e6e4c4067 Mon Sep 17 00:00:00 2001 From: matzapata Date: Tue, 13 Aug 2024 20:55:08 +0300 Subject: [PATCH 039/116] fix filter 0 balances checkbox --- .../AavePage/components/LendAssetsList/LendAssetsList.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx index 95180ca00..4d14b7028 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx @@ -25,7 +25,7 @@ export const LendAssetsList: FC = () => { [], ); - // TODO: mocked values + // TODO: mocked values and filter const lendPools: LendPoolDetails[] = [ { asset: 'BTC', @@ -57,7 +57,7 @@ export const LendAssetsList: FC = () => { containerClassName="mt-2 mb-4" label={t(pageTranslations.lendAssetsList.showZeroBalances)} checked={showZeroBalances} - onChange={s => setShowZeroBalances(s)} + onClick={() => setShowZeroBalances(s => !s)} />
Date: Wed, 14 Aug 2024 00:16:01 +0300 Subject: [PATCH 040/116] pr comment --- .../2_molecules/NetworkSwitch/NetworkSwitch.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/frontend/src/app/2_molecules/NetworkSwitch/NetworkSwitch.tsx b/apps/frontend/src/app/2_molecules/NetworkSwitch/NetworkSwitch.tsx index c6c8b6bb4..75ae0575d 100644 --- a/apps/frontend/src/app/2_molecules/NetworkSwitch/NetworkSwitch.tsx +++ b/apps/frontend/src/app/2_molecules/NetworkSwitch/NetworkSwitch.tsx @@ -12,10 +12,13 @@ type NetworkSwitchProps = { export const NetworkSwitch: FC = ({ components }) => { const { currentChainId } = useChainStore(); - if (components[currentChainId] !== undefined) { - return components[currentChainId] as ReactElement; - } else { - const requiredChainId = Object.keys(components)[0]; - return ; - } + const componentToRender = React.useMemo(() => { + return ( + components[currentChainId] || ( + + ) + ); + }, [currentChainId, components]); + + return componentToRender; }; From 20b3aee54adcb53cea601052d9514c7c5b146633 Mon Sep 17 00:00:00 2001 From: matzapata Date: Wed, 14 Aug 2024 17:12:08 +0300 Subject: [PATCH 041/116] token --- .../AaveReserveOverviewPage.tsx | 2 +- .../components/TopPanel/TopPanel.tsx | 10 ++++++---- .../components/WalletOverview/WalletOverview.tsx | 7 +++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx index a8ed4e0c4..5abce6272 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx @@ -19,7 +19,7 @@ enum OverviewTab { } const AaveReserveOverviewPage: FC = () => { - const [asset] = useState('BTC'); // TODO: mock + const [asset] = useState({ symbol: 'BTC', name: 'Bitcoin' }); // TODO: mock const [activeOverviewTab, setActiveOverviewTab] = useState( OverviewTab.RESERVE, ); diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx index 7603321ea..eb9df6bfe 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx @@ -13,13 +13,15 @@ import { translations } from '../../../../../locales/i18n'; const pageTranslations = translations.aaveReserveOverviewPage.topPanel; type TopPanelProps = { - asset: string; + asset: { + name: string; + symbol: string; + }; className?: string; }; export const TopPanel: FC = ({ asset, className }) => { // TODO: Mocked data - const assetName = 'Bitcoin'; const reserveSizeInM = 1234.58; const availableLiquidityM = 1234.58; const utilizationRate = 2.79; @@ -40,13 +42,13 @@ export const TopPanel: FC = ({ asset, className }) => {
- {assetName} + {asset.name}
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/WalletOverview.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/WalletOverview.tsx index 3d06037b1..cd7e9aeb9 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/WalletOverview.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/WalletOverview.tsx @@ -23,13 +23,16 @@ import { SupplyAction } from './components/SupplyAction/SupplyAction'; const pageTranslations = translations.aaveReserveOverviewPage; type WalletOverviewProps = { - asset: string; + asset: { + symbol: string; + name: string; + }; }; export const WalletOverview: FC = ({ asset: initialAsset, }) => { - const [asset, setAsset] = useState(initialAsset); + const [asset, setAsset] = useState(initialAsset.symbol); const { account, connectWallet, pending } = useWalletConnect(); const assetBalance = Decimal.from(0); // TODO: mocked From 5cab202765dfb24d196c4b22477c40e5b6a7c277 Mon Sep 17 00:00:00 2001 From: Matias Zapata <42716817+matzapata@users.noreply.github.com> Date: Wed, 14 Aug 2024 11:25:20 -0300 Subject: [PATCH 042/116] Mocked table cards plus refactor of statistics card (#14) * mocked table cards plus refactor of statistics card * ui fixes --- apps/frontend/src/app/1_atoms/Icons/Icons.tsx | 2 +- .../StatisticsCard/StatisticsCard.tsx | 31 ++--- .../AavePage/components/TopPanel/TopPanel.tsx | 38 ++++-- .../AaveReserveOverviewPage.tsx | 6 + .../BorrowDetailsGraph/BorrowDetailsGraph.tsx | 97 +++++++++++++++ .../components/EModeDetails/EModeDetails.tsx | 21 ++-- .../InterestRateModelGraph.tsx | 50 ++++++++ .../SupplyDetailsGraph/SupplyDetailsGraph.tsx | 110 ++++++++++++++++++ .../components/TopPanel/TopPanel.tsx | 41 +++++-- .../frontend/src/locales/en/translations.json | 33 ++++++ 10 files changed, 377 insertions(+), 52 deletions(-) create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx diff --git a/apps/frontend/src/app/1_atoms/Icons/Icons.tsx b/apps/frontend/src/app/1_atoms/Icons/Icons.tsx index fe729f7be..06ce2bee9 100644 --- a/apps/frontend/src/app/1_atoms/Icons/Icons.tsx +++ b/apps/frontend/src/app/1_atoms/Icons/Icons.tsx @@ -10,7 +10,7 @@ export const LinkIcon: IconType = ( fill="none" xmlns="http://www.w3.org/2000/svg" > - + & { +type StatisticsCardProps = { label: string; + value?: ReactElement; link?: string; - helperContent?: string; - amountRendererClassName?: string; + help?: string; + className?: string; + valueClassName?: string; }; export const StatisticsCard: FC = ({ link, label, value, - helperContent, - amountRendererClassName, + help, className, - ...props }) => { return (
{label} - {helperContent && } + {help && }
- diff --git a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx index 23a4685fe..e388db243 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx @@ -11,6 +11,7 @@ import { ParagraphSize, } from '@sovryn/ui'; +import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; import { useAccount } from '../../../../../hooks/useAccount'; import { translations } from '../../../../../locales/i18n'; @@ -40,21 +41,42 @@ export const TopPanel: FC = () => {
+ ) : undefined + } />
+ ) : undefined + } + help={t(pageTranslations.netApyInfo)} /> + ) : undefined + } + help={t(pageTranslations.collateralRatioInfo)} />
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx index 5abce6272..30869ebff 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx @@ -7,7 +7,10 @@ import { Helmet } from 'react-helmet-async'; import { Tabs, TabSize, TabType } from '@sovryn/ui'; import { translations } from '../../../locales/i18n'; +import { BorrowDetailsGraph } from './components/BorrowDetailsGraph/BorrowDetailsGraph'; import { EModeDetails } from './components/EModeDetails/EModeDetails'; +import { InterestRateModelGraph } from './components/InterestRateModelGraph/InterestRateModelGraph'; +import { SupplyDetailsGraph } from './components/SupplyDetailsGraph/SupplyDetailsGraph'; import { TopPanel } from './components/TopPanel/TopPanel'; import { WalletOverview } from './components/WalletOverview/WalletOverview'; @@ -61,7 +64,10 @@ const AaveReserveOverviewPage: FC = () => { 'lg:block space-y-4 w-full', )} > + + +
{/* wallet column */} diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx new file mode 100644 index 000000000..d85ab0808 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx @@ -0,0 +1,97 @@ +import React, { FC, useState } from 'react'; + +import { t } from 'i18next'; + +import { Accordion, Link, Paragraph } from '@sovryn/ui'; + +import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; +import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; +import { useIsMobile } from '../../../../../hooks/useIsMobile'; +import { translations } from '../../../../../locales/i18n'; + +const pageTranslations = translations.aaveReserveOverviewPage.borrowDetails; + +type BorrowDetailsGraphProps = {}; + +// TODO: mocked amounts + +export const BorrowDetailsGraph: FC = () => { + const [open, setOpen] = useState(true); + const { isMobile } = useIsMobile(); + + return ( + + {t(pageTranslations.title)} + + } + className="bg-gray-90 px-4 py-3 rounded lg:p-6 border border-gray-60" + labelClassName="justify-between flex items-center " + open={open || !isMobile} + onClick={setOpen} + flatMode={!isMobile} + > +
+
+ +
+ + {t(pageTranslations.of)} + +
+
+ + {t(pageTranslations.of)} + +
+ + {/* Progress bar */} +
+
+
+
+ } + /> +
+ } + /> + } + /> +
+
+ +
+ TODO: Graph +
+ +
+ + {t(pageTranslations.collectorInfo)} + + + {/* statistics */} +
+ } + /> + } + /> +
+
+
+ + ); +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx index 02ab684fb..379acb71b 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx @@ -5,6 +5,7 @@ import { t } from 'i18next'; import { Accordion, Icon, Link, Paragraph } from '@sovryn/ui'; import { EModeIcon } from '../../../../1_atoms/Icons/Icons'; +import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; @@ -14,7 +15,7 @@ const pageTranslations = translations.aaveReserveOverviewPage.eModeDetails; type EModeDetailsProps = {}; export const EModeDetails: FC = () => { - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(true); const { isMobile } = useIsMobile(); // TODO: All this data is mocked @@ -49,27 +50,21 @@ export const EModeDetails: FC = () => {
} /> } /> } />
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx new file mode 100644 index 000000000..d4f084dbe --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx @@ -0,0 +1,50 @@ +import React, { FC, useState } from 'react'; + +import { t } from 'i18next'; + +import { Accordion, Link } from '@sovryn/ui'; + +import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; +import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; +import { useIsMobile } from '../../../../../hooks/useIsMobile'; +import { translations } from '../../../../../locales/i18n'; + +const pageTranslations = translations.aaveReserveOverviewPage.interestRateModel; + +type InterestRateModelGraphProps = {}; + +// TODO: mocked amounts + +export const InterestRateModelGraph: FC = () => { + const [open, setOpen] = useState(true); + const { isMobile } = useIsMobile(); + + return ( + + {t(pageTranslations.title)} + + } + className="bg-gray-90 px-4 py-3 rounded lg:p-6 border border-gray-60" + labelClassName="justify-between flex items-center " + open={open || !isMobile} + onClick={setOpen} + flatMode={!isMobile} + > +
+
+ } + /> + +
+ +
+ TODO: Graph +
+
+
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx new file mode 100644 index 000000000..5d25bb094 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx @@ -0,0 +1,110 @@ +import React, { FC, useState } from 'react'; + +import { t } from 'i18next'; + +import { Accordion, Icon, IconNames, Paragraph } from '@sovryn/ui'; + +import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; +import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; +import { useIsMobile } from '../../../../../hooks/useIsMobile'; +import { translations } from '../../../../../locales/i18n'; + +const pageTranslations = translations.aaveReserveOverviewPage.supplyDetails; + +type SupplyDetailsGraphProps = {}; + +// TODO: mocked amounts + +export const SupplyDetailsGraph: FC = () => { + const [open, setOpen] = useState(true); + const { isMobile } = useIsMobile(); + + return ( + + {t(pageTranslations.title)} + + } + className="bg-gray-90 px-4 py-3 rounded lg:p-6 border border-gray-60" + labelClassName="justify-between flex items-center " + open={open || !isMobile} + onClick={setOpen} + flatMode={!isMobile} + > +
+
+ +
+ + {t(pageTranslations.of)} + +
+
+ + {t(pageTranslations.of)} + +
+ + {/* Progress bar */} +
+
+
+
+ } + /> + } + /> +
+ +
+ TODO: Graph +
+ +
+ {/* heading */} +
+ + {t(pageTranslations.collateralUsage)} + +
+ + + {t(pageTranslations.canBeCollateral)} + +
+
+ + {/* statistics */} +
+ } + /> + } + /> + } + /> +
+
+ +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx index eb9df6bfe..89b23608c 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx @@ -6,6 +6,7 @@ import { t } from 'i18next'; import { Heading, Icon, Paragraph, ParagraphSize } from '@sovryn/ui'; import { LinkIcon, WalletIcon } from '../../../../1_atoms/Icons/Icons'; +import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; import { translations } from '../../../../../locales/i18n'; @@ -64,26 +65,46 @@ export const TopPanel: FC = ({ asset, className }) => { + } /> + } /> + } /> + } /> diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index 5b5c6d737..00316f769 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -922,6 +922,39 @@ "liquidationPenaltyInfo": "When a liquidation occurs, liquidators repay up to 50% of the outstanding borrowed amount on behalf of the borrower. In return, they can buy the collateral at a discount and keep the difference (liquidation penalty) as a bonus.", "body": "E-Mode increases your LTV for a selected category of assets, meaning that when E-mode is enabled, you will have higher borrowing power over assets of the same E-mode category which are defined by Aave Governance. You can enter E-Mode from your Dashboard.", "learnMore": "Learn more" + }, + "supplyDetails": { + "title": "Supply details", + "maxLtv": "Max LTV", + "maxLtvInfo": "The Maximum LTV ratio represents the maximum borrowing power of a specific collateral. For example, if a collateral has an LTV of 75%, the user can borrow up to 0.75 worth of ETH in the principal currency for every 1 ETH worth of collateral.", + "liquidationThreshold": "Liquidation threshold", + "liquidationThresholdInfo": "This represents the threshold at which a borrow position will be considered undercollateralized and subject to liquidation for each collateral. For example, if a collateral has a liquidation threshold of 80%, it means that the position will be liquidated when the debt value is worth 80% of the collateral value.", + "liquidationPenalty": "Liquidation penalty", + "liquidationPenaltyInfo": "When a liquidation occurs, liquidators repay up to 50% of the outstanding borrowed amount on behalf of the borrower. In return, they can buy the collateral at a discount and keep the difference (liquidation penalty) as a bonus.", + "collateralUsage": "Collateral usage", + "canBeCollateral": "Can be collateral", + "totalSupplied": "Total supplied", + "totalSuppliedInfo": "Asset supply is limited to a certain amount to reduce protocol exposure to the asset and to help manage risks involved.", + "of": "of", + "apy": "APY" + }, + "borrowDetails": { + "title": "Borrow details", + "totalBorrowed": "Total borrowed", + "totalBorrowedInfo": "Borrowing of this asset is limited to a certain amount to minimize liquidity pool insolvency.", + "of": "of", + "apr": "APR", + "borrowCap": "Borrow cap", + "collectorInfo": "Collector info", + "reserveFactor": "Reserve factor", + "reserveFactorInfo": "Reserve factor is a percentage of interest which goes to a collector contract that is controlled by Aave governance to promote ecosystem growth.", + "collectorContract": "Collector contract", + "viewContract": "View contract" + }, + "interestRateModel": { + "title": "Interest rate model", + "utilizationRate": "Utilization rate", + "interestRateStrategy": "Interest rate strategy" } }, "bitocracyPage": { From 6946a5f61755cc0353242964b2068b84cbcea6f0 Mon Sep 17 00:00:00 2001 From: Luciano Perez Cerra Date: Mon, 19 Aug 2024 10:11:27 -0300 Subject: [PATCH 043/116] Saf 41 charts (#16) * mocked table cards plus refactor of statistics card * ui fixes * [SAF-41] give each graph it's own chart component * [SAF-41] add htmlLegend plugin in charts; remove comments and refactor some colors moving them to constants --------- Co-authored-by: matzapata --- apps/frontend/package.json | 1 + .../BorrowDetailsGraph/BorrowDetailsGraph.tsx | 23 ++- .../components/Chart/Chart.constants.ts | 72 ++++++++++ .../components/Chart/Chart.tsx | 119 ++++++++++++++++ .../components/Chart/Chart.types.ts | 6 + .../components/Chart/Chart.utils.ts | 89 ++++++++++++ .../InterestRateModelGraph.tsx | 36 ++++- .../components/Chart/Chart.constants.ts | 45 ++++++ .../components/Chart/Chart.tsx | 134 ++++++++++++++++++ .../components/Chart/Chart.types.ts | 8 ++ .../components/Chart/Chart.utils.ts | 89 ++++++++++++ .../SupplyDetailsGraph/SupplyDetailsGraph.tsx | 22 ++- .../components/Chart/Chart.constants.ts | 72 ++++++++++ .../components/Chart/Chart.tsx | 121 ++++++++++++++++ .../components/Chart/Chart.types.ts | 6 + .../components/Chart/Chart.utils.ts | 89 ++++++++++++ .../frontend/src/locales/en/translations.json | 19 ++- 17 files changed, 931 insertions(+), 20 deletions(-) create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.constants.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.types.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.utils.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.types.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.utils.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.constants.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.types.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.utils.ts diff --git a/apps/frontend/package.json b/apps/frontend/package.json index f2d618805..59e331b0a 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -28,6 +28,7 @@ "@uniswap/permit2-sdk": "1.2.0", "bitcoin-address-validation": "2.2.1", "chart.js": "4.1.1", + "chartjs-adapter-date-fns": "^3.0.0", "classnames": "2.3.2", "date-fns": "2.30.0", "dayjs": "1.11.7", diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx index d85ab0808..d4f547fe8 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, { FC, useMemo, useState } from 'react'; import { t } from 'i18next'; @@ -8,17 +8,30 @@ import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRen import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; +import { Chart } from './components/Chart/Chart'; +import { harcodedData, LINE_COLOR } from './components/Chart/Chart.constants'; +import { MockData } from './components/Chart/Chart.types'; const pageTranslations = translations.aaveReserveOverviewPage.borrowDetails; type BorrowDetailsGraphProps = {}; -// TODO: mocked amounts - export const BorrowDetailsGraph: FC = () => { const [open, setOpen] = useState(true); const { isMobile } = useIsMobile(); + // TODO: mocked amounts + const mockData: MockData<{ x: string; y: number }> = useMemo(() => { + const data1 = harcodedData; + + return { + data1, + label1: t(pageTranslations.chart.label1), + lineColor: LINE_COLOR, + xLabels: data1.map(item => item.x), + }; + }, []); + return ( = () => { -
- TODO: Graph -
+
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.constants.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.constants.ts new file mode 100644 index 000000000..c402c345b --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.constants.ts @@ -0,0 +1,72 @@ +export const PRIMARY_COLOR = '#2C303B'; +export const TEXT_COLOR = '#f5f5f5'; +export const LINE_COLOR = '#72eadf'; +export const TICK_COLOR = '#b6bac1'; +export const BORDER_COLOR = '#484D59'; +export const GRID_COLOR = 'rgb(72 77 89)'; +export const FONT_FAMILY = 'Roboto'; +export const FONT_SIZE = 12; +export const FONT_WEIGHT = '500'; +export const DASH_GRID_TYPE = 'dash'; + +export const CUSTOM_CANVAS_BACKGROUND_COLOR = { + id: 'customCanvasBackgroundColor', + beforeDraw: (chart, options) => { + const { ctx } = chart; + ctx.save(); + ctx.globalCompositeOperation = 'destination-over'; + ctx.fillStyle = '#1e2128'; + ctx.fillRect(0, 0, chart.width, chart.height); + ctx.restore(); + }, +}; + +export const harcodedData = [ + { x: '2023-06-15T00:00:00Z', y: 2.5 }, + { x: '2023-06-16T00:00:00Z', y: 2.6 }, + { x: '2023-06-17T00:00:00Z', y: 2.7 }, + { x: '2023-06-18T00:00:00Z', y: 2.7 }, + { x: '2023-06-19T00:00:00Z', y: 2.8 }, + { x: '2023-06-20T00:00:00Z', y: 2.9 }, + { x: '2023-06-21T00:00:00Z', y: 3.0 }, + { x: '2023-06-22T00:00:00Z', y: 3.0 }, + { x: '2023-06-23T00:00:00Z', y: 3.1 }, + { x: '2023-06-24T00:00:00Z', y: 3.1 }, + { x: '2023-06-25T00:00:00Z', y: 3.2 }, + { x: '2023-06-26T00:00:00Z', y: 3.3 }, + { x: '2023-06-27T00:00:00Z', y: 3.4 }, + { x: '2023-06-28T00:00:00Z', y: 3.5 }, + { x: '2023-06-29T00:00:00Z', y: 3.7 }, + { x: '2023-06-30T00:00:00Z', y: 4.0 }, + { x: '2023-07-01T00:00:00Z', y: 4.2 }, + { x: '2023-07-02T00:00:00Z', y: 4.0 }, + { x: '2023-07-03T00:00:00Z', y: 3.9 }, + { x: '2023-07-04T00:00:00Z', y: 3.8 }, + { x: '2023-07-05T00:00:00Z', y: 3.8 }, + { x: '2023-07-06T00:00:00Z', y: 3.7 }, + { x: '2023-07-07T00:00:00Z', y: 3.7 }, + { x: '2023-07-08T00:00:00Z', y: 3.6 }, + { x: '2023-07-09T00:00:00Z', y: 3.6 }, + { x: '2023-07-10T00:00:00Z', y: 3.5 }, + { x: '2023-07-11T00:00:00Z', y: 3.5 }, + { x: '2023-07-12T00:00:00Z', y: 3.5 }, + { x: '2023-07-13T00:00:00Z', y: 3.4 }, + { x: '2023-07-14T00:00:00Z', y: 3.4 }, + { x: '2023-07-15T00:00:00Z', y: 3.3 }, + { x: '2023-07-16T00:00:00Z', y: 3.3 }, + { x: '2023-07-17T00:00:00Z', y: 3.3 }, + { x: '2023-07-18T00:00:00Z', y: 3.3 }, + { x: '2023-07-19T00:00:00Z', y: 3.3 }, + { x: '2023-07-20T00:00:00Z', y: 3.2 }, + { x: '2023-07-21T00:00:00Z', y: 3.2 }, + { x: '2023-07-22T00:00:00Z', y: 3.2 }, + { x: '2023-07-23T00:00:00Z', y: 3.2 }, + { x: '2023-07-24T00:00:00Z', y: 3.2 }, + { x: '2023-07-25T00:00:00Z', y: 3.2 }, + { x: '2023-07-26T00:00:00Z', y: 3.1 }, + { x: '2023-07-27T00:00:00Z', y: 3.1 }, + { x: '2023-07-28T00:00:00Z', y: 3.1 }, + { x: '2023-07-29T00:00:00Z', y: 3.1 }, + { x: '2023-07-30T00:00:00Z', y: 3.1 }, + { x: '2023-07-31T00:00:00Z', y: 3.1 }, +]; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx new file mode 100644 index 000000000..308b02aa7 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx @@ -0,0 +1,119 @@ +import React, { FC, useEffect, useRef } from 'react'; + +import ChartLibrary from 'chart.js/auto'; +import 'chartjs-adapter-date-fns'; + +import { + CUSTOM_CANVAS_BACKGROUND_COLOR, + GRID_COLOR, + TICK_COLOR, +} from './Chart.constants'; +import { MockData } from './Chart.types'; +import { htmlLegendPlugin } from './Chart.utils'; + +type ChartProps = { + mockData: MockData; + yLabel1: string; +}; + +export const Chart: FC = ({ mockData }) => { + const canvas = useRef(null); + const chartRef = useRef(null); + + useEffect(() => { + if (chartRef.current) { + chartRef.current.destroy(); + } + + if (!canvas.current) { + return; + } + + chartRef.current = new ChartLibrary(canvas.current, { + type: 'line', + data: { + datasets: [ + { + type: 'line', + label: mockData.label1, + data: mockData.data1, + backgroundColor: mockData.lineColor, + borderColor: mockData.lineColor, + borderWidth: 2, + fill: false, + pointRadius: 0, + }, + ], + }, + options: { + scales: { + x: { + type: 'time', + time: { + unit: 'day', + tooltipFormat: 'MMMM dd yyyy', + displayFormats: { + day: 'MMM dd', + }, + }, + title: { + display: false, + }, + ticks: { + color: TICK_COLOR, + maxTicksLimit: 4, + labelOffset: 50, + maxRotation: 0, + }, + }, + y: { + beginAtZero: false, + suggestedMin: 1, + ticks: { + color: TICK_COLOR, + callback: function (value) { + return value + '%'; + }, + maxTicksLimit: 4, + align: 'center', + }, + grid: { + color: GRID_COLOR, + lineWidth: 1, + drawOnChartArea: true, + tickBorderDash: [5, 5], + tickBorderDashOffset: 0, + }, + }, + }, + plugins: { + legend: { + display: false, + }, + }, + layout: { + padding: 20, + }, + }, + plugins: [CUSTOM_CANVAS_BACKGROUND_COLOR, htmlLegendPlugin], + }); + + return () => { + if (chartRef.current) { + chartRef.current.destroy(); + } + }; + }, [mockData]); + + return ( +
{ + e.stopPropagation(); + }} + className="lg:h-[37rem] h-64 rounded" + > + + +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.types.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.types.ts new file mode 100644 index 000000000..95e2c0bf1 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.types.ts @@ -0,0 +1,6 @@ +export type MockData = { + data1: T[]; + label1: string; + lineColor: string; + xLabels: string[]; +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.utils.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.utils.ts new file mode 100644 index 000000000..a1574762a --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.utils.ts @@ -0,0 +1,89 @@ +const getOrCreateLegendList = id => { + const legendContainer = document.getElementById(id); + if (!legendContainer) { + return; + } + let listContainer = legendContainer.querySelector('ul'); + + if (!listContainer) { + listContainer = document.createElement('ul'); + listContainer.style.display = 'flex'; + listContainer.style.flexDirection = 'row'; + listContainer.style.margin = '0'; + listContainer.style.padding = '0'; + + legendContainer.appendChild(listContainer); + } + + return listContainer; +}; + +export const htmlLegendPlugin = { + id: 'htmlLegend', + afterUpdate(chart, args, options) { + const ul = getOrCreateLegendList( + options.containerID || 'legend-container-borrow-chart', + ); + if (!ul) { + return; + } + + // Remove old legend items + while (ul.firstChild) { + ul.firstChild.remove(); + } + + // Reuse the built-in legendItems generator + const items = chart.options.plugins.legend.labels.generateLabels(chart); + + items.forEach(item => { + const li = document.createElement('li'); + li.style.alignItems = 'center'; + li.style.cursor = 'pointer'; + li.style.display = 'flex'; + li.style.flexDirection = 'row'; + li.style.marginLeft = '10px'; + + li.onclick = () => { + const { type } = chart.config; + if (type === 'pie' || type === 'doughnut') { + // Pie and doughnut charts only have a single dataset and visibility is per item + chart.toggleDataVisibility(item.index); + } else { + chart.setDatasetVisibility( + item.datasetIndex, + !chart.isDatasetVisible(item.datasetIndex), + ); + } + chart.update(); + }; + + // Color box + const boxSpan = document.createElement('span'); + boxSpan.style.background = item.fillStyle; + boxSpan.style.borderColor = item.strokeStyle; + boxSpan.style.borderWidth = item.lineWidth + 'px'; + boxSpan.style.display = 'inline-block'; + boxSpan.style.flexShrink = '0'; + boxSpan.style.height = '10px'; + boxSpan.style.marginRight = '10px'; + boxSpan.style.width = '10px'; + + // Text + const textContainer = document.createElement('p'); + //textContainer.style.color = item.fontColor; + textContainer.style.margin = '0'; + textContainer.style.padding = '0'; + textContainer.style.textDecoration = item.hidden ? 'line-through' : ''; + textContainer.style.color = 'f5f5f5'; + + const text = document.createTextNode(item.text); + textContainer.appendChild(text); + + li.appendChild(boxSpan); + li.appendChild(textContainer); + ul.className = 'mb-2'; + ul.appendChild(li); + }); + }, +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx index d4f084dbe..a8a369388 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, { FC, useMemo, useState } from 'react'; import { t } from 'i18next'; @@ -8,17 +8,33 @@ import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRen import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; +import { Chart } from './components/Chart/Chart'; +import { harcodedData, LINE_COLOR } from './components/Chart/Chart.constants'; +import { MockData } from './components/Chart/Chart.types'; const pageTranslations = translations.aaveReserveOverviewPage.interestRateModel; type InterestRateModelGraphProps = {}; -// TODO: mocked amounts - export const InterestRateModelGraph: FC = () => { const [open, setOpen] = useState(true); const { isMobile } = useIsMobile(); + // TODO: mocked amounts + const mockData: MockData<{ x: number; y: number }> = useMemo(() => { + const data = harcodedData.values; + const currentData = harcodedData.annotations.current; + const optimalData = harcodedData.annotations.optimal; + + return { + data1: data, + data2: currentData, + data3: optimalData, + label1: t(pageTranslations.chart.label1), + lineColor: LINE_COLOR, + xLabels: data.map(() => ''), + }; + }, []); return ( = () => {
-
- TODO: Graph + + {/* statistics */} +
+ } + /> + } + />
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts new file mode 100644 index 000000000..6d52f0cb9 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts @@ -0,0 +1,45 @@ +export const PRIMARY_COLOR = '#2C303B'; +export const TEXT_COLOR = '#f5f5f5'; +export const LINE_COLOR = '#f58c31'; +export const BORDER_COLOR = '#484D59'; +export const TICK_COLOR = '#b6bac1'; +export const GRID_COLOR = 'rgb(72 77 89)'; +export const FONT_FAMILY = 'Roboto'; +export const FONT_SIZE = 12; +export const FONT_WEIGHT = '500'; +export const DASH_GRID_TYPE = 'dash'; + +export const CUSTOM_CANVAS_BACKGROUND_COLOR = { + id: 'customCanvasBackgroundColor', + beforeDraw: (chart, options) => { + const { ctx } = chart; + ctx.save(); + ctx.globalCompositeOperation = 'destination-over'; + ctx.fillStyle = '#1e2128'; + ctx.fillRect(0, 0, chart.width, chart.height); + ctx.restore(); + }, +}; + +export const harcodedData = { + values: [ + { x: 0, y: 0 }, // Start of the curve + { x: 10, y: 1 }, // Small increase + { x: 20, y: 2 }, // Gradual increase + { x: 30, y: 3 }, // Gradual increase + { x: 40, y: 5 }, // Gradual increase + { x: 60, y: 9 }, // Steeper increase + { x: 92, y: 15 }, // Significant rise, matching the 78.64% mark + { x: 100, y: 100 }, // Sharp rise at the 92% mark + ], + annotations: { + current: [ + { x: 78.64, y: 0 }, + { x: 78.64, y: 50 }, // Point at the origin for the line to the x-axis + ], + optimal: [ + { x: 92, y: 0 }, + { x: 92, y: 70 }, // Point at the origin for the line to the x-axis + ], + }, +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx new file mode 100644 index 000000000..cd5a6ba9b --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx @@ -0,0 +1,134 @@ +import React, { FC, useEffect, useRef } from 'react'; + +import ChartLibrary from 'chart.js/auto'; +import 'chartjs-adapter-date-fns'; + +import { + CUSTOM_CANVAS_BACKGROUND_COLOR, + GRID_COLOR, + TICK_COLOR, +} from './Chart.constants'; +import { MockData } from './Chart.types'; +import { htmlLegendPlugin } from './Chart.utils'; + +type ChartProps = { + mockData: MockData<{ x: number; y: number }>; + yLabel1: string; +}; + +export const Chart: FC = ({ mockData }) => { + const canvas = useRef(null); + const chartRef = useRef(null); + + useEffect(() => { + if (chartRef.current) { + chartRef.current.destroy(); + } + + if (!canvas.current) { + return; + } + + chartRef.current = new ChartLibrary(canvas.current, { + type: 'line', + data: { + labels: ['0%', '50%', '100%'], + datasets: [ + { + type: 'line', + label: mockData.label1, + data: mockData.data1, + backgroundColor: mockData.lineColor, + borderColor: mockData.lineColor, + borderWidth: 2, + fill: false, + pointRadius: 0, + }, + { + label: 'Current 78.64%', + type: 'scatter', + data: mockData.data2, + backgroundColor: 'cyan', + borderColor: 'cyan', + showLine: true, + borderDash: [1, 2], + pointRadius: 0, + }, + { + label: 'Optimal 92%', + type: 'scatter', + data: mockData.data3, + backgroundColor: '#4caf51', + borderColor: '#4caf51', + showLine: true, + borderDash: [1, 2], + pointRadius: 0, + }, + ], + }, + options: { + scales: { + x: { + type: 'linear', + min: 0, + max: 100, + ticks: { + color: TICK_COLOR, + callback: function (value) { + return value + '%'; + }, + maxTicksLimit: 5, + align: 'center', + }, + }, + y: { + min: 0, + max: 100, + ticks: { + color: '#b6bac1', + callback: function (value) { + return value + '%'; + }, + maxTicksLimit: 5, + align: 'center', + }, + grid: { + color: GRID_COLOR, + lineWidth: 1, + drawOnChartArea: true, + tickBorderDash: [5, 5], + tickBorderDashOffset: 0, + }, + }, + }, + layout: { + padding: 20, + }, + plugins: { + legend: { + display: false, + }, + }, + }, + plugins: [CUSTOM_CANVAS_BACKGROUND_COLOR, htmlLegendPlugin], + }); + + return () => { + if (chartRef.current) { + chartRef.current.destroy(); + } + }; + }, [mockData]); + + return ( +
{ + e.stopPropagation(); + }} + className="lg:h-[37rem] h-64 rounded" + > + + +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.types.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.types.ts new file mode 100644 index 000000000..cc3d258c6 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.types.ts @@ -0,0 +1,8 @@ +export type MockData = { + data1: T[]; + data2: T[]; + data3: T[]; + label1: string; + lineColor: string; + xLabels: string[]; +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.utils.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.utils.ts new file mode 100644 index 000000000..40a303ffa --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.utils.ts @@ -0,0 +1,89 @@ +const getOrCreateLegendList = id => { + const legendContainer = document.getElementById(id); + if (!legendContainer) { + return; + } + let listContainer = legendContainer.querySelector('ul'); + + if (!listContainer) { + listContainer = document.createElement('ul'); + listContainer.style.display = 'flex'; + listContainer.style.flexDirection = 'row'; + listContainer.style.margin = '0'; + listContainer.style.padding = '0'; + + legendContainer.appendChild(listContainer); + } + + return listContainer; +}; + +export const htmlLegendPlugin = { + id: 'htmlLegend', + afterUpdate(chart, args, options) { + const ul = getOrCreateLegendList( + options.containerID || 'legend-container-interest-chart', + ); + if (!ul) { + return; + } + + // Remove old legend items + while (ul.firstChild) { + ul.firstChild.remove(); + } + + // Reuse the built-in legendItems generator + const items = chart.options.plugins.legend.labels.generateLabels(chart); + + items.forEach(item => { + const li = document.createElement('li'); + li.style.alignItems = 'center'; + li.style.cursor = 'pointer'; + li.style.display = 'flex'; + li.style.flexDirection = 'row'; + li.style.marginLeft = '10px'; + + li.onclick = () => { + const { type } = chart.config; + if (type === 'pie' || type === 'doughnut') { + // Pie and doughnut charts only have a single dataset and visibility is per item + chart.toggleDataVisibility(item.index); + } else { + chart.setDatasetVisibility( + item.datasetIndex, + !chart.isDatasetVisible(item.datasetIndex), + ); + } + chart.update(); + }; + + // Color box + const boxSpan = document.createElement('span'); + boxSpan.style.background = item.fillStyle; + boxSpan.style.borderColor = item.strokeStyle; + boxSpan.style.borderWidth = item.lineWidth + 'px'; + boxSpan.style.display = 'inline-block'; + boxSpan.style.flexShrink = '0'; + boxSpan.style.height = '10px'; + boxSpan.style.marginRight = '10px'; + boxSpan.style.width = '10px'; + + // Text + const textContainer = document.createElement('p'); + //textContainer.style.color = item.fontColor; + textContainer.style.margin = '0'; + textContainer.style.padding = '0'; + textContainer.style.textDecoration = item.hidden ? 'line-through' : ''; + textContainer.style.color = 'f5f5f5'; + + const text = document.createTextNode(item.text); + textContainer.appendChild(text); + + li.appendChild(boxSpan); + li.appendChild(textContainer); + ul.className = 'mb-2'; + ul.appendChild(li); + }); + }, +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx index 5d25bb094..daf50235a 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, { FC, useMemo, useState } from 'react'; import { t } from 'i18next'; @@ -8,16 +8,28 @@ import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRen import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; +import { Chart } from './components/Chart/Chart'; +import { harcodedData, LINE_COLOR } from './components/Chart/Chart.constants'; +import { MockData } from './components/Chart/Chart.types'; const pageTranslations = translations.aaveReserveOverviewPage.supplyDetails; type SupplyDetailsGraphProps = {}; -// TODO: mocked amounts - export const SupplyDetailsGraph: FC = () => { const [open, setOpen] = useState(true); const { isMobile } = useIsMobile(); + // TODO: mocked amounts + const mockData: MockData<{ x: string; y: number }> = useMemo(() => { + const data1 = harcodedData; + + return { + data1, + label1: t(pageTranslations.chart.label1), + lineColor: LINE_COLOR, + xLabels: data1.map(item => item.x), + }; + }, []); return ( = () => { /> -
- TODO: Graph -
+
{/* heading */} diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.constants.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.constants.ts new file mode 100644 index 000000000..6d3ad4980 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.constants.ts @@ -0,0 +1,72 @@ +export const PRIMARY_COLOR = '#2C303B'; +export const TEXT_COLOR = '#f5f5f5'; +export const LINE_COLOR = '#f58c31'; +export const TICK_COLOR = '#b6bac1'; +export const BORDER_COLOR = '#484D59'; +export const GRID_COLOR = 'rgb(72 77 89)'; +export const FONT_FAMILY = 'Roboto'; +export const FONT_SIZE = 12; +export const FONT_WEIGHT = '500'; +export const DASH_GRID_TYPE = 'dash'; + +export const CUSTOM_CANVAS_BACKGROUND_COLOR = { + id: 'customCanvasBackgroundColor', + beforeDraw: (chart, options) => { + const { ctx } = chart; + ctx.save(); + ctx.globalCompositeOperation = 'destination-over'; + ctx.fillStyle = '#1e2128'; + ctx.fillRect(0, 0, chart.width, chart.height); + ctx.restore(); + }, +}; + +export const harcodedData = [ + { x: '2023-06-15T00:00:00Z', y: 2.5 }, + { x: '2023-06-16T00:00:00Z', y: 2.6 }, + { x: '2023-06-17T00:00:00Z', y: 2.7 }, + { x: '2023-06-18T00:00:00Z', y: 2.7 }, + { x: '2023-06-19T00:00:00Z', y: 2.8 }, + { x: '2023-06-20T00:00:00Z', y: 2.9 }, + { x: '2023-06-21T00:00:00Z', y: 3.0 }, + { x: '2023-06-22T00:00:00Z', y: 3.0 }, + { x: '2023-06-23T00:00:00Z', y: 3.1 }, + { x: '2023-06-24T00:00:00Z', y: 3.1 }, + { x: '2023-06-25T00:00:00Z', y: 3.2 }, + { x: '2023-06-26T00:00:00Z', y: 3.3 }, + { x: '2023-06-27T00:00:00Z', y: 3.4 }, + { x: '2023-06-28T00:00:00Z', y: 3.5 }, + { x: '2023-06-29T00:00:00Z', y: 3.7 }, + { x: '2023-06-30T00:00:00Z', y: 4.0 }, + { x: '2023-07-01T00:00:00Z', y: 4.2 }, + { x: '2023-07-02T00:00:00Z', y: 4.0 }, + { x: '2023-07-03T00:00:00Z', y: 3.9 }, + { x: '2023-07-04T00:00:00Z', y: 3.8 }, + { x: '2023-07-05T00:00:00Z', y: 3.8 }, + { x: '2023-07-06T00:00:00Z', y: 3.7 }, + { x: '2023-07-07T00:00:00Z', y: 3.7 }, + { x: '2023-07-08T00:00:00Z', y: 3.6 }, + { x: '2023-07-09T00:00:00Z', y: 3.6 }, + { x: '2023-07-10T00:00:00Z', y: 3.5 }, + { x: '2023-07-11T00:00:00Z', y: 3.5 }, + { x: '2023-07-12T00:00:00Z', y: 3.5 }, + { x: '2023-07-13T00:00:00Z', y: 3.4 }, + { x: '2023-07-14T00:00:00Z', y: 3.4 }, + { x: '2023-07-15T00:00:00Z', y: 3.3 }, + { x: '2023-07-16T00:00:00Z', y: 3.3 }, + { x: '2023-07-17T00:00:00Z', y: 3.3 }, + { x: '2023-07-18T00:00:00Z', y: 3.3 }, + { x: '2023-07-19T00:00:00Z', y: 3.3 }, + { x: '2023-07-20T00:00:00Z', y: 3.2 }, + { x: '2023-07-21T00:00:00Z', y: 3.2 }, + { x: '2023-07-22T00:00:00Z', y: 3.2 }, + { x: '2023-07-23T00:00:00Z', y: 3.2 }, + { x: '2023-07-24T00:00:00Z', y: 3.2 }, + { x: '2023-07-25T00:00:00Z', y: 3.2 }, + { x: '2023-07-26T00:00:00Z', y: 3.1 }, + { x: '2023-07-27T00:00:00Z', y: 3.1 }, + { x: '2023-07-28T00:00:00Z', y: 3.1 }, + { x: '2023-07-29T00:00:00Z', y: 3.1 }, + { x: '2023-07-30T00:00:00Z', y: 3.1 }, + { x: '2023-07-31T00:00:00Z', y: 3.1 }, +]; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.tsx new file mode 100644 index 000000000..9cb338322 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.tsx @@ -0,0 +1,121 @@ +import React, { FC, useEffect, useRef } from 'react'; + +import ChartLibrary from 'chart.js/auto'; +import 'chartjs-adapter-date-fns'; + +import { + CUSTOM_CANVAS_BACKGROUND_COLOR, + GRID_COLOR, + TICK_COLOR, +} from './Chart.constants'; +import { MockData } from './Chart.types'; +import { htmlLegendPlugin } from './Chart.utils'; + +type ChartProps = { + mockData: MockData; + yLabel1: string; +}; + +export const Chart: FC = ({ mockData }) => { + const canvas = useRef(null); + const chartRef = useRef(null); + + useEffect(() => { + if (chartRef.current) { + chartRef.current.destroy(); + } + + if (!canvas.current) { + return; + } + + chartRef.current = new ChartLibrary(canvas.current, { + type: 'line', + data: { + datasets: [ + { + type: 'line', + label: mockData.label1, + data: mockData.data1, + backgroundColor: 'rgba(245, 140, 49, 1)', + borderColor: mockData.lineColor, + borderWidth: 2, + fill: false, + pointRadius: 0, + }, + ], + }, + options: { + scales: { + x: { + type: 'time', + time: { + unit: 'day', + tooltipFormat: 'MMMM dd yyyy', + displayFormats: { + day: 'MMM dd', + }, + }, + title: { + display: false, + text: 'Date', + color: '#ffffff', + }, + ticks: { + color: TICK_COLOR, + maxTicksLimit: 4, + labelOffset: 50, + maxRotation: 0, + }, + }, + y: { + beginAtZero: false, + suggestedMin: 1, + ticks: { + color: TICK_COLOR, + callback: function (value) { + return value + '%'; + }, + maxTicksLimit: 4, + align: 'center', + }, + grid: { + color: GRID_COLOR, + lineWidth: 1, + drawOnChartArea: true, + tickBorderDash: [5, 5], + tickBorderDashOffset: 0, + }, + }, + }, + plugins: { + legend: { + display: false, + }, + }, + layout: { + padding: 20, + }, + }, + plugins: [CUSTOM_CANVAS_BACKGROUND_COLOR, htmlLegendPlugin], + }); + + return () => { + if (chartRef.current) { + chartRef.current.destroy(); + } + }; + }, [mockData]); + + return ( +
{ + e.stopPropagation(); + }} + className="lg:h-[37rem] h-64 rounded" + > + + +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.types.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.types.ts new file mode 100644 index 000000000..95e2c0bf1 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.types.ts @@ -0,0 +1,6 @@ +export type MockData = { + data1: T[]; + label1: string; + lineColor: string; + xLabels: string[]; +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.utils.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.utils.ts new file mode 100644 index 000000000..6df318eb7 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.utils.ts @@ -0,0 +1,89 @@ +const getOrCreateLegendList = id => { + const legendContainer = document.getElementById(id); + if (!legendContainer) { + return; + } + let listContainer = legendContainer.querySelector('ul'); + + if (!listContainer) { + listContainer = document.createElement('ul'); + listContainer.style.display = 'flex'; + listContainer.style.flexDirection = 'row'; + listContainer.style.margin = '0'; + listContainer.style.padding = '0'; + + legendContainer.appendChild(listContainer); + } + + return listContainer; +}; + +export const htmlLegendPlugin = { + id: 'htmlLegend', + afterUpdate(chart, args, options) { + const ul = getOrCreateLegendList( + options.containerID || 'legend-container-supply-chart', + ); + if (!ul) { + return; + } + + // Remove old legend items + while (ul.firstChild) { + ul.firstChild.remove(); + } + + // Reuse the built-in legendItems generator + const items = chart.options.plugins.legend.labels.generateLabels(chart); + + items.forEach(item => { + const li = document.createElement('li'); + li.style.alignItems = 'center'; + li.style.cursor = 'pointer'; + li.style.display = 'flex'; + li.style.flexDirection = 'row'; + li.style.marginLeft = '10px'; + + li.onclick = () => { + const { type } = chart.config; + if (type === 'pie' || type === 'doughnut') { + // Pie and doughnut charts only have a single dataset and visibility is per item + chart.toggleDataVisibility(item.index); + } else { + chart.setDatasetVisibility( + item.datasetIndex, + !chart.isDatasetVisible(item.datasetIndex), + ); + } + chart.update(); + }; + + // Color box + const boxSpan = document.createElement('span'); + boxSpan.style.background = item.fillStyle; + boxSpan.style.borderColor = item.strokeStyle; + boxSpan.style.borderWidth = item.lineWidth + 'px'; + boxSpan.style.display = 'inline-block'; + boxSpan.style.flexShrink = '0'; + boxSpan.style.height = '10px'; + boxSpan.style.marginRight = '10px'; + boxSpan.style.width = '10px'; + + // Text + const textContainer = document.createElement('p'); + //textContainer.style.color = item.fontColor; + textContainer.style.margin = '0'; + textContainer.style.padding = '0'; + textContainer.style.textDecoration = item.hidden ? 'line-through' : ''; + textContainer.style.color = 'f5f5f5'; + + const text = document.createTextNode(item.text); + textContainer.appendChild(text); + + li.appendChild(boxSpan); + li.appendChild(textContainer); + ul.className = 'mb-2'; + ul.appendChild(li); + }); + }, +}; diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index 00316f769..46893d98f 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -936,7 +936,10 @@ "totalSupplied": "Total supplied", "totalSuppliedInfo": "Asset supply is limited to a certain amount to reduce protocol exposure to the asset and to help manage risks involved.", "of": "of", - "apy": "APY" + "apy": "APY", + "chart": { + "label1": "Supply APR" + } }, "borrowDetails": { "title": "Borrow details", @@ -949,12 +952,22 @@ "reserveFactor": "Reserve factor", "reserveFactorInfo": "Reserve factor is a percentage of interest which goes to a collector contract that is controlled by Aave governance to promote ecosystem growth.", "collectorContract": "Collector contract", - "viewContract": "View contract" + "viewContract": "View contract", + "chart": { + "label1": "Borrow APR, variable" + } }, "interestRateModel": { "title": "Interest rate model", "utilizationRate": "Utilization rate", - "interestRateStrategy": "Interest rate strategy" + "interestRateStrategy": "Interest rate strategy", + "reserveFactor": "Reserve factor", + "reserveFactorInfo": "Reserve factor is a percentage of interest which goes to a collector contract that is controlled by Aave governance to promote ecosystem growth.", + "collectorContract": "Collector contract", + "viewContract": "View contract", + "chart": { + "label1": "Borrow APR, variable" + } } }, "bitocracyPage": { From 8359dbbc33ebdea7b2b4d3b21ff4e5dc39bc3a05 Mon Sep 17 00:00:00 2001 From: Christian Escalante Date: Tue, 20 Aug 2024 08:57:46 -0300 Subject: [PATCH 044/116] removed view transactions button (out of scope) --- .../AavePage/components/TopPanel/TopPanel.tsx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx index e388db243..e8846454d 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx @@ -2,14 +2,7 @@ import React, { FC, useState } from 'react'; import { t } from 'i18next'; -import { - Button, - ButtonSize, - ButtonStyle, - Heading, - Paragraph, - ParagraphSize, -} from '@sovryn/ui'; +import { Heading, Paragraph, ParagraphSize } from '@sovryn/ui'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; @@ -80,14 +73,6 @@ export const TopPanel: FC = () => { />
- -
-
); From dd2393147623fb59832afe9e64483dd50d3e9752 Mon Sep 17 00:00:00 2001 From: matzapata Date: Tue, 20 Aug 2024 16:59:48 +0300 Subject: [PATCH 045/116] fix breakpoints --- apps/frontend/src/app/5_pages/AavePage/AavePage.tsx | 10 +++++----- .../AaveReserveOverviewPage.tsx | 6 +++--- .../BorrowDetailsGraph/components/Chart/Chart.tsx | 2 +- .../InterestRateModelGraph/components/Chart/Chart.tsx | 2 +- .../SupplyDetailsGraph/components/Chart/Chart.tsx | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx b/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx index baa74f0bb..18dd1374c 100644 --- a/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx @@ -31,10 +31,10 @@ const AavePage: FC = () => { -
+
{/* Tab selector */} { /> {/* Lending and borrowing columns */} -
+
@@ -69,7 +69,7 @@ const AavePage: FC = () => {
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx index 30869ebff..b2fef9581 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx @@ -57,11 +57,11 @@ const AaveReserveOverviewPage: FC = () => { /> {/* reserve graphics columns */} -
+
@@ -74,7 +74,7 @@ const AaveReserveOverviewPage: FC = () => {
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx index 308b02aa7..0089fe504 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx @@ -110,7 +110,7 @@ export const Chart: FC = ({ mockData }) => { onClick={e => { e.stopPropagation(); }} - className="lg:h-[37rem] h-64 rounded" + className="w-full rounded" > diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx index cd5a6ba9b..00df99364 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx @@ -125,7 +125,7 @@ export const Chart: FC = ({ mockData }) => { onClick={e => { e.stopPropagation(); }} - className="lg:h-[37rem] h-64 rounded" + className="w-full rounded" > diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.tsx index 9cb338322..30449d9f0 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.tsx @@ -112,7 +112,7 @@ export const Chart: FC = ({ mockData }) => { onClick={e => { e.stopPropagation(); }} - className="lg:h-[37rem] h-64 rounded" + className="w-full rounded" > From 14878b4edb4ced1f4e0adb02e4799c816cd99910 Mon Sep 17 00:00:00 2001 From: Luciano Perez Cerra Date: Tue, 20 Aug 2024 15:28:32 -0300 Subject: [PATCH 046/116] [SAF-41] fix color constant and background for mobile vs desktop views (#20) * [SAF-41] fix color constant and background for mobile vs desktop views * [SAF-41] use theme variables for colors and breakpoint usage for better consistency * Apply suggestions from code review Co-authored-by: Juan Dahl --------- Co-authored-by: Juan Dahl --- .../AaveReserveOverviewPage.tsx | 5 ++++- .../BorrowDetailsGraph/BorrowDetailsGraph.tsx | 5 +++-- .../components/Chart/Chart.constants.ts | 20 +++++++++--------- .../components/Chart/Chart.tsx | 6 ++---- .../InterestRateModelGraph.tsx | 5 +++-- .../components/Chart/Chart.constants.ts | 20 +++++++++--------- .../components/Chart/Chart.tsx | 16 +++++++------- .../SupplyDetailsGraph/SupplyDetailsGraph.tsx | 5 +++-- .../components/Chart/Chart.constants.ts | 21 ++++++++++--------- .../components/Chart/Chart.tsx | 6 ++---- .../frontend/src/locales/en/translations.json | 3 ++- 11 files changed, 58 insertions(+), 54 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx index b2fef9581..1c9418098 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import { t } from 'i18next'; import { Helmet } from 'react-helmet-async'; -import { Tabs, TabSize, TabType } from '@sovryn/ui'; +import { Paragraph, Tabs, TabSize, TabType } from '@sovryn/ui'; import { translations } from '../../../locales/i18n'; import { BorrowDetailsGraph } from './components/BorrowDetailsGraph/BorrowDetailsGraph'; @@ -34,6 +34,9 @@ const AaveReserveOverviewPage: FC = () => { + + {t(pageTranslations.reserveStatusTab.fullTitle)} +
= () => { return { data1, label1: t(pageTranslations.chart.label1), - lineColor: LINE_COLOR, + lineColor: theme.colors.positive, xLabels: data1.map(item => item.x), }; }, []); diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.constants.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.constants.ts index c402c345b..7156e654a 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.constants.ts +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.constants.ts @@ -1,21 +1,21 @@ -export const PRIMARY_COLOR = '#2C303B'; -export const TEXT_COLOR = '#f5f5f5'; -export const LINE_COLOR = '#72eadf'; +import { theme } from '@sovryn/tailwindcss-config'; + +export const GRID_COLOR = '#484d59'; export const TICK_COLOR = '#b6bac1'; -export const BORDER_COLOR = '#484D59'; -export const GRID_COLOR = 'rgb(72 77 89)'; -export const FONT_FAMILY = 'Roboto'; -export const FONT_SIZE = 12; -export const FONT_WEIGHT = '500'; -export const DASH_GRID_TYPE = 'dash'; +const SM_BREAKPOINT = parseInt(theme.screens.sm, 10) || 576; export const CUSTOM_CANVAS_BACKGROUND_COLOR = { id: 'customCanvasBackgroundColor', beforeDraw: (chart, options) => { const { ctx } = chart; + const windowWidth = window.innerWidth; ctx.save(); ctx.globalCompositeOperation = 'destination-over'; - ctx.fillStyle = '#1e2128'; + if (windowWidth < SM_BREAKPOINT) { + ctx.fillStyle = theme.colors['gray-90']; + } else { + ctx.fillStyle = theme.colors['gray-80']; + } ctx.fillRect(0, 0, chart.width, chart.height); ctx.restore(); }, diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx index 0089fe504..e16a4b200 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx @@ -79,10 +79,8 @@ export const Chart: FC = ({ mockData }) => { }, grid: { color: GRID_COLOR, - lineWidth: 1, - drawOnChartArea: true, tickBorderDash: [5, 5], - tickBorderDashOffset: 0, + drawTicks: false, }, }, }, @@ -110,7 +108,7 @@ export const Chart: FC = ({ mockData }) => { onClick={e => { e.stopPropagation(); }} - className="w-full rounded" + className="lg:p-6 lg:bg-gray-80" > diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx index a8a369388..12a4bcb10 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx @@ -2,6 +2,7 @@ import React, { FC, useMemo, useState } from 'react'; import { t } from 'i18next'; +import { theme } from '@sovryn/tailwindcss-config'; import { Accordion, Link } from '@sovryn/ui'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; @@ -9,7 +10,7 @@ import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/Statistic import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; import { Chart } from './components/Chart/Chart'; -import { harcodedData, LINE_COLOR } from './components/Chart/Chart.constants'; +import { harcodedData } from './components/Chart/Chart.constants'; import { MockData } from './components/Chart/Chart.types'; const pageTranslations = translations.aaveReserveOverviewPage.interestRateModel; @@ -31,7 +32,7 @@ export const InterestRateModelGraph: FC = () => { data2: currentData, data3: optimalData, label1: t(pageTranslations.chart.label1), - lineColor: LINE_COLOR, + lineColor: theme.colors['primary-30'], xLabels: data.map(() => ''), }; }, []); diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts index 6d52f0cb9..241c37c9b 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts @@ -1,21 +1,21 @@ -export const PRIMARY_COLOR = '#2C303B'; -export const TEXT_COLOR = '#f5f5f5'; -export const LINE_COLOR = '#f58c31'; -export const BORDER_COLOR = '#484D59'; +import { theme } from '@sovryn/tailwindcss-config'; + +export const GRID_COLOR = '#484d59'; export const TICK_COLOR = '#b6bac1'; -export const GRID_COLOR = 'rgb(72 77 89)'; -export const FONT_FAMILY = 'Roboto'; -export const FONT_SIZE = 12; -export const FONT_WEIGHT = '500'; -export const DASH_GRID_TYPE = 'dash'; +const SM_BREAKPOINT = parseInt(theme.screens.sm, 10) || 576; export const CUSTOM_CANVAS_BACKGROUND_COLOR = { id: 'customCanvasBackgroundColor', beforeDraw: (chart, options) => { const { ctx } = chart; + const windowWidth = window.innerWidth; ctx.save(); ctx.globalCompositeOperation = 'destination-over'; - ctx.fillStyle = '#1e2128'; + if (windowWidth < SM_BREAKPOINT) { + ctx.fillStyle = theme.colors['gray-90']; + } else { + ctx.fillStyle = theme.colors['gray-80']; + } ctx.fillRect(0, 0, chart.width, chart.height); ctx.restore(); }, diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx index 00df99364..54892247a 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx @@ -3,6 +3,8 @@ import React, { FC, useEffect, useRef } from 'react'; import ChartLibrary from 'chart.js/auto'; import 'chartjs-adapter-date-fns'; +import { theme } from '@sovryn/tailwindcss-config'; + import { CUSTOM_CANVAS_BACKGROUND_COLOR, GRID_COLOR, @@ -48,8 +50,8 @@ export const Chart: FC = ({ mockData }) => { label: 'Current 78.64%', type: 'scatter', data: mockData.data2, - backgroundColor: 'cyan', - borderColor: 'cyan', + borderColor: theme.colors.positive, + backgroundColor: theme.colors.positive, showLine: true, borderDash: [1, 2], pointRadius: 0, @@ -58,8 +60,8 @@ export const Chart: FC = ({ mockData }) => { label: 'Optimal 92%', type: 'scatter', data: mockData.data3, - backgroundColor: '#4caf51', - borderColor: '#4caf51', + borderColor: theme.colors.success, + backgroundColor: theme.colors.success, showLine: true, borderDash: [1, 2], pointRadius: 0, @@ -94,10 +96,8 @@ export const Chart: FC = ({ mockData }) => { }, grid: { color: GRID_COLOR, - lineWidth: 1, - drawOnChartArea: true, tickBorderDash: [5, 5], - tickBorderDashOffset: 0, + drawTicks: false, }, }, }, @@ -125,7 +125,7 @@ export const Chart: FC = ({ mockData }) => { onClick={e => { e.stopPropagation(); }} - className="w-full rounded" + className="lg:p-6 lg:bg-gray-80" > diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx index daf50235a..86e59fa50 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx @@ -2,6 +2,7 @@ import React, { FC, useMemo, useState } from 'react'; import { t } from 'i18next'; +import { theme } from '@sovryn/tailwindcss-config'; import { Accordion, Icon, IconNames, Paragraph } from '@sovryn/ui'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; @@ -9,7 +10,7 @@ import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/Statistic import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; import { Chart } from './components/Chart/Chart'; -import { harcodedData, LINE_COLOR } from './components/Chart/Chart.constants'; +import { harcodedData } from './components/Chart/Chart.constants'; import { MockData } from './components/Chart/Chart.types'; const pageTranslations = translations.aaveReserveOverviewPage.supplyDetails; @@ -26,7 +27,7 @@ export const SupplyDetailsGraph: FC = () => { return { data1, label1: t(pageTranslations.chart.label1), - lineColor: LINE_COLOR, + lineColor: theme.colors['primary-30'], xLabels: data1.map(item => item.x), }; }, []); diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.constants.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.constants.ts index 6d3ad4980..5a615f6bb 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.constants.ts +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.constants.ts @@ -1,21 +1,22 @@ -export const PRIMARY_COLOR = '#2C303B'; -export const TEXT_COLOR = '#f5f5f5'; -export const LINE_COLOR = '#f58c31'; +import { theme } from '@sovryn/tailwindcss-config'; + +export const GRID_COLOR = '#484d59'; export const TICK_COLOR = '#b6bac1'; -export const BORDER_COLOR = '#484D59'; -export const GRID_COLOR = 'rgb(72 77 89)'; -export const FONT_FAMILY = 'Roboto'; -export const FONT_SIZE = 12; -export const FONT_WEIGHT = '500'; -export const DASH_GRID_TYPE = 'dash'; +const SM_BREAKPOINT = parseInt(theme.screens.sm, 10); export const CUSTOM_CANVAS_BACKGROUND_COLOR = { id: 'customCanvasBackgroundColor', beforeDraw: (chart, options) => { const { ctx } = chart; + const windowWidth = window.innerWidth; ctx.save(); ctx.globalCompositeOperation = 'destination-over'; - ctx.fillStyle = '#1e2128'; + + if (windowWidth < SM_BREAKPOINT) { + ctx.fillStyle = theme.colors['gray-90']; + } else { + ctx.fillStyle = theme.colors['gray-80']; + } ctx.fillRect(0, 0, chart.width, chart.height); ctx.restore(); }, diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.tsx index 30449d9f0..aab0f7505 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/components/Chart/Chart.tsx @@ -81,10 +81,8 @@ export const Chart: FC = ({ mockData }) => { }, grid: { color: GRID_COLOR, - lineWidth: 1, - drawOnChartArea: true, tickBorderDash: [5, 5], - tickBorderDashOffset: 0, + drawTicks: false, }, }, }, @@ -112,7 +110,7 @@ export const Chart: FC = ({ mockData }) => { onClick={e => { e.stopPropagation(); }} - className="w-full rounded" + className="lg:p-6 lg:bg-gray-80" > diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index 46893d98f..65ac2d9f6 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -894,7 +894,8 @@ "oraclePrice": "Oracle price" }, "reserveStatusTab": { - "title": "Reserve status" + "title": "Reserve status", + "fullTitle": "Reserve status & configuration" }, "yourWalletTab": { "title": "Your wallet", From 704e86c23be26af4d26401eb6a8b34f51018f48a Mon Sep 17 00:00:00 2001 From: Matias Zapata <42716817+matzapata@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:54:28 -0300 Subject: [PATCH 047/116] Dashboard pool data integration (#15) (#22) * dashboard tables * emode usd and borrowing powers * fix asset details * pr comments * unit fixes --- apps/frontend/package.json | 3 + .../AssetAmountPriceRenderer.tsx | 19 +- .../StatisticsCard/StatisticsCard.tsx | 4 +- .../src/app/5_pages/AavePage/AavePage.tsx | 84 ++++++- .../BorrowAssetsList.constants.tsx | 23 +- .../BorrowAssetsList/BorrowAssetsList.tsx | 34 ++- .../BorrowAssetsList.types.tsx | 8 +- .../BorrowAssetDetails/BorrowAssetDetails.tsx | 21 +- .../BorrowPositionsList.constants.tsx | 10 +- .../BorrowPositionsList.tsx | 58 +++-- .../BorrowPositionsList.types.tsx | 10 +- .../BorrowPositionDetails.tsx | 7 +- .../EfficiencyModeCard/EfficiencyModeCard.tsx | 6 +- .../LendAssetsList.constants.tsx | 5 +- .../LendAssetsList/LendAssetsList.tsx | 32 +-- .../LendAssetsList/LendAssetsList.types.tsx | 5 +- .../components/AssetBalance/AssetBalance.tsx | 24 ++ .../LendAssetDetails/LendAssetDetails.tsx | 9 +- .../LendPositionsList.constants.tsx | 5 +- .../LendPositionsList/LendPositionsList.tsx | 49 ++-- .../LendPositionsList.types.tsx | 7 +- .../LendPositionDetails.tsx | 3 +- .../AavePage/components/TopPanel/TopPanel.tsx | 32 ++- .../components/BorrowAction/BorrowAction.tsx | 2 + .../components/SupplyAction/SupplyAction.tsx | 3 +- apps/frontend/src/constants/aave.ts | 12 + .../src/hooks/useAaveReservesData.tsx | 68 +++++ .../src/hooks/useAaveUserReservesData.tsx | 81 ++++++ .../frontend/src/locales/en/translations.json | 3 +- apps/frontend/src/utils/aave.ts | 238 ++++++++++++++++++ .../src/utils/graphql/bob/generated.tsx | 1 - .../src/utils/graphql/mynt/generated.tsx | 1 - .../src/utils/graphql/rsk/generated.tsx | 1 - .../src/utils/graphql/zero/generated.tsx | 1 - yarn.lock | 28 ++- 35 files changed, 714 insertions(+), 183 deletions(-) create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/AssetBalance/AssetBalance.tsx create mode 100644 apps/frontend/src/constants/aave.ts create mode 100644 apps/frontend/src/hooks/useAaveReservesData.tsx create mode 100644 apps/frontend/src/hooks/useAaveUserReservesData.tsx create mode 100644 apps/frontend/src/utils/aave.ts diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 59e331b0a..2d4ad9970 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -4,6 +4,8 @@ "homepage": ".", "private": true, "dependencies": { + "@aave/contract-helpers": "^1.29.1", + "@aave/math-utils": "^1.29.1", "@apollo/client": "3.7.1", "@apollo/react-hooks": "4.0.0", "@loadable/component": "5.15.2", @@ -55,6 +57,7 @@ "react-slider": "2.0.6", "react-timer-hook": "3.0.7", "reactjs-localstorage": "1.0.1", + "reflect-metadata": "^0.2.2", "remark-gfm": "3.0.1", "rxjs": "7.5.6", "sanitize-html": "2.11.0", diff --git a/apps/frontend/src/app/2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer.tsx b/apps/frontend/src/app/2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer.tsx index 201798a8c..e77f89d7e 100644 --- a/apps/frontend/src/app/2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer.tsx +++ b/apps/frontend/src/app/2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer.tsx @@ -7,29 +7,34 @@ import { Decimalish } from '@sovryn/utils'; import { AmountRenderer } from '../AmountRenderer/AmountRenderer'; type AssetAmountPriceRendererProps = { - value: Decimalish; asset: string; + value: Decimalish; + valueUSD: Decimalish; className?: string; valueClassName?: string; priceClassName?: string; }; export const AssetAmountPriceRenderer: FC = ({ - value, asset, + value, + valueUSD, className, valueClassName, priceClassName, }) => { - // TODO: get price in usd - const usdPrice = value; - return (
- + = ({ )} ) : ( - {t(translations.common.na)} + --- )}
diff --git a/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx b/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx index baa74f0bb..14e11c8a9 100644 --- a/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, { FC, useMemo, useState } from 'react'; import classNames from 'classnames'; import { t } from 'i18next'; @@ -12,6 +12,13 @@ import { BorrowPositionsList } from './components/BorrowPositionsList/BorrowPosi import { LendAssetsList } from './components/LendAssetsList/LendAssetsList'; import { LendPositionsList } from './components/LendPositionsList/LendPositionsList'; import { TopPanel } from './components/TopPanel/TopPanel'; +import { useAaveUserReservesData } from '../../../hooks/useAaveUserReservesData'; +import { LendPosition } from './components/LendPositionsList/LendPositionsList.types'; +import { BorrowPosition } from './components/BorrowPositionsList/BorrowPositionsList.types'; +import { useAaveReservesData } from '../../../hooks/useAaveReservesData'; +import { Decimal } from '@sovryn/utils'; +import { BorrowPoolDetails } from './components/BorrowAssetsList/BorrowAssetsList.types'; +import { LendPoolDetails } from './components/LendAssetsList/LendAssetsList.types'; const pageTranslations = translations.aavePage; @@ -21,15 +28,69 @@ enum ActiveTab { } const AavePage: FC = () => { + const { reserves } = useAaveReservesData(); + const { userReservesSummary } = useAaveUserReservesData(); const [activeTab, setActiveTab] = useState(ActiveTab.LEND); + const lendPositions: LendPosition[] = useMemo(() => { + if (!userReservesSummary) return []; + return userReservesSummary.suppliedAssets.map(s => ({ + asset: s.asset, + apy: s.apy, + supplied: s.supplied, + suppliedUSD: s.suppliedUSD, + collateral: s.isCollateral, + })); + }, [userReservesSummary]); + + const borrowPositions: BorrowPosition[] = useMemo(() => { + if (!userReservesSummary) return []; + return userReservesSummary.borrowedAssets.map(ba => ({ + asset: ba.asset, + apy: ba.apy, + borrowed: ba.borrowed, + borrowedUSD: ba.borrowedUSD, + apyType: ba.apyType, + })); + }, [userReservesSummary]); + + const borrowPools: BorrowPoolDetails[] = useMemo(() => { + if (!userReservesSummary) { + return reserves.map(r => ({ + asset: r.symbol, + apy: Decimal.from(r.variableBorrowAPY).mul(100), + })); + } else { + return reserves.map(r => ({ + asset: r.symbol, + apy: Decimal.from(r.variableBorrowAPY).mul(100), + available: userReservesSummary.borrowPower.div(r.priceInUSD), + availableUSD: userReservesSummary.borrowPower, + })); + } + }, [reserves, userReservesSummary]); + + const lendPools: LendPoolDetails[] = useMemo( + () => + reserves.map(r => ({ + asset: r.symbol, + apy: Decimal.from(r.supplyAPY).mul(100), + canBeCollateral: r.usageAsCollateralEnabled, + })), + [reserves], + ); + return (
{t(pageTranslations.meta.title)} - +
{/* Tab selector */} @@ -61,8 +122,13 @@ const AavePage: FC = () => { 'lg:block space-y-4', )} > - - + +
{/* Borrowing column */} @@ -72,8 +138,14 @@ const AavePage: FC = () => { 'lg:block space-y-4', )} > - - + +
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx index c8de37d5f..52d165e1f 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.constants.tsx @@ -5,11 +5,11 @@ import { t } from 'i18next'; import { Align, HelperButton } from '@sovryn/ui'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; -import { AssetAmountPriceRenderer } from '../../../../2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer'; import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { translations } from '../../../../../locales/i18n'; import { BorrowPoolDetails } from './BorrowAssetsList.types'; import { BorrowAssetAction } from './components/BorrowAssetAction/BorrowAssetAction'; +import { AssetAmountPriceRenderer } from '../../../../2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer'; const pageTranslations = translations.aavePage; @@ -44,15 +44,20 @@ export const COLUMNS_CONFIG = [ /> ), - cellRenderer: (position: BorrowPoolDetails) => ( - - ), + cellRenderer: (position: BorrowPoolDetails) => + position.available !== undefined && + position.availableUSD !== undefined ? ( + + ) : ( + - + ), }, { - id: 'apr', + id: 'apy', sortable: true, align: Align.center, className: '[&_*]:mx-auto [&_*]:space-x-2', // center head @@ -63,7 +68,7 @@ export const COLUMNS_CONFIG = [ ), cellRenderer: (pool: BorrowPoolDetails) => ( - + ), }, { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx index 76a2c27f1..105731d30 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx @@ -12,34 +12,30 @@ import { BorrowAssetDetails } from './components/BorrowAssetDetails/BorrowAssetD const pageTranslations = translations.aavePage.borrowAssetsList; -type BorrowAssetsListProps = {}; +type BorrowAssetsListProps = { + borrowPools: BorrowPoolDetails[]; +}; -export const BorrowAssetsList: FC = () => { +export const BorrowAssetsList: FC = ({ + borrowPools, +}) => { const [open, setOpen] = useState(true); const [orderOptions, setOrderOptions] = useState(); const rowTitleRenderer = useCallback( - r => , + (r: BorrowPoolDetails) => ( + + ), [], ); const mobileRenderer = useCallback(p => , []); - // TODO: just a mock for now - const borrowPools: BorrowPoolDetails[] = [ - { - asset: 'BTC', - apr: 2.01, - available: 12.34, - availableUsd: 100, - }, - { - asset: 'ETH', - apr: 2.01, - available: 12.34, - availableUsd: 100, - }, - ]; - return ( = ({ pool }) => { return (
- {/* APR */} + {/* APY */} - {t(pageTranslations.common.apr)}{' '} - + {t(pageTranslations.common.apy)}{' '} + } - value={} + value={} /> {/* Available */} + pool.available && pool.availableUSD ? ( + + ) : ( + - + ) } />
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx index 23a4cc203..d8c35afce 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx @@ -41,22 +41,22 @@ export const COLUMNS_CONFIG = [ ), cellRenderer: (pool: BorrowPosition) => ( - + ), }, { - id: 'apr', + id: 'apy', sortable: true, align: Align.center, className: '[&_*]:mx-auto [&_*]:space-x-2', // center head title: ( - {t(translations.aavePage.common.apr)}{' '} - + {t(translations.aavePage.common.apy)}{' '} + ), cellRenderer: (position: BorrowPosition) => ( - + ), }, { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index 7e3a7824d..d96d6d40d 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -3,6 +3,7 @@ import React, { FC, useCallback, useState } from 'react'; import { t } from 'i18next'; import { Accordion, OrderOptions, Paragraph, Table } from '@sovryn/ui'; +import { Decimal } from '@sovryn/utils'; import { AaveRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { useAccount } from '../../../../../hooks/useAccount'; @@ -15,18 +16,34 @@ import { EfficiencyModeCard } from './components/EfficiencyModeCard/EfficiencyMo const pageTranslations = translations.aavePage; -type BorrowPositionsListProps = {}; +type BorrowPositionsListProps = { + borrowPositions: BorrowPosition[]; + borrowBalance?: Decimal; + borrowWeightedApy?: Decimal; + borrowPowerUsed?: Decimal; + eModeEnabled: boolean; +}; -export const BorrowPositionsList: FC = () => { +export const BorrowPositionsList: FC = ({ + borrowPositions, + borrowBalance, + borrowPowerUsed, + borrowWeightedApy, + eModeEnabled, +}) => { const { account } = useAccount(); const [open, setOpen] = useState(true); - const [balance] = useState(123.45); // TODO: mock - const [apy] = useState(2.05); // TODO: mock - const [borrowPowerUsed] = useState(2.05); // TODO: mock const [orderOptions, setOrderOptions] = useState(); const rowTitleRenderer = useCallback( - r => , + (r: BorrowPosition) => ( + + ), [], ); @@ -35,22 +52,6 @@ export const BorrowPositionsList: FC = () => { [], ); - // TODO: mocked values - const borrowPositions: BorrowPosition[] = [ - { - asset: 'BTC', - apr: 2.24, - balance: 12.34, - apyType: 'variable', - }, - { - asset: 'ETH', - apr: 2.33, - balance: 12.34, - apyType: 'fixed', - }, - ]; - return ( = () => { {t(pageTranslations.borrowPositionsList.title)}
E-Mode - +
} @@ -69,23 +70,26 @@ export const BorrowPositionsList: FC = () => { > {account ? ( <> - +
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx index e56b6b1ea..f7641645b 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx @@ -1,6 +1,10 @@ +import { Decimal } from '@sovryn/utils'; +import { ApyType } from '../../../../../utils/aave'; + export type BorrowPosition = { asset: string; - balance: number; - apr: number; - apyType: 'variable' | 'fixed'; + borrowed: Decimal; + borrowedUSD: Decimal; + apy: Decimal; + apyType: ApyType; }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx index 076979a55..6a2ca15c7 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx @@ -25,8 +25,9 @@ export const BorrowPositionDetails: FC = ({ label={t(translations.aavePage.common.balance)} value={ } /> @@ -39,7 +40,9 @@ export const BorrowPositionDetails: FC = ({ } - value={} + value={ + + } /> {/* Apy type */} diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx index 8e12f107e..069106293 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, { FC } from 'react'; import classNames from 'classnames'; import { t } from 'i18next'; @@ -18,13 +18,13 @@ import { translations } from '../../../../../../../locales/i18n'; type EfficiencyModeCardProps = { className?: string; + enabled: boolean; }; export const EfficiencyModeCard: FC = ({ className, + enabled, }) => { - const [enabled] = useState(false); - return ( ), cellRenderer: (pool: LendPoolDetails) => ( - + ), }, { @@ -54,7 +55,7 @@ export const COLUMNS_CONFIG = [ ), cellRenderer: (pool: LendPoolDetails) => ( - + ), }, { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx index 4d14b7028..45675f242 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx @@ -12,35 +12,29 @@ import { LendAssetDetails } from './components/LendAssetDetails/LendAssetDetails const pageTranslations = translations.aavePage; -type LendAssetsListProps = {}; +type LendAssetsListProps = { + lendPools: LendPoolDetails[]; +}; -export const LendAssetsList: FC = () => { +export const LendAssetsList: FC = ({ lendPools }) => { const [open, setOpen] = useState(true); const [showZeroBalances, setShowZeroBalances] = useState(true); const [orderOptions, setOrderOptions] = useState(); const mobileRenderer = useCallback(p => , []); const rowTitleRenderer = useCallback( - r => , + (r: LendPoolDetails) => ( + + ), [], ); - // TODO: mocked values and filter - const lendPools: LendPoolDetails[] = [ - { - asset: 'BTC', - apy: 2.02, - walletBalance: 12.34, - canBeCollateral: true, - }, - { - asset: 'ETH', - apy: 2.02, - walletBalance: 12.34, - canBeCollateral: false, - }, - ]; - return ( = ({ + asset, +}) => { + const balance = useAssetBalance(asset); + const { account } = useAccount(); + + return account ? ( + + ) : ( + - + ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx index f02aea902..7a83ea87b 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx @@ -6,10 +6,10 @@ import { t } from 'i18next'; import { HelperButton, Icon, IconNames, SimpleTableRow } from '@sovryn/ui'; import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; -import { AssetAmountPriceRenderer } from '../../../../../../2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer'; import { translations } from '../../../../../../../locales/i18n'; import { LendPoolDetails } from '../../LendAssetsList.types'; import { LendAssetAction } from '../LendAssetAction/LendAssetAction'; +import { AssetBalanceRenderer } from '../AssetBalance/AssetBalance'; type LendAssetDetailsProps = { pool: LendPoolDetails; @@ -22,12 +22,7 @@ export const LendAssetDetails: FC = ({ pool }) => { {/* Available */} - } + value={} /> {/* APY */} diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx index 5f875685a..76c7c9f28 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx @@ -42,7 +42,8 @@ export const COLUMNS_CONFIG = [ ), cellRenderer: (position: LendPosition) => ( ), @@ -59,7 +60,7 @@ export const COLUMNS_CONFIG = [ ), cellRenderer: (position: LendPosition) => ( - + ), }, { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx index a0ab8d43f..058392cf5 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx @@ -9,6 +9,7 @@ import { Paragraph, Table, } from '@sovryn/ui'; +import { Decimal } from '@sovryn/utils'; import { AaveRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { useAccount } from '../../../../../hooks/useAccount'; @@ -20,21 +21,35 @@ import { LendPositionDetails } from './components/LendPositionDetails/LendPositi const pageTranslations = translations.aavePage; -type LendPositionsListProps = {}; +type LendPositionsListProps = { + supplyBalance?: Decimal; + supplyWeightedApy?: Decimal; + collateralBalance?: Decimal; + lendPositions: LendPosition[]; +}; -export const LendPositionsList: FC = () => { +export const LendPositionsList: FC = ({ + supplyBalance, + supplyWeightedApy, + collateralBalance, + lendPositions, +}) => { const { account } = useAccount(); const [open, setOpen] = useState(true); - const [balance] = useState(100); // TODO: mocked data - const [apy] = useState(2.3); // TODO: mocked data - const [collateral] = useState(3); // TODO: mocked data const [orderOptions, setOrderOptions] = useState({ orderBy: 'balance', orderDirection: OrderDirection.Asc, }); const rowTitleRenderer = useCallback( - r => , + (r: LendPosition) => ( + + ), [], ); const mobileRenderer = useCallback( @@ -42,22 +57,6 @@ export const LendPositionsList: FC = () => { [], ); - // TODO: mocked data - const lendPositions: LendPosition[] = [ - { - asset: 'BTC', - apy: 2.01, - balance: 12.34, - collateral: true, - }, - { - asset: 'ETH', - apy: 2.04, - balance: 1.34, - collateral: false, - }, - ]; - return ( = () => {
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.types.tsx index 9a43a4bfb..c3f52e519 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.types.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.types.tsx @@ -1,6 +1,9 @@ +import { Decimal } from '@sovryn/utils'; + export type LendPosition = { asset: string; - balance: number; - apy: number; + supplied: Decimal; + suppliedUSD: Decimal; + apy: Decimal; collateral: boolean; }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx index d1bcd4c36..6a076ca75 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx @@ -26,7 +26,8 @@ export const LendPositionDetails: FC = ({ label={t(translations.aavePage.common.balance)} value={ } diff --git a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx index e8846454d..fe1f5172a 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, { FC } from 'react'; import { t } from 'i18next'; @@ -8,16 +8,22 @@ import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRen import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; import { useAccount } from '../../../../../hooks/useAccount'; import { translations } from '../../../../../locales/i18n'; +import { Decimalish } from '@sovryn/utils'; const pageTranslations = translations.aavePage.topPanel; -type TopPanelProps = {}; +type TopPanelProps = { + netWorth?: Decimalish; + netApy?: Decimalish; + healthFactor?: Decimalish; +}; -export const TopPanel: FC = () => { +export const TopPanel: FC = ({ + netApy, + netWorth, + healthFactor, +}) => { const { account } = useAccount(); - const [netWorth] = useState(1234567.58); // TODO: mock - const [netApy] = useState(2.69); // TODO: mock - const [collateralRatio] = useState(11); // TODO: mock return (
@@ -35,9 +41,10 @@ export const TopPanel: FC = () => { @@ -48,10 +55,11 @@ export const TopPanel: FC = () => { ) : undefined @@ -59,17 +67,17 @@ export const TopPanel: FC = () => { help={t(pageTranslations.netApyInfo)} /> ) : undefined } - help={t(pageTranslations.collateralRatioInfo)} />
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/components/BorrowAction/BorrowAction.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/components/BorrowAction/BorrowAction.tsx index a177cf25a..950795d40 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/components/BorrowAction/BorrowAction.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/components/BorrowAction/BorrowAction.tsx @@ -16,6 +16,7 @@ type BorrowActionProps = { export const BorrowAction: FC = ({ asset }) => { const availableToBorrow = 0; // TODO: this is mocked + const availableToBorrowUsd = 0; const isBorrowDisabled = useMemo(() => { // TODO: add conditions @@ -36,6 +37,7 @@ export const BorrowAction: FC = ({ asset }) => { = ({ asset }) => { const availableToSupply = 0; // TODO: this is mocked. + const availableToSupplyUSD = 0; // TODO: this is mocked. const isSupplyDisabled = useMemo(() => { // TODO: add conditions @@ -36,8 +37,8 @@ export const SupplyAction: FC = ({ asset }) => {
diff --git a/apps/frontend/src/constants/aave.ts b/apps/frontend/src/constants/aave.ts new file mode 100644 index 000000000..0724422f6 --- /dev/null +++ b/apps/frontend/src/constants/aave.ts @@ -0,0 +1,12 @@ +import { ethers } from 'ethers'; + +export const config = { + chainId: 137, + UiPoolDataProviderV3Address: '0x5598BbFA2f4fE8151f45bBA0a3edE1b54B51a0a9', + PoolAddressesProviderAddress: '0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb', + assetsWhitelist: ['DAI', 'USDC', 'USDT', 'BTC', 'WETH', 'EURS', 'WMATIC'], + // TODO: temporary + provider: new ethers.providers.JsonRpcProvider( + 'https://polygon-bor-rpc.publicnode.com', + ), +}; diff --git a/apps/frontend/src/hooks/useAaveReservesData.tsx b/apps/frontend/src/hooks/useAaveReservesData.tsx new file mode 100644 index 000000000..201d160df --- /dev/null +++ b/apps/frontend/src/hooks/useAaveReservesData.tsx @@ -0,0 +1,68 @@ +import { + ReserveDataHumanized, + ReservesDataHumanized, + UiPoolDataProvider, +} from '@aave/contract-helpers'; +import { formatReserves, FormatReserveUSDResponse } from '@aave/math-utils'; + +import { useCallback, useEffect, useMemo, useState } from 'react'; + +import dayjs from 'dayjs'; + +import { config } from '../constants/aave'; + +export type Reserve = ReserveDataHumanized & FormatReserveUSDResponse; + +export const useAaveReservesData = () => { + const provider = config.provider; // TODO: replace with useAccount + const [reserves, setReserves] = useState([]); + const [reservesData, setReservesData] = + useState(null); + + const uiPoolDataProvider = useMemo( + () => + provider + ? new UiPoolDataProvider({ + provider, + uiPoolDataProviderAddress: config.UiPoolDataProviderV3Address, + chainId: config.chainId, + }) + : null, + [provider], + ); + + const fetchReservesData = useCallback( + async (uiPoolDataProvider: UiPoolDataProvider) => { + const currentTimestamp = dayjs().unix(); + const reservesData = await uiPoolDataProvider.getReservesHumanized({ + lendingPoolAddressProvider: config.PoolAddressesProviderAddress, + }); + const reserves = reservesData.reservesData.filter(r => + config.assetsWhitelist.includes(r.symbol), + ); + const formattedReserves = formatReserves({ + reserves, + currentTimestamp, + marketReferenceCurrencyDecimals: + reservesData.baseCurrencyData.marketReferenceCurrencyDecimals, + marketReferencePriceInUsd: + reservesData.baseCurrencyData.marketReferenceCurrencyPriceInUsd, + }); + + setReserves(formattedReserves); + setReservesData({ + baseCurrencyData: reservesData.baseCurrencyData, + reservesData: reserves, + }); + }, + [], + ); + + useEffect(() => { + if (uiPoolDataProvider) { + fetchReservesData(uiPoolDataProvider); + } + }, [uiPoolDataProvider, fetchReservesData]); + + return { reserves, reservesData }; +}; diff --git a/apps/frontend/src/hooks/useAaveUserReservesData.tsx b/apps/frontend/src/hooks/useAaveUserReservesData.tsx new file mode 100644 index 000000000..3903ee413 --- /dev/null +++ b/apps/frontend/src/hooks/useAaveUserReservesData.tsx @@ -0,0 +1,81 @@ +import { + ReservesDataHumanized, + UiPoolDataProvider, +} from '@aave/contract-helpers'; +import { formatUserSummary } from '@aave/math-utils'; + +import { useCallback, useEffect, useMemo, useState } from 'react'; + +import dayjs from 'dayjs'; +import { ethers } from 'ethers'; + +import { config } from '../constants/aave'; +import { AaveUserReservesSummary } from '../utils/aave'; +import { Reserve, useAaveReservesData } from './useAaveReservesData'; +import { useAccount } from './useAccount'; + +export const useAaveUserReservesData = () => { + const provider = config.provider; + const { reserves, reservesData } = useAaveReservesData(); + const { signer } = useAccount(); + const [userReservesSummary, setUserReservesSummary] = + useState(null); + + const uiPoolDataProvider = useMemo( + () => + provider + ? new UiPoolDataProvider({ + provider, + uiPoolDataProviderAddress: config.UiPoolDataProviderV3Address, + chainId: config.chainId, + }) + : null, + [provider], + ); + + const fetchPoolData = useCallback( + async ( + uiPoolDataProvider: UiPoolDataProvider, + reserves: Reserve[], + reservesData: ReservesDataHumanized, + signer: ethers.Signer, + ) => { + const userReservesData = + await uiPoolDataProvider.getUserReservesHumanized({ + lendingPoolAddressProvider: config.PoolAddressesProviderAddress, + user: await signer.getAddress(), + }); + + setUserReservesSummary( + AaveUserReservesSummary.from( + formatUserSummary({ + currentTimestamp: dayjs().unix(), + userReserves: userReservesData.userReserves, + userEmodeCategoryId: userReservesData.userEmodeCategoryId, + formattedReserves: reserves, + marketReferenceCurrencyDecimals: + reservesData.baseCurrencyData.marketReferenceCurrencyDecimals, + marketReferencePriceInUsd: + reservesData.baseCurrencyData.marketReferenceCurrencyPriceInUsd, + }), + ), + ); + }, + [], + ); + + useEffect(() => { + if ( + !uiPoolDataProvider || + reserves.length === 0 || + !signer || + !reservesData + ) { + return; + } + + fetchPoolData(uiPoolDataProvider, reserves, reservesData, signer); + }, [uiPoolDataProvider, reserves, signer, reservesData, fetchPoolData]); + + return { userReservesSummary }; +}; diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index 46893d98f..c4771acd9 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -815,8 +815,7 @@ "netWorth": "Net worth", "netApy": "Net API", "netApyInfo": "Net APY is the combined effect of all supply and borrow positions on net worth, including incentives. It is possible to have a negative net APY if debt APY is higher than supply APY.", - "collateralRatio": "Collateral ratio", - "collateralRatioInfo": "The collateral ratio defines the maximum amount of assets that can be borrowed with a specific collatera", + "healthFactor": "Health factor", "viewTransactions": "View transactions" }, "borrowPositionsList": { diff --git a/apps/frontend/src/utils/aave.ts b/apps/frontend/src/utils/aave.ts new file mode 100644 index 000000000..baa58ec92 --- /dev/null +++ b/apps/frontend/src/utils/aave.ts @@ -0,0 +1,238 @@ +import { ReserveDataHumanized } from '@aave/contract-helpers'; +import { + FormatReserveUSDResponse, + FormatUserSummaryResponse, +} from '@aave/math-utils'; +import { Decimal } from '@sovryn/utils'; +type UserSummary = FormatUserSummaryResponse< + ReserveDataHumanized & FormatReserveUSDResponse +>; + +export enum ApyType { + VARIABLE = 'variable', + STABLE = 'stable', +} + +export type SuppliedAsset = { + asset: string; + assetAddress: string; + apy: Decimal; + isCollateral: boolean; + supplied: Decimal; + suppliedUSD: Decimal; +}; + +export type BorrowedAsset = { + asset: string; + assetAddress: string; + apy: Decimal; + apyType: ApyType; + borrowed: Decimal; + borrowedUSD: Decimal; +}; + +export class AaveUserReservesSummary { + public netWorth: Decimal; + public netApy: Decimal; + public healthFactor: Decimal; + public supplyBalance: Decimal; // OK + public supplyWeightedApy: Decimal; + public collateralBalance: Decimal; + public borrowBalance: Decimal; + public borrowWeightedApy: Decimal; + public borrowPower: Decimal; + public borrowPowerUsed: Decimal; + public suppliedAssets: SuppliedAsset[]; + public borrowedAssets: BorrowedAsset[]; + public eModeEnabled: boolean; + + constructor(private readonly userSummary: UserSummary) { + // balances + this.netWorth = Decimal.from(this.userSummary.netWorthUSD); + this.borrowBalance = Decimal.from(this.userSummary.totalBorrowsUSD); + this.supplyBalance = this.computeSuppliedBalance( + this.userSummary.userReservesData, + ); + this.collateralBalance = this.computeCollateral( + this.userSummary.userReservesData, + ); + + // apy + this.supplyWeightedApy = this.computeWeightedSupplyApy( + this.userSummary.userReservesData, + ); + this.borrowWeightedApy = this.computeWeightedBorrowApy( + this.userSummary.userReservesData, + ); + this.netApy = this.computeNetApy( + this.supplyWeightedApy, + this.supplyBalance, + this.borrowWeightedApy, + this.borrowBalance, + this.netWorth, + ); + + // health and borrow status + this.borrowPower = this.computeBorrowPower( + Decimal.from(this.userSummary.availableBorrowsUSD), + this.borrowBalance, + ); + this.borrowPowerUsed = this.computeBorrowPowerUsed( + this.borrowBalance, + this.borrowPower, + ); + this.healthFactor = this.computeHealthFactor( + this.collateralBalance, + Decimal.from(this.userSummary.currentLiquidationThreshold), + this.borrowBalance, + ); + + // supplied and borrowed assets + this.suppliedAssets = this.computeSuppliedAssets( + this.userSummary.userReservesData, + ); + this.borrowedAssets = this.computeBorrowedAssets( + this.userSummary.userReservesData, + ); + + // emode + this.eModeEnabled = this.userSummary.userEmodeCategoryId !== 0; + } + + static from( + userSummary: FormatUserSummaryResponse< + ReserveDataHumanized & FormatReserveUSDResponse + >, + ) { + return new AaveUserReservesSummary(userSummary); + } + + private computeNetApy( + weightedSupplyApy: Decimal, + suppliedBalance: Decimal, + weightedBorrowApy: Decimal, + borrowedBalance: Decimal, + netWorthUsd: Decimal, + ): Decimal { + return weightedSupplyApy + .mul(suppliedBalance) + .div(netWorthUsd) + .sub(weightedBorrowApy.mul(borrowedBalance).div(netWorthUsd)); + } + + private computeSuppliedBalance( + reserves: UserSummary['userReservesData'], + ): Decimal { + return reserves.reduce( + (suppliedBalance, r) => suppliedBalance.add(r.underlyingBalanceUSD), + Decimal.from(0), + ); + } + + private computeBorrowPower( + availableBorrowsUSD: Decimal, + borrowedBalance: Decimal, + ) { + return Decimal.from(availableBorrowsUSD).add(borrowedBalance); + } + + private computeBorrowPowerUsed( + borrowedBalance: Decimal, + borrowPower: Decimal, + ) { + if (borrowPower.eq(0)) return Decimal.from(0); + return Decimal.from(borrowedBalance).div(borrowPower).mul(100); + } + + private computeCollateral( + reserves: UserSummary['userReservesData'], + ): Decimal { + return reserves.reduce( + (collateral, r) => + collateral.add( + r.usageAsCollateralEnabledOnUser ? r.underlyingBalanceUSD : 0, + ), + Decimal.from(0), + ); + } + + private computeHealthFactor( + collateral: Decimal, + currentLiquidationThreshold: Decimal, + borrowedBalance: Decimal, + ): Decimal { + if (borrowedBalance.eq(0)) return Decimal.from(0); + return collateral.mul(currentLiquidationThreshold).div(borrowedBalance); + } + + private computeWeightedBorrowApy( + reserves: UserSummary['userReservesData'], + ): Decimal { + let totalBorrowedUSD = Decimal.from(0); + let weightedBorrowAPYSum = Decimal.from(0); + + reserves.forEach(reserve => { + const borrowedAmountUSD = Decimal.from(reserve.totalBorrowsUSD); + const borrowAPY = Decimal.from(reserve.reserve.variableBorrowAPY); + + weightedBorrowAPYSum = weightedBorrowAPYSum.add( + borrowAPY.mul(borrowedAmountUSD), + ); + totalBorrowedUSD = totalBorrowedUSD.add(borrowedAmountUSD); + }); + + if (totalBorrowedUSD.eq(0)) return Decimal.from(0); + return weightedBorrowAPYSum.div(totalBorrowedUSD).mul(100); + } + + private computeWeightedSupplyApy( + reserves: UserSummary['userReservesData'], + ): Decimal { + let totalBorrowedUSD = Decimal.from(0); + let weightedBorrowAPYSum = Decimal.from(0); + + reserves.forEach(reserve => { + const borrowedAmountUSD = Decimal.from(reserve.totalBorrowsUSD); + const borrowAPY = Decimal.from(reserve.reserve.supplyAPY); + + weightedBorrowAPYSum = weightedBorrowAPYSum.add( + borrowAPY.mul(borrowedAmountUSD), + ); + totalBorrowedUSD = totalBorrowedUSD.add(borrowedAmountUSD); + }); + + if (totalBorrowedUSD.eq(0)) return Decimal.from(0); + return weightedBorrowAPYSum.div(totalBorrowedUSD).mul(100); + } + + private computeSuppliedAssets( + reserves: UserSummary['userReservesData'], + ): SuppliedAsset[] { + return reserves + .filter(r => Decimal.from(r.underlyingBalanceUSD).gt(0)) + .map(r => ({ + asset: r.reserve.symbol, + assetAddress: r.underlyingAsset, + apy: Decimal.from(r.reserve.supplyAPY).mul(100), + isCollateral: r.usageAsCollateralEnabledOnUser, + supplied: Decimal.from(r.underlyingBalance), + suppliedUSD: Decimal.from(r.underlyingBalanceUSD), + })); + } + + private computeBorrowedAssets( + reserves: UserSummary['userReservesData'], + ): BorrowedAsset[] { + return reserves + .filter(r => Decimal.from(r.totalBorrowsUSD).gt(0)) + .map(r => ({ + asset: r.reserve.symbol, + assetAddress: r.underlyingAsset, + borrowed: Decimal.from(r.totalBorrows), + borrowedUSD: Decimal.from(r.totalBorrowsUSD), + apy: Decimal.from(r.reserve.variableBorrowAPY).mul(100), + apyType: + Number(r.variableBorrows) > 0 ? ApyType.VARIABLE : ApyType.STABLE, + })); + } +} diff --git a/apps/frontend/src/utils/graphql/bob/generated.tsx b/apps/frontend/src/utils/graphql/bob/generated.tsx index fb4b63963..eddd99bd3 100644 --- a/apps/frontend/src/utils/graphql/bob/generated.tsx +++ b/apps/frontend/src/utils/graphql/bob/generated.tsx @@ -1,6 +1,5 @@ import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; - export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { diff --git a/apps/frontend/src/utils/graphql/mynt/generated.tsx b/apps/frontend/src/utils/graphql/mynt/generated.tsx index 5af92ca2e..d4dca3b2b 100644 --- a/apps/frontend/src/utils/graphql/mynt/generated.tsx +++ b/apps/frontend/src/utils/graphql/mynt/generated.tsx @@ -1,6 +1,5 @@ import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; - export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { diff --git a/apps/frontend/src/utils/graphql/rsk/generated.tsx b/apps/frontend/src/utils/graphql/rsk/generated.tsx index 1679b3971..28ef61297 100644 --- a/apps/frontend/src/utils/graphql/rsk/generated.tsx +++ b/apps/frontend/src/utils/graphql/rsk/generated.tsx @@ -1,6 +1,5 @@ import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; - export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { diff --git a/apps/frontend/src/utils/graphql/zero/generated.tsx b/apps/frontend/src/utils/graphql/zero/generated.tsx index 7a8931e37..158c284f6 100644 --- a/apps/frontend/src/utils/graphql/zero/generated.tsx +++ b/apps/frontend/src/utils/graphql/zero/generated.tsx @@ -1,6 +1,5 @@ import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; - export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { diff --git a/yarn.lock b/yarn.lock index 20c556a0d..8be9694db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,18 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== +"@aave/contract-helpers@^1.29.1": + version "1.29.1" + resolved "https://registry.yarnpkg.com/@aave/contract-helpers/-/contract-helpers-1.29.1.tgz#34beab8afa35bbfc6168c78ced8317d96a493fe0" + integrity sha512-34z5CKpNdEx26G+DSezovdR3PyAf0v0Typbf2udaKG4GrOBgqbKqxr4zqS/dpFO5aAQJJ+nuEM19lKRK4sMcpg== + dependencies: + isomorphic-unfetch "^3.1.0" + +"@aave/math-utils@^1.29.1": + version "1.29.1" + resolved "https://registry.yarnpkg.com/@aave/math-utils/-/math-utils-1.29.1.tgz#fcb9499bd472bde7b80429c7dbe63d4358852697" + integrity sha512-a+L2+vjza/nH4BxUTzwDcoJH/Qr6UP+g+9YSwG7YKUgoVfdy8gJs5VyXjfDDEVe9lMeZuPJq7CqrgV4P2M6Nkg== + "@actions/core@^1.10.0": version "1.10.0" resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.10.0.tgz#44551c3c71163949a2f06e94d9ca2157a0cfac4f" @@ -21035,6 +21047,11 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +reflect-metadata@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" + integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== + refractor@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.6.0.tgz#ac318f5a0715ead790fcfb0c71f4dd83d977935a" @@ -21600,14 +21617,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@6: - version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - -rxjs@7.5.6, rxjs@^7.5.5, rxjs@^7.5.6: +rxjs@6, rxjs@7.5.6, rxjs@^7.5.5, rxjs@^7.5.6: version "7.5.6" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc" integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw== @@ -23492,7 +23502,7 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@1.14.1, tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: +tslib@1.14.1, tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== From ce56708203f38cc8a9ebb5ea7e2624d19e19f2fc Mon Sep 17 00:00:00 2001 From: Matias Zapata <42716817+matzapata@users.noreply.github.com> Date: Wed, 21 Aug 2024 10:58:47 -0300 Subject: [PATCH 048/116] Lend integration (#19) * dashboard tables * emode usd and borrowing powers * fix asset details * wip * Dashboard pool data integration (#15) * dashboard tables * emode usd and borrowing powers * fix asset details * pr comments * unit fixes * supply wip * fix asset renderer add usd amount cleanups * fixes * cleanup --- .../AssetAmountInput/AssetAmountInput.tsx | 19 +- .../AssetRenderer/AssetRenderer.tsx | 10 +- .../BorrowPositionsList.types.tsx | 2 +- .../LendAssetAction/LendAssetAction.tsx | 3 +- .../components/LendModal/LendForm.tsx | 72 ++++-- .../LendModal/LendModalContainer.tsx | 4 +- .../AavePage/components/TopPanel/TopPanel.tsx | 2 +- apps/frontend/src/constants/aave.ts | 3 + apps/frontend/src/hooks/useAaveDeposit.tsx | 47 ++++ .../frontend/src/locales/en/translations.json | 4 + apps/frontend/src/utils/aave.ts | 2 + .../aave/AaveSupplyTransactionsFactory.ts | 111 ++++++++ .../src/utils/aave/AaveUserReservesSummary.ts | 236 ++++++++++++++++++ .../src/utils/graphql/bob/generated.tsx | 1 + .../src/utils/graphql/mynt/generated.tsx | 1 + .../src/utils/graphql/rsk/generated.tsx | 1 + .../src/utils/graphql/zero/generated.tsx | 1 + 17 files changed, 493 insertions(+), 26 deletions(-) create mode 100644 apps/frontend/src/hooks/useAaveDeposit.tsx create mode 100644 apps/frontend/src/utils/aave/AaveSupplyTransactionsFactory.ts create mode 100644 apps/frontend/src/utils/aave/AaveUserReservesSummary.ts diff --git a/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx b/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx index 0d3abb9a6..97ed14a9a 100644 --- a/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx +++ b/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx @@ -1,8 +1,10 @@ -import React, { FC, useCallback, useState } from 'react'; +import React, { FC, useCallback } from 'react'; import { AmountInput, Paragraph, Select, SelectOption } from '@sovryn/ui'; import { Decimal, Decimalish } from '@sovryn/utils'; +import { BOB_CHAIN_ID } from '../../../config/chains'; + import { AmountRenderer } from '../AmountRenderer/AmountRenderer'; import { AssetRenderer } from '../AssetRenderer/AssetRenderer'; import { MaxButton } from '../MaxButton/MaxButton'; @@ -15,6 +17,7 @@ type AssetAmountInputProps = { amountValue?: string | number; onAmountChange?: (value: string) => unknown; assetValue: string; + assetUsdValue?: Decimalish; assetOptions: SelectOption[]; onAssetChange: (asset: string) => unknown; }; @@ -27,14 +30,18 @@ export const AssetAmountInput: FC = ({ onAmountChange, invalid, assetValue, + assetUsdValue, assetOptions, onAssetChange, }) => { - const [assetUsdValue] = useState(0); // TODO: mock - const assetOptionRenderer = useCallback( ({ value }) => ( - + ), [], ); @@ -72,7 +79,8 @@ export const AssetAmountInput: FC = ({ />
@@ -84,7 +92,6 @@ export const AssetAmountInput: FC = ({ labelRenderer={assetOptionRenderer} className="min-w-[6.7rem]" menuClassName="max-h-[10rem] sm:max-h-[20rem]" - dataAttribute="asset-select" />
diff --git a/apps/frontend/src/app/2_molecules/AssetRenderer/AssetRenderer.tsx b/apps/frontend/src/app/2_molecules/AssetRenderer/AssetRenderer.tsx index 6f686d83c..15a681d87 100644 --- a/apps/frontend/src/app/2_molecules/AssetRenderer/AssetRenderer.tsx +++ b/apps/frontend/src/app/2_molecules/AssetRenderer/AssetRenderer.tsx @@ -70,7 +70,10 @@ export const AssetRenderer: FC = ({ setLogo(item.icon); setToken(item.symbol); }) - .catch(() => setLogo('')); + .catch(() => { + setToken(asset); + setLogo(''); + }); } }, [address, asset, chainId, showAssetLogo]); @@ -81,7 +84,10 @@ export const AssetRenderer: FC = ({ setLogo(item.icon); setToken(item.symbol); }) - .catch(() => setLogo('')); + .catch(() => { + setToken(asset); + setLogo(''); + }); } }, [address, asset, chainId, showAssetLogo]); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx index f7641645b..2c9d3d773 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx @@ -1,5 +1,5 @@ import { Decimal } from '@sovryn/utils'; -import { ApyType } from '../../../../../utils/aave'; +import { ApyType } from '../../../../../utils/aave/AaveUserReservesSummary'; export type BorrowPosition = { asset: string; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx index 2aa07f6b1..6f4d5cbd7 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx @@ -13,7 +13,7 @@ type LendAssetActionProps = { pool: LendPoolDetails; }; -export const LendAssetAction: FC = () => { +export const LendAssetAction: FC = ({ pool }) => { const navigate = useNavigate(); const [isLendModalOpen, setIsLendModalOpen] = useState(false); @@ -41,6 +41,7 @@ export const LendAssetAction: FC = () => { /> diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendModal/LendForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendModal/LendForm.tsx index 34fef11c9..5d8ad0c89 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendModal/LendForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendModal/LendForm.tsx @@ -2,6 +2,7 @@ import React, { FC, useMemo, useState } from 'react'; import { t } from 'i18next'; +import { getAssetData } from '@sovryn/contracts'; import { Button, ErrorBadge, @@ -11,43 +12,65 @@ import { } from '@sovryn/ui'; import { Decimal } from '@sovryn/utils'; +import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; + import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; +import { useAaveDeposit } from '../../../../../../../hooks/useAaveDeposit'; +import { useAaveReservesData } from '../../../../../../../hooks/useAaveReservesData'; +import { useAccount } from '../../../../../../../hooks/useAccount'; +import { useAssetBalance } from '../../../../../../../hooks/useAssetBalance'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; import { translations } from '../../../../../../../locales/i18n'; const pageTranslations = translations.aavePage; type LendFormProps = { + asset: string; onSuccess: () => unknown; }; -export const LendForm: FC = () => { - const lendApy = 4.01; // TODO: this is mocked data. Replace with proper hook - const lendAssets = useMemo(() => ['BTC', 'SOV'], []); // TODO: this is mocked data. Replace with proper hook - const [maximumLendAmount] = useState(Decimal.from(10)); // TODO: this is mocked data. Replace with proper hook - const [lendAsset, setLendAsset] = useState(lendAssets[0]); +export const LendForm: FC = ({ + asset: initialAsset, + onSuccess, +}) => { + const { account } = useAccount(); + const { reserves } = useAaveReservesData(); + const [lendAsset, setLendAsset] = useState(initialAsset); const [lendAmount, setLendAmount, lendSize] = useDecimalAmountInput(''); + const { balance: lendAssetBalance } = useAssetBalance( + lendAsset, + BOB_CHAIN_ID, + account, + ); + const { handleDeposit } = useAaveDeposit( + () => null, + () => null, + ); + + const reserve = useMemo(() => { + return reserves.find(r => r.symbol === lendAsset) ?? reserves[0]; + }, [reserves, lendAsset]); const lendAssetsOptions = useMemo( () => - lendAssets.map(token => ({ - value: token, + reserves.map(r => ({ + value: r.symbol, label: ( ), })), - [lendAssets], + [reserves], ); const isValidLendAmount = useMemo( - () => (lendSize.gt(0) ? lendSize.lte(maximumLendAmount) : true), - [lendSize, maximumLendAmount], + () => (lendSize.gt(0) ? lendSize.lte(lendAssetBalance) : true), + [lendSize, lendAssetBalance], ); const submitButtonDisabled = useMemo( @@ -55,17 +78,22 @@ export const LendForm: FC = () => { [isValidLendAmount, lendSize], ); + const assetUsdValue: Decimal = useMemo(() => { + return Decimal.from(reserve?.priceInUSD ?? 0).mul(lendSize); + }, [reserve, lendSize]); + return (
@@ -82,16 +110,32 @@ export const LendForm: FC = () => { } + value={ + + } />
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx index 146c4effc..9664910b6 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx @@ -17,6 +17,7 @@ import { RepayWithCollateralForm } from './RepayWithCollateralForm'; import { RepayWithWalletBalanceForm } from './RepayWithWalletBalanceForm'; type RepayModalContainerProps = { + asset: string; isOpen: boolean; handleCloseModal: () => unknown; }; @@ -27,6 +28,7 @@ enum RepayWith { } export const RepayModalContainer: FC = ({ + asset, isOpen, handleCloseModal, }) => { @@ -61,13 +63,17 @@ export const RepayModalContainer: FC = ({ activeClassName: 'text-primary-20', dataAttribute: 'collateral', label: t(translations.aavePage.common.collateral), + disabled: true, }, ]} type={TabType.secondary} /> {activeTab === RepayWith.BALANCE ? ( - + ) : ( )} diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx index 9dda5864d..b1efc2e4e 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx @@ -2,6 +2,7 @@ import React, { FC, useMemo, useState } from 'react'; import { t } from 'i18next'; +import { getAssetData } from '@sovryn/contracts'; import { Button, ErrorBadge, @@ -11,9 +12,16 @@ import { } from '@sovryn/ui'; import { Decimal } from '@sovryn/utils'; +import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; + import { AmountTransition } from '../../../../../../2_molecules/AmountTransition/AmountTransition'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; +import { useAaveRepay } from '../../../../../../../hooks/aave/useAaveRepay'; +import { useAaveReservesData } from '../../../../../../../hooks/aave/useAaveReservesData'; +import { useAaveUserReservesData } from '../../../../../../../hooks/aave/useAaveUserReservesData'; +import { useAccount } from '../../../../../../../hooks/useAccount'; +import { useAssetBalance } from '../../../../../../../hooks/useAssetBalance'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; import { translations } from '../../../../../../../locales/i18n'; import { CollateralRatioHealthBar } from '../../../CollateralRatioHealthBar/CollateralRatioHealthBar'; @@ -21,52 +29,95 @@ import { CollateralRatioHealthBar } from '../../../CollateralRatioHealthBar/Coll const pageTranslations = translations.aavePage; type RepayWithWalletBalanceFormProps = { + asset: string; onSuccess: () => unknown; }; export const RepayWithWalletBalanceForm: FC< RepayWithWalletBalanceFormProps -> = () => { - const totalBorrowed = Decimal.from(10); // TODO: this is mocked data. Replace with proper hook - const collateralToLoanRate = Decimal.from(10); // TODO: this is mocked data. Replace with proper hook - const collateralSize = Decimal.from(10); // TODO: this is mockd data. Replace with proper hook - const assetsToRepay = useMemo(() => ['BTC', 'SOV'], []); // TODO: this is mocked data. Replace with proper hook - const [maximumRepayAmount] = useState(Decimal.from(10)); // TODO: this is mocked data. Replace with proper hook - const [repayAsset, setRepayAsset] = useState(assetsToRepay[0]); +> = ({ asset }) => { + const { account } = useAccount(); + const { handleRepay } = useAaveRepay({}); + const reserves = useAaveReservesData(); + const userReservesSummary = useAaveUserReservesData(); + const [repayAsset, setRepayAsset] = useState(asset); const [repayAmount, setRepayAmount, repaySize] = useDecimalAmountInput(''); + const { balance: repayAssetBalance } = useAssetBalance( + repayAsset, + BOB_CHAIN_ID, + account, + ); const repayAssetsOptions = useMemo( () => - assetsToRepay.map(token => ({ - value: token, - label: ( - - ), - })), - [assetsToRepay], + userReservesSummary + ? userReservesSummary.borrowedAssets.map(ba => ({ + value: ba.asset, + label: ( + + ), + })) + : [], + [userReservesSummary], ); - const isValidRepayAmount = useMemo( - () => (repaySize.gt(0) ? repaySize.lte(maximumRepayAmount) : true), - [repaySize, maximumRepayAmount], - ); + const debt = useMemo(() => { + return userReservesSummary?.borrowedAssets.find( + a => a.asset === repayAsset, + ); + }, [userReservesSummary, repayAsset]); + + const maximumRepayAmount = useMemo(() => { + return debt + ? debt.borrowed.gt(repayAssetBalance) + ? repayAssetBalance + : debt.borrowed + : Decimal.from(0); + }, [debt, repayAssetBalance]); + + const reserve = useMemo(() => { + return reserves.find(r => r.symbol === repayAsset); + }, [reserves, repayAsset]); + + const repayUsdAmount = useMemo(() => { + return repaySize.mul(reserve?.priceInUSD ?? 0); + }, [repaySize, reserve]); + + const newDebtAmount = useMemo(() => { + const nd = debt?.borrowed ? debt.borrowed.sub(repaySize) : Decimal.from(0); + return nd.gt(0) ? nd : Decimal.from(0); + }, [debt, repaySize]); + + const newDebtAmountUSD = useMemo(() => { + return newDebtAmount.mul(reserve?.priceInUSD ?? 0); + }, [newDebtAmount, reserve]); const collateralRatio = useMemo(() => { - if ([collateralSize, totalBorrowed, repaySize].some(v => v.isZero())) { - return Decimal.from(0); - } + if (!userReservesSummary) return Decimal.from(0); + + return userReservesSummary.healthFactor.div( + userReservesSummary.currentLiquidationThreshold, + ); + }, [userReservesSummary]); - return collateralSize.mul(collateralToLoanRate).div(totalBorrowed).mul(100); - }, [collateralSize, totalBorrowed, repaySize, collateralToLoanRate]); + const newCollateralRatio = useMemo(() => { + if (!userReservesSummary) return Decimal.from(0); + if (newDebtAmountUSD.eq(0)) return Decimal.from('100000000'); - // TODO: Add validations - const submitButtonDisabled = useMemo( - () => !isValidRepayAmount || repaySize.lte(0), - [isValidRepayAmount, repaySize], + const newHealthFactor = userReservesSummary.healthFactor + .mul(userReservesSummary.borrowBalance) + .div(newDebtAmountUSD); + return newHealthFactor.div(userReservesSummary.currentLiquidationThreshold); + }, [userReservesSummary, newDebtAmountUSD]); + + const isValidRepayAmount = useMemo( + () => (repaySize.gt(0) ? repaySize.lte(maximumRepayAmount) : true), + [repaySize, maximumRepayAmount], ); return ( @@ -79,6 +130,7 @@ export const RepayWithWalletBalanceForm: FC< onAmountChange={setRepayAmount} invalid={!isValidRepayAmount} assetValue={repayAsset} + assetUsdValue={repayUsdAmount} onAssetChange={setRepayAsset} assetOptions={repayAssetsOptions} /> @@ -92,41 +144,70 @@ export const RepayWithWalletBalanceForm: FC< )}
- + - } - /> -
} /> + + } + />
= ({ orderOptions={orderOptions} setOrderOptions={setOrderOptions} /> + + + + + + + ); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx index bf0db4949..94444fded 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState, useCallback } from 'react'; +import React, { FC } from 'react'; import { t } from 'i18next'; import { useNavigate } from 'react-router-dom'; @@ -6,33 +6,24 @@ import { useNavigate } from 'react-router-dom'; import { Button, ButtonStyle } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; -import { BorrowPoolDetails } from '../../BorrowAssetsList.types'; -import { BorrowModalContainer } from '../BorrowModal/BorrowModalContainer'; const pageTranslations = translations.aavePage; type BorrowAssetActionProps = { - pool: BorrowPoolDetails; + onBorrowClick: () => void; }; -export const BorrowAssetAction: FC = ({ pool }) => { +export const BorrowAssetAction: FC = ({ + onBorrowClick, +}) => { const navigate = useNavigate(); - const [isBorrowModalOpen, setIsBorrowModalOpen] = useState(false); - - const handleBorrowClick = useCallback(() => { - setIsBorrowModalOpen(true); - }, []); - - const handleBorrowClose = useCallback(() => { - setIsBorrowModalOpen(false); - }, []); return (
); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx index c9f554616..37ef83178 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx @@ -14,9 +14,13 @@ const pageTranslations = translations.aavePage; type BorrowAssetDetailsProps = { pool: BorrowPoolDetails; + onBorrowClick: () => void; }; -export const BorrowAssetDetails: FC = ({ pool }) => ( +export const BorrowAssetDetails: FC = ({ + pool, + onBorrowClick, +}) => (
{/* APY */} @@ -27,7 +31,7 @@ export const BorrowAssetDetails: FC = ({ pool }) => ( } - value={} + value={} /> {/* Available */} @@ -47,6 +51,6 @@ export const BorrowAssetDetails: FC = ({ pool }) => ( />
- +
); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx index 1cd4f88f9..2518fd084 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx @@ -21,11 +21,11 @@ import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; import { config } from '../../../../../../../constants/aave'; import { useAaveBorrow } from '../../../../../../../hooks/aave/useAaveBorrow'; -import { useAaveReservesData } from '../../../../../../../hooks/aave/useAaveReservesData'; import { useAaveUserReservesData } from '../../../../../../../hooks/aave/useAaveUserReservesData'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; import { translations } from '../../../../../../../locales/i18n'; import { BorrowRateMode } from '../../../../../../../types/aave'; +import { AaveCalculations } from '../../../../../../../utils/aave/AaveCalculations'; import { CollateralRatioHealthBar } from '../../../CollateralRatioHealthBar/CollateralRatioHealthBar'; const pageTranslations = translations.aavePage; @@ -35,81 +35,73 @@ type BorrowFormProps = { onSuccess: () => void; }; -export const BorrowForm: FC = ({ asset }) => { - const reserves = useAaveReservesData(); +export const BorrowForm: FC = ({ asset, onSuccess }) => { const userReservesSummary = useAaveUserReservesData(); const [borrowAsset, setBorrowAsset] = useState(asset); const [borrowAmount, setBorrowAmount, borrowSize] = useDecimalAmountInput(''); const [acknowledge, setAcknowledge] = useState(false); const { handleBorrow } = useAaveBorrow(); - const reserve = useMemo(() => { - return reserves.find(r => r.symbol === borrowAsset); - }, [reserves, borrowAsset]); + const borrowableAssetsOptions = useMemo( + () => + userReservesSummary.reserves + .filter(r => r.reserve.borrowingEnabled) + .map(r => ({ + value: r.reserve.symbol, + label: ( + + ), + })), + [userReservesSummary.reserves], + ); - const variableBorrowAPR = useMemo(() => { - if (!reserve) return Decimal.from(0); - return Decimal.from(reserve.variableBorrowAPR).mul(100); - }, [reserve]); + const borrowReserve = useMemo(() => { + return userReservesSummary.reserves.find( + r => r.reserve.symbol === borrowAsset, + ); + }, [userReservesSummary.reserves, borrowAsset]); + + const borrowUsdAmount = useMemo(() => { + return borrowSize.mul(borrowReserve?.reserve.priceInUSD ?? 0); + }, [borrowSize, borrowReserve?.reserve.priceInUSD]); const maximumBorrowAmount = useMemo(() => { - if (!reserve || !userReservesSummary) return Decimal.from(0); + return borrowReserve?.availableToBorrow ?? Decimal.from(0); + }, [borrowReserve?.availableToBorrow]); - // deducted from hf=kte/borrowed => newhf=kte/borrowed+new_borrow => MinHealthFactor = newhf - return userReservesSummary.borrowBalance.mul( - userReservesSummary.healthFactor.div(config.MinHealthFactor).sub(1), + const newCollateralRatio = useMemo(() => { + return AaveCalculations.computeCollateralRatio( + userReservesSummary.collateralBalance, + userReservesSummary.borrowBalance.add(borrowUsdAmount), ); - }, [userReservesSummary, reserve]); + }, [ + userReservesSummary.collateralBalance, + userReservesSummary.borrowBalance, + borrowUsdAmount, + ]); - const borrowUsdAmount = useMemo(() => { - return borrowSize.mul(reserve?.priceInUSD ?? 0); - }, [borrowSize, reserve]); - - const borrowableAssetsOptions = useMemo( - () => - reserves.map(r => ({ - value: r.symbol, - label: ( - - ), - })), - [reserves], - ); + const liquidationPrice = useMemo(() => { + return AaveCalculations.computeLiquidationPrice( + borrowSize, + userReservesSummary.currentLiquidationThreshold, + userReservesSummary.collateralBalance, + ); + }, [ + borrowSize, + userReservesSummary.currentLiquidationThreshold, + userReservesSummary.collateralBalance, + ]); const isValidBorrowAmount = useMemo( () => (borrowSize.gt(0) ? borrowSize.lte(maximumBorrowAmount) : true), [borrowSize, maximumBorrowAmount], ); - const collateralRatio = useMemo(() => { - if (!userReservesSummary) return Decimal.from(0); - const newHealthFactor = userReservesSummary.healthFactor - .mul(userReservesSummary.borrowBalance) - .div(userReservesSummary.borrowBalance.add(borrowUsdAmount)); - - return newHealthFactor.div(userReservesSummary.currentLiquidationThreshold); - }, [userReservesSummary, borrowUsdAmount]); - - const liquidationPrice = useMemo(() => { - if (!borrowSize || !reserve || !userReservesSummary) { - return Decimal.from(0); - } - - return borrowSize - .mul(userReservesSummary.currentLiquidationThreshold) - .div(userReservesSummary.collateralBalance); - }, [borrowSize, reserve, userReservesSummary]); - - const submitButtonDisabled = useMemo( - () => !isValidBorrowAmount || borrowSize.lte(0) || !acknowledge || !reserve, - [isValidBorrowAmount, borrowSize, acknowledge, reserve], - ); - return (
@@ -140,25 +132,24 @@ export const BorrowForm: FC = ({ asset }) => { label={t(translations.aavePage.borrowForm.borrowApr)} value={ } /> - + + } /> = ({ asset }) => { })} value={ @@ -194,11 +185,17 @@ export const BorrowForm: FC = ({ asset }) => { onClick={async () => { handleBorrow( borrowSize, - await getAssetData(reserve!.symbol, BOB_CHAIN_ID), + await getAssetData(borrowReserve!.reserve.symbol, BOB_CHAIN_ID), BorrowRateMode.VARIABLE, + { onComplete: onSuccess }, ); }} - disabled={submitButtonDisabled} + disabled={ + !isValidBorrowAmount || + borrowSize.lte(0) || + !acknowledge || + !borrowReserve + } text={t(translations.common.buttons.confirm)} /> diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.utils.ts b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.utils.ts deleted file mode 100644 index 7b5be4fbf..000000000 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.utils.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Decimal } from '@sovryn/utils'; - -import { Reserve } from '../../../../../../../hooks/aave/useAaveReservesData'; - -export const getCollateralRatioThresholds = ( - reserve: Reserve | undefined, - eModeEnabled: boolean, -) => { - let minimumCollateralRatio: Decimal; - if (reserve !== undefined) { - minimumCollateralRatio = eModeEnabled - ? Decimal.from(reserve.formattedEModeLiquidationThreshold) - : Decimal.from(reserve.formattedReserveLiquidationThreshold); - } else { - minimumCollateralRatio = Decimal.from(1); - } - - const threeholds = { - START: minimumCollateralRatio.mul(0.9).toNumber(), - MIDDLE_START: minimumCollateralRatio.toNumber(), - MIDDLE_END: minimumCollateralRatio.mul(1.2).toNumber(), - END: minimumCollateralRatio.mul(1.6).toNumber(), - }; - - // rule of 3 basically END -> 100; {} -> x => x = {} * 100 / END - const scalingFactor = 100 / threeholds.END; - - return { - START: threeholds.START * scalingFactor, - MIDDLE_START: threeholds.MIDDLE_START * scalingFactor, - MIDDLE_END: threeholds.MIDDLE_END * scalingFactor, - END: 100, - }; -}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowModalContainer.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowModalContainer.tsx deleted file mode 100644 index 7e9c322a2..000000000 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowModalContainer.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React, { FC } from 'react'; - -import { t } from 'i18next'; - -import { Dialog, DialogBody, DialogHeader } from '@sovryn/ui'; - -import { translations } from '../../../../../../../locales/i18n'; -import { BorrowForm } from './BorrowForm'; - -type BorrowModalContainerProps = { - asset: string; - isOpen: boolean; - handleCloseModal: () => void; -}; - -export const BorrowModalContainer: FC = ({ - asset, - isOpen, - handleCloseModal, -}) => ( - - - - - - -); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx index 979f9a652..2a56f3d48 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.constants.tsx @@ -9,13 +9,13 @@ import { BOB_CHAIN_ID } from '../../../../../config/chains'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { translations } from '../../../../../locales/i18n'; -import { LoanType } from '../../../../../utils/aave/AaveUserReservesSummary'; +import { BorrowRateMode } from '../../../../../types/aave'; import { BorrowPosition } from './BorrowPositionsList.types'; import { BorrowPositionAction } from './components/BorrowPositionAction/BorrowPositionAction'; const pageTranslations = translations.aavePage; -export const COLUMNS_CONFIG = [ +export const COLUMNS_CONFIG = (onRepayClick: (asset: string) => unknown) => [ { id: 'asset', sortable: true, @@ -78,7 +78,7 @@ export const COLUMNS_CONFIG = [ {t( pageTranslations.common[ - position.type === LoanType.STABLE ? 'stable' : 'variable' + position.type === BorrowRateMode.STABLE ? 'stable' : 'variable' ], )} @@ -89,7 +89,7 @@ export const COLUMNS_CONFIG = [ align: Align.center, title: ' ', cellRenderer: (position: BorrowPosition) => ( - + onRepayClick(position.asset)} /> ), }, ]; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index 87989cdfb..3ce35d09c 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -2,7 +2,15 @@ import React, { FC, useCallback, useState } from 'react'; import { t } from 'i18next'; -import { Accordion, OrderOptions, Paragraph, Table } from '@sovryn/ui'; +import { + Accordion, + Dialog, + DialogBody, + DialogHeader, + OrderOptions, + Paragraph, + Table, +} from '@sovryn/ui'; import { Decimal } from '@sovryn/utils'; import { AaveRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; @@ -13,14 +21,15 @@ import { COLUMNS_CONFIG } from './BorrowPositionsList.constants'; import { BorrowPosition } from './BorrowPositionsList.types'; import { BorrowPositionDetails } from './components/BorrowPositionDetails/BorrowPositionDetails'; import { EfficiencyModeCard } from './components/EfficiencyModeCard/EfficiencyModeCard'; +import { RepayForm } from './components/RepayForm/RepayForm'; const pageTranslations = translations.aavePage; type BorrowPositionsListProps = { borrowPositions: BorrowPosition[]; - borrowBalance?: Decimal; - borrowWeightedApy?: Decimal; - borrowPowerUsed?: Decimal; + borrowBalance: Decimal; + borrowWeightedApy: Decimal; + borrowPowerUsed: Decimal; eModeEnabled: boolean; }; @@ -34,6 +43,17 @@ export const BorrowPositionsList: FC = ({ const { account } = useAccount(); const [open, setOpen] = useState(true); const [orderOptions, setOrderOptions] = useState(); + const [repayAssetDialog, setRepayAssetDialog] = useState< + string | undefined + >(); + + const onRepayClick = useCallback((asset: string) => { + setRepayAssetDialog(asset); + }, []); + + const onRepayClose = useCallback(() => { + setRepayAssetDialog(undefined); + }, []); const rowTitleRenderer = useCallback( (r: BorrowPosition) => ( @@ -48,8 +68,13 @@ export const BorrowPositionsList: FC = ({ ); const mobileRenderer = useCallback( - p => , - [], + p => ( + onRepayClick(p.asset)} + /> + ), + [onRepayClick], ); return ( @@ -79,25 +104,26 @@ export const BorrowPositionsList: FC = ({
+
= ({ orderOptions={orderOptions} setOrderOptions={setOrderOptions} /> + + + + + + + ) : (
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx index 8445cf994..4b632e359 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.types.tsx @@ -1,11 +1,11 @@ import { Decimal } from '@sovryn/utils'; -import { LoanType } from '../../../../../utils/aave/AaveUserReservesSummary'; +import { BorrowRateMode } from '../../../../../types/aave'; export type BorrowPosition = { asset: string; borrowed: Decimal; borrowedUSD: Decimal; apy: Decimal; - type: LoanType; + type: BorrowRateMode; }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionAction/BorrowPositionAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionAction/BorrowPositionAction.tsx index b4e01a2bc..c2c358f14 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionAction/BorrowPositionAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionAction/BorrowPositionAction.tsx @@ -1,44 +1,24 @@ -import React, { FC, useCallback, useState } from 'react'; +import React, { FC } from 'react'; import { t } from 'i18next'; import { Button, ButtonStyle } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; -import { BorrowPosition } from '../../BorrowPositionsList.types'; -import { RepayModalContainer } from '../RepayModal/RepayModalContainer'; type BorrowPositionActionProps = { - position: BorrowPosition; + onRepayClick: () => void; }; export const BorrowPositionAction: FC = ({ - position, -}) => { - const [isRepayModalOpen, setIsRepayModalOpen] = useState(false); - - const handleRepayClick = useCallback(() => { - setIsRepayModalOpen(true); - }, []); - - const handleRepayClose = useCallback(() => { - setIsRepayModalOpen(false); - }, []); - - return ( -
-
- ); -}; + onRepayClick, +}) => ( +
+
+); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx index 40352742e..f4dd3d725 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowPositionDetails/BorrowPositionDetails.tsx @@ -7,16 +7,18 @@ import { HelperButton, SimpleTableRow } from '@sovryn/ui'; import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { AssetAmountPriceRenderer } from '../../../../../../2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer'; import { translations } from '../../../../../../../locales/i18n'; -import { LoanType } from '../../../../../../../utils/aave/AaveUserReservesSummary'; +import { BorrowRateMode } from '../../../../../../../types/aave'; import { BorrowPosition } from '../../BorrowPositionsList.types'; import { BorrowPositionAction } from '../BorrowPositionAction/BorrowPositionAction'; type BorrowPositionDetailsProps = { position: BorrowPosition; + onRepayClick: () => unknown; }; export const BorrowPositionDetails: FC = ({ position, + onRepayClick, }) => (
@@ -48,12 +50,12 @@ export const BorrowPositionDetails: FC = ({ label={t(translations.aavePage.common.apyType)} value={t( translations.aavePage.common[ - position.type === LoanType.STABLE ? 'stable' : 'variable' + position.type === BorrowRateMode.STABLE ? 'stable' : 'variable' ], )} />
- +
); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx new file mode 100644 index 000000000..ad48869e4 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx @@ -0,0 +1,65 @@ +import React, { FC, useMemo, useState } from 'react'; + +import { t } from 'i18next'; + +import { Paragraph, ParagraphSize, Tabs, TabType } from '@sovryn/ui'; + +import { translations } from '../../../../../../../locales/i18n'; +import { RepayWithCollateralForm } from './RepayWithCollateralForm'; +import { RepayWithWalletBalanceForm } from './RepayWithWalletBalanceForm'; + +type RepayFormProps = { + asset: string; + onSuccess: () => unknown; +}; + +enum RepayWith { + BALANCE = 0, + COLLATERAL, +} + +export const RepayForm: FC = ({ asset, onSuccess }) => { + const [activeTab, setActiveTab] = useState(RepayWith.BALANCE); + + const tabItems = useMemo(() => { + return [ + { + activeClassName: 'text-primary-20', + dataAttribute: 'wallet-balance', + label: t(translations.aavePage.repayModal.walletBalance), + }, + { + activeClassName: 'text-primary-20', + dataAttribute: 'collateral', + label: t(translations.aavePage.common.collateral), + disabled: true, + }, + ]; + }, []); + + return ( +
+ + {t(translations.aavePage.repayModal.repayWith)} + + + + + {activeTab === RepayWith.BALANCE ? ( + + ) : ( + + )} +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx similarity index 97% rename from apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx rename to apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx index 31bb23995..5fb5c34cd 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx @@ -16,6 +16,7 @@ import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/Amo import { AmountTransition } from '../../../../../../2_molecules/AmountTransition/AmountTransition'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; +import { config } from '../../../../../../../constants/aave'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; import { translations } from '../../../../../../../locales/i18n'; import { CollateralRatioHealthBar } from '../../../CollateralRatioHealthBar/CollateralRatioHealthBar'; @@ -137,7 +138,10 @@ export const RepayWithCollateralForm: FC = () => { )}
- + = ({ asset }) => { +> = ({ asset, onSuccess }) => { const { account } = useAccount(); const { handleRepay } = useAaveRepay(); - const reserves = useAaveReservesData(); const userReservesSummary = useAaveUserReservesData(); const [repayAsset, setRepayAsset] = useState(asset); const [repayAmount, setRepayAmount, repaySize] = useDecimalAmountInput(''); @@ -50,69 +50,58 @@ export const RepayWithWalletBalanceForm: FC< const repayAssetsOptions = useMemo( () => - userReservesSummary - ? userReservesSummary.borrowedAssets.map(ba => ({ - value: ba.asset, - label: ( - - ), - })) - : [], + userReservesSummary.reserves + .filter(r => r.borrowed.gt(0)) + .map(ba => ({ + value: ba.asset, + label: ( + + ), + })), [userReservesSummary], ); - const debt = useMemo(() => { - return userReservesSummary?.borrowedAssets.find( - a => a.asset === repayAsset, + const repayReserve = useMemo(() => { + return userReservesSummary.reserves.find( + r => r.reserve.symbol === repayAsset, ); - }, [userReservesSummary, repayAsset]); + }, [userReservesSummary.reserves, repayAsset]); const maximumRepayAmount = useMemo(() => { - return debt - ? debt.borrowed.gt(repayAssetBalance) + return repayReserve + ? repayReserve.borrowed.gt(repayAssetBalance) ? repayAssetBalance - : debt.borrowed + : repayReserve.borrowed : Decimal.from(0); - }, [debt, repayAssetBalance]); - - const reserve = useMemo(() => { - return reserves.find(r => r.symbol === repayAsset); - }, [reserves, repayAsset]); + }, [repayReserve, repayAssetBalance]); const repayUsdAmount = useMemo(() => { - return repaySize.mul(reserve?.priceInUSD ?? 0); - }, [repaySize, reserve]); + return repaySize.mul(repayReserve?.reserve.priceInUSD ?? 0); + }, [repaySize, repayReserve]); const newDebtAmount = useMemo(() => { - const nd = debt?.borrowed ? debt.borrowed.sub(repaySize) : Decimal.from(0); - return nd.gt(0) ? nd : Decimal.from(0); - }, [debt, repaySize]); - - const newDebtAmountUSD = useMemo(() => { - return newDebtAmount.mul(reserve?.priceInUSD ?? 0); - }, [newDebtAmount, reserve]); + const newDebt = repayReserve?.borrowed + ? repayReserve.borrowed.sub(repaySize) + : Decimal.from(0); - const collateralRatio = useMemo(() => { - if (!userReservesSummary) return Decimal.from(0); + // avoid negatives: + return newDebt.gt(0) ? newDebt : Decimal.from(0); + }, [repayReserve?.borrowed, repaySize]); - return userReservesSummary.healthFactor.div( - userReservesSummary.currentLiquidationThreshold, - ); - }, [userReservesSummary]); + const newDebtAmountUSD = useMemo(() => { + return newDebtAmount.mul(repayReserve?.reserve.priceInUSD ?? 0); + }, [newDebtAmount, repayReserve]); const newCollateralRatio = useMemo(() => { - if (!userReservesSummary) return Decimal.from(0); - if (newDebtAmountUSD.eq(0)) return Decimal.from('100000000'); - - const newHealthFactor = userReservesSummary.healthFactor - .mul(userReservesSummary.borrowBalance) - .div(newDebtAmountUSD); - return newHealthFactor.div(userReservesSummary.currentLiquidationThreshold); + return AaveCalculations.computeCollateralRatio( + userReservesSummary.collateralBalance, + userReservesSummary.borrowBalance.add(newDebtAmountUSD), + ); }, [userReservesSummary, newDebtAmountUSD]); const isValidRepayAmount = useMemo( @@ -144,7 +133,10 @@ export const RepayWithWalletBalanceForm: FC< )} - + diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx deleted file mode 100644 index 0bc5e8811..000000000 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, { FC, useState } from 'react'; - -import { t } from 'i18next'; - -import { - Dialog, - DialogBody, - DialogHeader, - Paragraph, - ParagraphSize, - Tabs, - TabType, -} from '@sovryn/ui'; - -import { translations } from '../../../../../../../locales/i18n'; -import { RepayWithCollateralForm } from './RepayWithCollateralForm'; -import { RepayWithWalletBalanceForm } from './RepayWithWalletBalanceForm'; - -type RepayModalContainerProps = { - asset: string; - isOpen: boolean; - handleCloseModal: () => void; -}; - -enum RepayWith { - BALANCE = 0, - COLLATERAL, -} - -export const RepayModalContainer: FC = ({ - asset, - isOpen, - handleCloseModal, -}) => { - const [activeTab, setActiveTab] = useState(RepayWith.BALANCE); - - return ( - - - - - {t(translations.aavePage.repayModal.repayWith)} - - - - - {activeTab === RepayWith.BALANCE ? ( - - ) : ( - - )} - - - ); -}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.tsx b/apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.tsx index d9cdfeded..9779d46fc 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.tsx @@ -11,21 +11,16 @@ import { getCollateralRatioThresholds } from './CollateralRatioHealthBar.utils'; type CollateralRatioHealthBarProps = { ratio: Decimal; - thresholds?: { - START: number; - MIDDLE_START: number; - MIDDLE_END: number; - END: number; - }; + minimum: Decimal; }; export const CollateralRatioHealthBar: FC = ({ ratio, - thresholds, + minimum, }) => { const collateralRatioThresholds = useMemo( - () => thresholds ?? getCollateralRatioThresholds(), - [thresholds], + () => getCollateralRatioThresholds(minimum), + [minimum], ); return ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.utils.tsx b/apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.utils.tsx index fb0b10ad4..a70156331 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.utils.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/CollateralRatioHealthBar/CollateralRatioHealthBar.utils.tsx @@ -1,8 +1,7 @@ -import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS } from '../../../../../constants/lending'; +import { Decimal } from '@sovryn/utils'; -export const getCollateralRatioThresholds = () => { - const minimumCollateralRatio = - MINIMUM_COLLATERAL_RATIO_LENDING_POOLS.mul(100); +export const getCollateralRatioThresholds = (minimum: Decimal) => { + const minimumCollateralRatio = minimum.mul(100); return { START: minimumCollateralRatio.mul(0.9).toNumber(), diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx index cdcf6165c..823282578 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx @@ -10,12 +10,11 @@ import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRen import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { translations } from '../../../../../locales/i18n'; import { LendPoolDetails } from './LendAssetsList.types'; -import { AssetBalanceRenderer } from './components/AssetBalance/AssetBalance'; import { LendAssetAction } from './components/LendAssetAction/LendAssetAction'; const pageTranslations = translations.aavePage; -export const COLUMNS_CONFIG = [ +export const COLUMNS_CONFIG = (onLendClick: (asset: string) => unknown) => [ { id: 'asset', sortable: true, @@ -38,15 +37,15 @@ export const COLUMNS_CONFIG = [ { id: 'walletBalance', sortable: true, - align: Align.center, className: '[&_*]:mx-auto [&_*]:space-x-2', // center head + align: Align.center, title: ( - + {t(pageTranslations.lendAssetsList.walletBalance)} ), cellRenderer: (pool: LendPoolDetails) => ( - + ), }, { @@ -88,6 +87,8 @@ export const COLUMNS_CONFIG = [ id: 'actions', align: Align.center, title: ' ', - cellRenderer: (pool: LendPoolDetails) => , + cellRenderer: (pool: LendPoolDetails) => ( + onLendClick(pool.asset)} /> + ), }, ]; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx index 5cf632610..19dcd5e24 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.tsx @@ -1,14 +1,23 @@ -import React, { FC, useCallback, useState } from 'react'; +import React, { FC, useCallback, useMemo, useState } from 'react'; import { t } from 'i18next'; -import { Accordion, Checkbox, OrderOptions, Table } from '@sovryn/ui'; +import { + Accordion, + Checkbox, + Dialog, + DialogBody, + DialogHeader, + OrderOptions, + Table, +} from '@sovryn/ui'; import { AaveRowTitle } from '../../../../2_molecules/AavePoolRowTitle/AavePoolRowTitle'; import { translations } from '../../../../../locales/i18n'; import { COLUMNS_CONFIG } from './LendAssetsList.constants'; import { LendPoolDetails } from './LendAssetsList.types'; import { LendAssetDetails } from './components/LendAssetDetails/LendAssetDetails'; +import { LendForm } from './components/LendForm/LendForm'; const pageTranslations = translations.aavePage; @@ -20,8 +29,21 @@ export const LendAssetsList: FC = ({ lendPools }) => { const [open, setOpen] = useState(true); const [showZeroBalances, setShowZeroBalances] = useState(true); const [orderOptions, setOrderOptions] = useState(); + const [lendAssetDialog, setLendAssetDialog] = useState(); + + const onLendClick = useCallback((asset: string) => { + setLendAssetDialog(asset); + }, []); + + const onLendClose = useCallback(() => { + setLendAssetDialog(undefined); + }, []); + + const mobileRenderer = useCallback( + p => onLendClick(p.asset)} />, + [onLendClick], + ); - const mobileRenderer = useCallback(p => , []); const rowTitleRenderer = useCallback( row => ( = ({ lendPools }) => { [], ); + const filteredLendPools = useMemo(() => { + if (!showZeroBalances) { + return lendPools.filter(p => p.walletBalance.gt(0)); + } + return lendPools; + }, [lendPools, showZeroBalances]); + return ( = ({ lendPools }) => { />
+ + + + + + + ); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.types.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.types.tsx index d5232ddf5..49bd834ec 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.types.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.types.tsx @@ -4,4 +4,5 @@ export type LendPoolDetails = { asset: string; apy: Decimal; canBeCollateral: boolean; + walletBalance: Decimal; }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/AssetBalance/AssetBalance.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/AssetBalance/AssetBalance.tsx index 2f8de9374..b52ea6ddf 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/AssetBalance/AssetBalance.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/AssetBalance/AssetBalance.tsx @@ -1,7 +1,10 @@ import React, { FC } from 'react'; + +import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; + import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; -import { useAssetBalance } from '../../../../../../../hooks/useAssetBalance'; import { useAccount } from '../../../../../../../hooks/useAccount'; +import { useAssetBalance } from '../../../../../../../hooks/useAssetBalance'; export type AssetBalanceRendererProps = { asset: string; @@ -10,8 +13,8 @@ export type AssetBalanceRendererProps = { export const AssetBalanceRenderer: FC = ({ asset, }) => { - const balance = useAssetBalance(asset); const { account } = useAccount(); + const balance = useAssetBalance(asset, BOB_CHAIN_ID); return account ? ( void; }; -export const LendAssetAction: FC = ({ pool }) => { +export const LendAssetAction: FC = ({ onLendClick }) => { const navigate = useNavigate(); - const [isLendModalOpen, setIsLendModalOpen] = useState(false); - - const handleLendClick = useCallback(() => { - setIsLendModalOpen(true); - }, []); - - const handleLendClose = useCallback(() => { - setIsLendModalOpen(false); - }, []); return (
); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx index 50f6615d6..08e01c502 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx @@ -13,9 +13,13 @@ import { LendAssetAction } from '../LendAssetAction/LendAssetAction'; type LendAssetDetailsProps = { pool: LendPoolDetails; + onLendClick: () => unknown; }; -export const LendAssetDetails: FC = ({ pool }) => ( +export const LendAssetDetails: FC = ({ + pool, + onLendClick, +}) => (
{/* Available */} @@ -34,6 +38,7 @@ export const LendAssetDetails: FC = ({ pool }) => ( value={} /> + {/* Can be collateral */} = ({ pool }) => ( />
- +
); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendModal/LendForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendForm/LendForm.tsx similarity index 95% rename from apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendModal/LendForm.tsx rename to apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendForm/LendForm.tsx index ae3d54fe9..0d492b254 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendModal/LendForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendForm/LendForm.tsx @@ -46,10 +46,6 @@ export const LendForm: FC = ({ ); const { handleDeposit } = useAaveSupply(); - const reserve = useMemo(() => { - return reserves.find(r => r.symbol === lendAsset) ?? reserves[0]; - }, [reserves, lendAsset]); - const lendAssetsOptions = useMemo( () => reserves.map(r => ({ @@ -66,20 +62,19 @@ export const LendForm: FC = ({ [reserves], ); - const isValidLendAmount = useMemo( - () => (lendSize.gt(0) ? lendSize.lte(lendAssetBalance) : true), - [lendSize, lendAssetBalance], - ); - - const submitButtonDisabled = useMemo( - () => !isValidLendAmount || lendSize.lte(0), - [isValidLendAmount, lendSize], - ); + const reserve = useMemo(() => { + return reserves.find(r => r.symbol === lendAsset) ?? reserves[0]; + }, [reserves, lendAsset]); const assetUsdValue: Decimal = useMemo(() => { return Decimal.from(reserve?.priceInUSD ?? 0).mul(lendSize); }, [reserve, lendSize]); + const isValidLendAmount = useMemo( + () => (lendSize.gt(0) ? lendSize.lte(lendAssetBalance) : true), + [lendSize, lendAssetBalance], + ); + return (
@@ -127,11 +122,12 @@ export const LendForm: FC = ({
= ({ orderOptions={orderOptions} setOrderOptions={setOrderOptions} /> + + + + + + + ) : (
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx index f28eb9848..fe8044f4e 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx @@ -1,43 +1,25 @@ -import React, { FC, useCallback, useState } from 'react'; +import React, { FC } from 'react'; import { t } from 'i18next'; import { Button, ButtonStyle } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; -import { LendPosition } from '../../LendPositionsList.types'; -import { WithdrawModalContainer } from '../WithdrawModal/WithdrawModalContainer'; type LendPositionActionProps = { - position: LendPosition; + onWithdrawClick: () => unknown; }; export const LendPositionAction: FC = ({ - position, + onWithdrawClick, }) => { - const [isWithdrawModalOpen, setIsWithdrawModalOpen] = useState(false); - - const handleWithdrawClick = useCallback(() => { - setIsWithdrawModalOpen(true); - }, []); - - const handleWithdrawClose = useCallback(() => { - setIsWithdrawModalOpen(false); - }, []); - return (
); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx index b1010515a..58ed5a6b1 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionDetails/LendPositionDetails.tsx @@ -13,13 +13,16 @@ import { ToggleCollateralAction } from '../ToggleCollateralAction/ToggleCollater type LendPositionDetailsProps = { position: LendPosition; + onWithdrawClick: (asset: string) => void; }; export const LendPositionDetails: FC = ({ position, + onWithdrawClick, }) => (
+ {/* Balance */} = ({ } /> + {/* APY */} @@ -44,12 +48,15 @@ export const LendPositionDetails: FC = ({ value={} /> + {/* Can be collateral */} } />
- + onWithdrawClick(position.asset)} + />
); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx similarity index 73% rename from apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawForm.tsx rename to apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx index f984a894c..144c23628 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx @@ -19,7 +19,6 @@ import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; -import { useAaveReservesData } from '../../../../../../../hooks/aave/useAaveReservesData'; import { useAaveUserReservesData } from '../../../../../../../hooks/aave/useAaveUserReservesData'; import { useAaveWithdraw } from '../../../../../../../hooks/aave/useAaveWithdraw'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; @@ -34,7 +33,6 @@ type WithdrawFormProps = { export const WithdrawForm: FC = ({ asset }) => { const { handleWithdraw } = useAaveWithdraw(); - const reserves = useAaveReservesData(); const userReservesSummary = useAaveUserReservesData(); const [withdrawAsset, setWithdrawAsset] = useState(asset); const [withdrawAmount, setWithdrawAmount, withdrawSize] = @@ -42,36 +40,37 @@ export const WithdrawForm: FC = ({ asset }) => { const withdrawableAssetsOptions = useMemo( () => - !userReservesSummary - ? [] - : userReservesSummary.suppliedAssets.map(sa => ({ - value: sa.asset, - label: ( - - ), - })), + userReservesSummary.reserves + .filter(r => r.supplied.gt(0)) + .map(sa => ({ + value: sa.asset, + label: ( + + ), + })), [userReservesSummary], ); - const maximumWithdrawAmount: Decimal = useMemo(() => { - if (!userReservesSummary) return Decimal.from(0); - const sa = userReservesSummary.suppliedAssets.find( - sa => sa.asset === withdrawAsset, + const withdrawReserve = useMemo(() => { + return userReservesSummary.reserves.find( + r => r.reserve.symbol === withdrawAsset, ); - return sa ? sa.supplied : Decimal.from(0); - }, [userReservesSummary, withdrawAsset]); + }, [withdrawAsset, userReservesSummary]); - const withdrawAmountUsd: Decimal = useMemo(() => { - if (!reserves) return Decimal.from(0); - const reserve = reserves.find(r => r.symbol === withdrawAsset); + const maximumWithdrawAmount: Decimal = useMemo(() => { + return withdrawReserve ? withdrawReserve.supplied : Decimal.from(0); + }, [withdrawReserve]); - return reserve ? withdrawSize.mul(reserve.priceInUSD) : Decimal.from(0); - }, [withdrawSize, reserves, withdrawAsset]); + const withdrawAmountUsd: Decimal = useMemo(() => { + return withdrawReserve + ? withdrawSize.mul(withdrawReserve.reserve.priceInUSD) + : Decimal.from(0); + }, [withdrawSize, withdrawReserve]); const remainingSupply = useMemo( () => maximumWithdrawAmount.sub(withdrawSize), @@ -88,21 +87,22 @@ export const WithdrawForm: FC = ({ asset }) => { [isValidWithdrawAmount, withdrawSize], ); + const tabItems = useMemo( + () => [ + // For now just withdraw is supported + { + activeClassName: 'text-primary-20', + dataAttribute: 'withdraw', + label: t(translations.common.withdraw), + }, + ], + [], + ); + return (
- +
unknown; -}; - -export const WithdrawModalContainer: FC = ({ - asset, - isOpen, - handleCloseModal, -}) => ( - - - - - - -); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx index 1dacbc811..86b8a9723 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/TopPanel/TopPanel.tsx @@ -13,9 +13,9 @@ import { translations } from '../../../../../locales/i18n'; const pageTranslations = translations.aavePage.topPanel; type TopPanelProps = { - netWorth?: Decimalish; - netApy?: Decimalish; - healthFactor?: Decimalish; + netWorth: Decimalish; + netApy: Decimalish; + healthFactor: Decimalish; }; export const TopPanel: FC = ({ @@ -41,7 +41,7 @@ export const TopPanel: FC = ({ = ({ = ({ { @@ -31,7 +30,7 @@ export const useAaveRepay = () => { async ( amount: Decimal, asset: AssetDetailsData, - loanType: LoanType, + borrowRateMode: BorrowRateMode, opts?: TransactionFactoryOptions, ) => { if (!aaveRepayTransactionsFactory) { @@ -45,7 +44,7 @@ export const useAaveRepay = () => { await aaveRepayTransactionsFactory.repay( asset, bnAmount, - loanType, + borrowRateMode, opts, ), ); diff --git a/apps/frontend/src/hooks/aave/useAaveReservesData.tsx b/apps/frontend/src/hooks/aave/useAaveReservesData.tsx index 6c99852fc..768f30efd 100644 --- a/apps/frontend/src/hooks/aave/useAaveReservesData.tsx +++ b/apps/frontend/src/hooks/aave/useAaveReservesData.tsx @@ -18,7 +18,7 @@ export type Reserve = ReserveDataHumanized & FormatReserveUSDResponse; export type ReserveData = Reserve[]; export const useAaveReservesData = (): ReserveData => { - const provider = config.provider; // TODO: replace with useAccount + const provider = config.provider; // TODO: replace with useAccount. Circular dependency error... const uiPoolDataProvider = useMemo( () => diff --git a/apps/frontend/src/hooks/aave/useAaveUserReservesData.tsx b/apps/frontend/src/hooks/aave/useAaveUserReservesData.tsx index b110be088..fb1133932 100644 --- a/apps/frontend/src/hooks/aave/useAaveUserReservesData.tsx +++ b/apps/frontend/src/hooks/aave/useAaveUserReservesData.tsx @@ -1,22 +1,25 @@ import { UiPoolDataProvider } from '@aave/contract-helpers'; import { formatReserves, formatUserSummary } from '@aave/math-utils'; -import { useMemo } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import dayjs from 'dayjs'; -import { BOB_CHAIN_ID } from '../../config/chains'; - import { config } from '../../constants/aave'; -import { AaveUserReservesSummary } from '../../utils/aave/AaveUserReservesSummary'; -import { useCachedData } from '../useCachedData'; - -type UserReservesData = AaveUserReservesSummary | null; +import { + AaveUserReservesSummary, + AaveUserReservesSummaryFactory, +} from '../../utils/aave/AaveUserReservesSummary'; +import { useAccount } from '../useAccount'; +import { useBlockNumber } from '../useBlockNumber'; -export const useAaveUserReservesData = (): UserReservesData => { - const provider = config.provider; - const account = '0xF754D0f4de0e815b391D997Eeec5cD07E59858F0'; - // const { account, provider } = useAccount(); TODO: activate this instead of 2 above once calculations in bob are possible +export const useAaveUserReservesData = (): AaveUserReservesSummary => { + const { account, provider } = useAccount(); + const [value, setValue] = useState( + AaveUserReservesSummaryFactory.buildZeroSummary([]), + ); + const { value: blockNumber } = useBlockNumber(); + const [processedBlock, setProcessedBlock] = useState(); const uiPoolDataProvider = useMemo( () => @@ -30,49 +33,54 @@ export const useAaveUserReservesData = (): UserReservesData => { [provider], ); - const { value } = useCachedData( - `AaveUserReservesData/${account}`, - BOB_CHAIN_ID, - async () => { - if (!account || !uiPoolDataProvider) { - return null; - } + const loadUserReservesData = useCallback(async () => { + if (!account || !provider || !uiPoolDataProvider || !blockNumber) { + return null; + } - const [reservesData, userReservesData] = await Promise.all([ - uiPoolDataProvider.getReservesHumanized({ - lendingPoolAddressProvider: config.PoolAddressesProviderAddress, - }), - uiPoolDataProvider.getUserReservesHumanized({ - lendingPoolAddressProvider: config.PoolAddressesProviderAddress, - user: account, - }), - ]); - const { + const [reservesData, userReservesData] = await Promise.all([ + uiPoolDataProvider.getReservesHumanized({ + lendingPoolAddressProvider: config.PoolAddressesProviderAddress, + }), + uiPoolDataProvider.getUserReservesHumanized({ + lendingPoolAddressProvider: config.PoolAddressesProviderAddress, + user: account, + }), + ]); + const { + marketReferenceCurrencyDecimals, + marketReferenceCurrencyPriceInUsd: marketReferencePriceInUsd, + } = reservesData.baseCurrencyData; + const currentTimestamp = dayjs().unix(); + const userSummary = formatUserSummary({ + currentTimestamp, + marketReferencePriceInUsd, + marketReferenceCurrencyDecimals, + userReserves: userReservesData.userReserves, + userEmodeCategoryId: userReservesData.userEmodeCategoryId, + formattedReserves: formatReserves({ + currentTimestamp, + marketReferencePriceInUsd, marketReferenceCurrencyDecimals, - marketReferenceCurrencyPriceInUsd: marketReferencePriceInUsd, - } = reservesData.baseCurrencyData; - const currentTimestamp = dayjs().unix(); + reserves: reservesData.reservesData, + }), + }); - return AaveUserReservesSummary.from( - formatUserSummary({ - currentTimestamp, - marketReferencePriceInUsd, - marketReferenceCurrencyDecimals, - userReserves: userReservesData.userReserves, - userEmodeCategoryId: userReservesData.userEmodeCategoryId, - formattedReserves: formatReserves({ - currentTimestamp, - marketReferencePriceInUsd, - marketReferenceCurrencyDecimals, - reserves: reservesData.reservesData, - }), - }), - ); - }, - [uiPoolDataProvider, account], - null, - { ttl: 1000 * 60, fallbackToPreviousResult: true }, - ); + setValue( + await AaveUserReservesSummaryFactory.buildSummary( + provider, + account, + userSummary, + ), + ); + setProcessedBlock(blockNumber); + }, [account, uiPoolDataProvider, blockNumber, provider]); + + useEffect(() => { + if (blockNumber !== processedBlock) { + loadUserReservesData(); + } + }, [loadUserReservesData, processedBlock, blockNumber]); return value; }; diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index ef9d878a5..2bee35fc2 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -814,7 +814,7 @@ "tx": { "supplyTitle": "Deposit {{symbol}} to the pool", "supplySubtitle": "Transfers {{amount}} {{symbol}} to the pool", - "withdrawTitle": "Withdraw {{asset}} from the pool", + "withdrawTitle": "Withdraw {{symbol}} from the pool", "withdrawSubtitle": "Transfer {{amount}} {{symbol}} from the pool", "repayTitle":"Repay loan", "repaySubtitle": "Repay {{amount}} {{symbol}} to the pool", diff --git a/apps/frontend/src/utils/aave/AaveCalculations.ts b/apps/frontend/src/utils/aave/AaveCalculations.ts new file mode 100644 index 000000000..391509699 --- /dev/null +++ b/apps/frontend/src/utils/aave/AaveCalculations.ts @@ -0,0 +1,140 @@ +import { Decimal } from '@sovryn/utils'; + +import { UserSummary } from './AaveUserReservesSummary'; + +export class AaveCalculations { + static computeNetApy( + weightedSupplyApy: Decimal, + suppliedBalance: Decimal, + weightedBorrowApy: Decimal, + borrowedBalance: Decimal, + netWorthUsd: Decimal, + ): Decimal { + if (netWorthUsd.eq(0)) { + return Decimal.from(0); + } + + return weightedSupplyApy + .mul(suppliedBalance) + .div(netWorthUsd) + .sub(weightedBorrowApy.mul(borrowedBalance).div(netWorthUsd)); + } + + static computeSuppliedBalance( + reserves: UserSummary['userReservesData'], + ): Decimal { + return reserves.reduce( + (suppliedBalance, r) => suppliedBalance.add(r.underlyingBalanceUSD), + Decimal.from(0), + ); + } + + static computeBorrowedBalance( + reserves: UserSummary['userReservesData'], + ): Decimal { + return reserves.reduce( + (borrowedBalance, r) => borrowedBalance.add(r.totalBorrows), + Decimal.from(0), + ); + } + + static computeBorrowPower( + availableBorrowsUSD: Decimal, + borrowedBalance: Decimal, + ) { + return Decimal.from(availableBorrowsUSD).add(borrowedBalance); + } + + static computeBorrowPowerUsed( + borrowedBalance: Decimal, + borrowPower: Decimal, + ) { + if (borrowPower.eq(0)) { + return Decimal.from(100); // all used + } + return Decimal.from(borrowedBalance).div(borrowPower).mul(100); + } + + static computeCollateral(reserves: UserSummary['userReservesData']): Decimal { + return reserves.reduce( + (collateral, r) => + collateral.add( + r.usageAsCollateralEnabledOnUser ? r.underlyingBalanceUSD : 0, + ), + Decimal.from(0), + ); + } + + static computeHealthFactor( + collateral: Decimal, + currentLiquidationThreshold: Decimal, + borrowedBalance: Decimal, + ): Decimal { + if (borrowedBalance.eq(0)) { + return Decimal.from(Infinity); + } + return collateral.mul(currentLiquidationThreshold).div(borrowedBalance); + } + + static computeCollateralRatio(collateral: Decimal, borrowedBalance: Decimal) { + if (borrowedBalance.eq(0)) { + return Decimal.from(Infinity); + } + return collateral.div(borrowedBalance); + } + + static computeWeightedBorrowApy( + reserves: UserSummary['userReservesData'], + ): Decimal { + let totalBorrowedUSD = Decimal.from(0); + let weightedBorrowAPYSum = Decimal.from(0); + + reserves.forEach(reserve => { + const borrowedAmountUSD = Decimal.from(reserve.totalBorrowsUSD); + const borrowAPY = Decimal.from(reserve.reserve.variableBorrowAPY); + + weightedBorrowAPYSum = weightedBorrowAPYSum.add( + borrowAPY.mul(borrowedAmountUSD), + ); + totalBorrowedUSD = totalBorrowedUSD.add(borrowedAmountUSD); + }); + + if (totalBorrowedUSD.eq(0) || weightedBorrowAPYSum.eq(0)) { + return Decimal.from(0); + } + return weightedBorrowAPYSum.div(totalBorrowedUSD).mul(100); + } + + static computeWeightedSupplyApy( + reserves: UserSummary['userReservesData'], + ): Decimal { + let totalSuppliedUSD = Decimal.from(0); + let weightedSupplyAPYSum = Decimal.from(0); + + reserves.forEach(reserve => { + const suppliedAmountUSD = Decimal.from(reserve.underlyingBalanceUSD); + const supplyAPY = Decimal.from(reserve.reserve.supplyAPY); + + weightedSupplyAPYSum = weightedSupplyAPYSum.add( + supplyAPY.mul(suppliedAmountUSD), + ); + totalSuppliedUSD = totalSuppliedUSD.add(suppliedAmountUSD); + }); + + if (totalSuppliedUSD.eq(0) || weightedSupplyAPYSum.eq(0)) { + return Decimal.from(0); + } + return weightedSupplyAPYSum.div(totalSuppliedUSD).mul(100); + } + + static computeLiquidationPrice( + borrowSize: Decimal, + currentLiquidationThreshold: Decimal, + collateralBalance: Decimal, + ) { + if (collateralBalance.eq(0)) { + return Decimal.from(Infinity); // get liquidated right away + } + return borrowSize.mul(currentLiquidationThreshold).div(collateralBalance); + } +} diff --git a/apps/frontend/src/utils/aave/AaveRepayTransactionsFactory.ts b/apps/frontend/src/utils/aave/AaveRepayTransactionsFactory.ts index c437f1b59..05f8e3522 100644 --- a/apps/frontend/src/utils/aave/AaveRepayTransactionsFactory.ts +++ b/apps/frontend/src/utils/aave/AaveRepayTransactionsFactory.ts @@ -1,4 +1,4 @@ -import { BigNumber, constants, ethers } from 'ethers'; +import { BigNumber, constants, Contract, ethers } from 'ethers'; import { t } from 'i18next'; import { AssetDetailsData, getAssetDataByAddress } from '@sovryn/contracts'; @@ -10,8 +10,8 @@ import { TransactionType, } from '../../app/3_organisms/TransactionStepDialog/TransactionStepDialog.types'; import { translations } from '../../locales/i18n'; -import { TransactionFactoryOptions } from '../../types/aave'; -import { LoanType } from './AaveUserReservesSummary'; +import { BorrowRateMode, TransactionFactoryOptions } from '../../types/aave'; +import { prepareApproveTransaction } from '../transactions'; export class AaveRepayTransactionsFactory { private readonly Pool: ethers.Contract; @@ -42,48 +42,57 @@ export class AaveRepayTransactionsFactory { async repay( token: AssetDetailsData, amount: BigNumber, - loanType: LoanType, + borrowRateMode: BorrowRateMode, opts?: TransactionFactoryOptions, ): Promise { - if (token.isNative) return this.repayNative(amount, loanType, opts); - else return this.repayToken(token, amount, loanType, opts); + if (token.isNative) return this.repayNative(amount, borrowRateMode, opts); + else return this.repayToken(token, amount, borrowRateMode, opts); } private async repayToken( asset: AssetDetailsData, amount: BigNumber, - loanType: LoanType, + borrowRateMode: BorrowRateMode, opts?: TransactionFactoryOptions, ): Promise { - return [ - { - title: t(translations.aavePage.tx.repayTitle, { - symbol: asset.symbol, - }), - subtitle: t(translations.aavePage.tx.repaySubtitle, { - symbol: asset.symbol, - amount: ethers.utils.formatUnits(amount, asset.decimals), - }), - request: { - type: TransactionType.signTransaction, - args: [ - asset.address, - amount.toString(), - loanType, - await this.signer.getAddress(), - ], - contract: this.Pool, - fnName: 'repay', - value: 0, - }, - onComplete: opts?.onComplete, + const approval = await prepareApproveTransaction({ + spender: this.PoolAddress, + token: asset.symbol, + contract: new Contract(asset.address, asset.abi, this.signer), + amount: amount, + chain: BOB_CHAIN_ID, + }); + const transactions: Transaction[] = approval ? [approval] : []; + + transactions.push({ + title: t(translations.aavePage.tx.repayTitle, { + symbol: asset.symbol, + }), + subtitle: t(translations.aavePage.tx.repaySubtitle, { + symbol: asset.symbol, + amount: ethers.utils.formatUnits(amount, asset.decimals), + }), + request: { + type: TransactionType.signTransaction, + args: [ + asset.address, + amount.toString(), + borrowRateMode, + await this.signer.getAddress(), + ], + contract: this.Pool, + fnName: 'repay', + value: 0, }, - ]; + onComplete: opts?.onComplete, + }); + + return transactions; } private async repayNative( amount: BigNumber, - loanType: LoanType, + borrowRateMode: BorrowRateMode, opts?: TransactionFactoryOptions, ): Promise { const nativeAsset = await getAssetDataByAddress( @@ -105,7 +114,7 @@ export class AaveRepayTransactionsFactory { args: [ this.PoolAddress, amount.toString(), - loanType, + borrowRateMode, await this.signer.getAddress(), ], contract: this.WETHGateway, diff --git a/apps/frontend/src/utils/aave/AaveSupplyTransactionsFactory.ts b/apps/frontend/src/utils/aave/AaveSupplyTransactionsFactory.ts index fe238f10d..a3e0ea17b 100644 --- a/apps/frontend/src/utils/aave/AaveSupplyTransactionsFactory.ts +++ b/apps/frontend/src/utils/aave/AaveSupplyTransactionsFactory.ts @@ -72,7 +72,7 @@ export class AaveSupplyTransactionsFactory { }), request: { type: TransactionType.signTransaction, - args: [token, useAsCollateral], + args: [token.address, useAsCollateral], contract: this.Pool, fnName: 'setUserUseReserveAsCollateral', }, diff --git a/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts b/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts index 0e31f6509..158d3525b 100644 --- a/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts +++ b/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts @@ -4,237 +4,199 @@ import { FormatUserSummaryResponse, } from '@aave/math-utils'; +import { BigNumber, ethers } from 'ethers'; + +import { AssetDetailsData, getAssetData } from '@sovryn/contracts'; import { Decimal } from '@sovryn/utils'; -type UserSummary = FormatUserSummaryResponse< +import { BOB_CHAIN_ID } from '../../config/chains'; + +import { Reserve } from '../../hooks/aave/useAaveReservesData'; +import { BorrowRateMode } from '../../types/aave'; +import { decimalic, fromWei } from '../math'; +import { AaveCalculations } from './AaveCalculations'; + +export type UserSummary = FormatUserSummaryResponse< ReserveDataHumanized & FormatReserveUSDResponse >; -export enum LoanType { - STABLE = 1, - VARIABLE = 2, -} - -export type SuppliedAsset = { +export type ReserveSummary = { + reserve: Reserve; asset: string; - assetAddress: string; - apy: Decimal; - isCollateral: boolean; + + walletBalance: Decimal; + walletBalanceUsd: Decimal; + collateral: boolean; supplied: Decimal; suppliedUSD: Decimal; -}; - -export type BorrowedAsset = { - asset: string; - assetAddress: string; - apy: Decimal; - type: LoanType; borrowed: Decimal; borrowedUSD: Decimal; + borrowRateMode: BorrowRateMode; + availableToBorrow: Decimal; }; -export class AaveUserReservesSummary { - public netWorth: Decimal; - public netApy: Decimal; - public healthFactor: Decimal; - public supplyBalance: Decimal; - public supplyWeightedApy: Decimal; - public collateralBalance: Decimal; - public currentLiquidationThreshold: Decimal; - public borrowBalance: Decimal; - public borrowWeightedApy: Decimal; - public borrowPower: Decimal; - public borrowPowerUsed: Decimal; - public suppliedAssets: SuppliedAsset[]; - public borrowedAssets: BorrowedAsset[]; - public eModeEnabled: boolean; - - constructor(private readonly userSummary: UserSummary) { - // balances - this.netWorth = Decimal.from(this.userSummary.netWorthUSD); - this.borrowBalance = Decimal.from(this.userSummary.totalBorrowsUSD); - this.supplyBalance = this.computeSuppliedBalance( - this.userSummary.userReservesData, +export type AaveUserReservesSummary = { + netApy: Decimal; + netWorth: Decimal; + healthFactor: Decimal; + collateralRatio: Decimal; + + supplyBalance: Decimal; + supplyWeightedApy: Decimal; + collateralBalance: Decimal; + currentLiquidationThreshold: Decimal; + + borrowBalance: Decimal; + borrowWeightedApy: Decimal; + borrowPower: Decimal; + borrowPowerUsed: Decimal; + eModeEnabled: boolean; + eModeCategoryId: number; + + reserves: ReserveSummary[]; +}; + +export class AaveUserReservesSummaryFactory { + static buildZeroSummary(reserves: Reserve[]): AaveUserReservesSummary { + return { + netApy: Decimal.ZERO, + netWorth: Decimal.ZERO, + healthFactor: Decimal.ZERO, + collateralRatio: Decimal.ZERO, + + supplyBalance: Decimal.ZERO, + supplyWeightedApy: Decimal.ZERO, + collateralBalance: Decimal.ZERO, + currentLiquidationThreshold: Decimal.ZERO, + + borrowBalance: Decimal.ZERO, + borrowWeightedApy: Decimal.ZERO, + borrowPower: Decimal.ZERO, + borrowPowerUsed: Decimal.ZERO, + eModeEnabled: false, + eModeCategoryId: 0, + + reserves: reserves.map(r => ({ + reserve: r, + asset: r.symbol, + + walletBalance: Decimal.ZERO, + walletBalanceUsd: Decimal.ZERO, + collateral: false, + supplied: Decimal.ZERO, + suppliedUSD: Decimal.ZERO, + availableToSupply: Decimal.ZERO, + borrowed: Decimal.ZERO, + borrowedUSD: Decimal.ZERO, + borrowRateMode: BorrowRateMode.VARIABLE, + availableToBorrow: Decimal.ZERO, + })), + }; + } + + static async buildSummary( + provider: ethers.providers.Provider, + account: string, + userSummary: UserSummary, + ): Promise { + const netWorth = Decimal.from(userSummary.netWorthUSD); + const borrowBalance = Decimal.from(userSummary.totalBorrowsUSD); + const supplyBalance = AaveCalculations.computeSuppliedBalance( + userSummary.userReservesData, ); - this.collateralBalance = this.computeCollateral( - this.userSummary.userReservesData, + const collateralBalance = AaveCalculations.computeCollateral( + userSummary.userReservesData, ); - - // apy - this.supplyWeightedApy = this.computeWeightedSupplyApy( - this.userSummary.userReservesData, + const supplyWeightedApy = AaveCalculations.computeWeightedSupplyApy( + userSummary.userReservesData, ); - this.borrowWeightedApy = this.computeWeightedBorrowApy( - this.userSummary.userReservesData, + const borrowWeightedApy = AaveCalculations.computeWeightedBorrowApy( + userSummary.userReservesData, ); - this.netApy = this.computeNetApy( - this.supplyWeightedApy, - this.supplyBalance, - this.borrowWeightedApy, - this.borrowBalance, - this.netWorth, + const netApy = AaveCalculations.computeNetApy( + supplyWeightedApy, + supplyBalance, + borrowWeightedApy, + borrowBalance, + netWorth, ); // health and borrow status - this.currentLiquidationThreshold = Decimal.from( - this.userSummary.currentLiquidationThreshold, - ); - this.borrowPower = this.computeBorrowPower( - Decimal.from(this.userSummary.availableBorrowsUSD), - this.borrowBalance, + const currentLiquidationThreshold = Decimal.from( + userSummary.currentLiquidationThreshold, ); - this.borrowPowerUsed = this.computeBorrowPowerUsed( - this.borrowBalance, - this.borrowPower, - ); - this.healthFactor = this.computeHealthFactor( - this.collateralBalance, - this.currentLiquidationThreshold, - this.borrowBalance, - ); - - // supplied and borrowed assets - this.suppliedAssets = this.computeSuppliedAssets( - this.userSummary.userReservesData, + const borrowPower = AaveCalculations.computeBorrowPower( + Decimal.from(userSummary.availableBorrowsUSD), + borrowBalance, ); - this.borrowedAssets = this.computeBorrowedAssets( - this.userSummary.userReservesData, + const borrowPowerUsed = AaveCalculations.computeBorrowPowerUsed( + borrowBalance, + borrowPower, ); - - // emode - this.eModeEnabled = this.userSummary.userEmodeCategoryId !== 0; - } - - static from( - userSummary: FormatUserSummaryResponse< - ReserveDataHumanized & FormatReserveUSDResponse - >, - ) { - return new AaveUserReservesSummary(userSummary); - } - - private computeNetApy( - weightedSupplyApy: Decimal, - suppliedBalance: Decimal, - weightedBorrowApy: Decimal, - borrowedBalance: Decimal, - netWorthUsd: Decimal, - ): Decimal { - return weightedSupplyApy - .mul(suppliedBalance) - .div(netWorthUsd) - .sub(weightedBorrowApy.mul(borrowedBalance).div(netWorthUsd)); - } - - private computeSuppliedBalance( - reserves: UserSummary['userReservesData'], - ): Decimal { - return reserves.reduce( - (suppliedBalance, r) => suppliedBalance.add(r.underlyingBalanceUSD), - Decimal.from(0), + const healthFactor = AaveCalculations.computeHealthFactor( + collateralBalance, + currentLiquidationThreshold, + borrowBalance, ); - } - - private computeBorrowPower( - availableBorrowsUSD: Decimal, - borrowedBalance: Decimal, - ) { - return Decimal.from(availableBorrowsUSD).add(borrowedBalance); - } - - private computeBorrowPowerUsed( - borrowedBalance: Decimal, - borrowPower: Decimal, - ) { - return Decimal.from(borrowedBalance).div(borrowPower).mul(100); - } - - private computeCollateral( - reserves: UserSummary['userReservesData'], - ): Decimal { - return reserves.reduce( - (collateral, r) => - collateral.add( - r.usageAsCollateralEnabledOnUser ? r.underlyingBalanceUSD : 0, - ), - Decimal.from(0), + const collateralRatio = AaveCalculations.computeCollateralRatio( + collateralBalance, + borrowBalance, ); - } - private computeHealthFactor( - collateral: Decimal, - currentLiquidationThreshold: Decimal, - borrowedBalance: Decimal, - ): Decimal { - return collateral.mul(currentLiquidationThreshold).div(borrowedBalance); - } - - private computeWeightedBorrowApy( - reserves: UserSummary['userReservesData'], - ): Decimal { - let totalBorrowedUSD = Decimal.from(0); - let weightedBorrowAPYSum = Decimal.from(0); - - reserves.forEach(reserve => { - const borrowedAmountUSD = Decimal.from(reserve.totalBorrowsUSD); - const borrowAPY = Decimal.from(reserve.reserve.variableBorrowAPY); - - weightedBorrowAPYSum = weightedBorrowAPYSum.add( - borrowAPY.mul(borrowedAmountUSD), - ); - totalBorrowedUSD = totalBorrowedUSD.add(borrowedAmountUSD); - }); - - return weightedBorrowAPYSum.div(totalBorrowedUSD).mul(100); - } - - private computeWeightedSupplyApy( - reserves: UserSummary['userReservesData'], - ): Decimal { - let totalBorrowedUSD = Decimal.from(0); - let weightedBorrowAPYSum = Decimal.from(0); - - reserves.forEach(reserve => { - const borrowedAmountUSD = Decimal.from(reserve.totalBorrowsUSD); - const borrowAPY = Decimal.from(reserve.reserve.supplyAPY); - - weightedBorrowAPYSum = weightedBorrowAPYSum.add( - borrowAPY.mul(borrowedAmountUSD), - ); - totalBorrowedUSD = totalBorrowedUSD.add(borrowedAmountUSD); - }); - - return weightedBorrowAPYSum.div(totalBorrowedUSD).mul(100); - } - - private computeSuppliedAssets( - reserves: UserSummary['userReservesData'], - ): SuppliedAsset[] { - return reserves - .filter(r => Decimal.from(r.underlyingBalanceUSD).gt(0)) - .map(r => ({ - asset: r.reserve.symbol, - assetAddress: r.underlyingAsset, - apy: Decimal.from(r.reserve.supplyAPY).mul(100), - isCollateral: r.usageAsCollateralEnabledOnUser, - supplied: Decimal.from(r.underlyingBalance), - suppliedUSD: Decimal.from(r.underlyingBalanceUSD), - })); + return { + netApy, + netWorth, + healthFactor, + collateralRatio, + + supplyBalance, + supplyWeightedApy, + collateralBalance, + currentLiquidationThreshold, + + borrowBalance, + borrowWeightedApy, + borrowPower, + borrowPowerUsed, + eModeEnabled: userSummary.userEmodeCategoryId !== 0, + eModeCategoryId: userSummary.userEmodeCategoryId, + + reserves: await Promise.all( + userSummary.userReservesData.map(async r => { + const asset = await getAssetData(r.reserve.symbol, BOB_CHAIN_ID); + const balance = await getBalance(asset, account, provider); + const decimalBalance = decimalic(fromWei(balance, asset.decimals)); + + const availableToBorrow = borrowPower.div(r.reserve.priceInUSD); + + return { + asset: r.reserve.symbol, + reserve: r.reserve, + + walletBalance: decimalBalance, + walletBalanceUsd: decimalBalance.mul(r.reserve.priceInUSD), + collateral: r.usageAsCollateralEnabledOnUser, + supplied: Decimal.from(r.underlyingBalance), + suppliedUSD: Decimal.from(r.underlyingBalanceUSD), + borrowed: Decimal.from(r.totalBorrows), + borrowedUSD: Decimal.from(r.totalBorrowsUSD), + borrowRateMode: Decimal.from(r.variableBorrows).gt(0) + ? BorrowRateMode.VARIABLE + : BorrowRateMode.STABLE, + availableToBorrow, + }; + }), + ), + }; } +} - private computeBorrowedAssets( - reserves: UserSummary['userReservesData'], - ): BorrowedAsset[] { - return reserves - .filter(r => Decimal.from(r.totalBorrowsUSD).gt(0)) - .map(r => ({ - asset: r.reserve.symbol, - assetAddress: r.underlyingAsset, - borrowed: Decimal.from(r.totalBorrows), - borrowedUSD: Decimal.from(r.totalBorrowsUSD), - apy: Decimal.from(r.reserve.variableBorrowAPY).mul(100), - type: - Number(r.variableBorrows) > 0 ? LoanType.VARIABLE : LoanType.STABLE, - })); - } +function getBalance( + asset: AssetDetailsData, + account: string, + provider: ethers.providers.Provider, +): Promise { + return asset.isNative + ? provider.getBalance(account) + : asset.contract(provider).balanceOf(account); } diff --git a/packages/contracts/src/contracts/assets/bobTestnet.ts b/packages/contracts/src/contracts/assets/bobTestnet.ts index 2fed174eb..8050427bb 100644 --- a/packages/contracts/src/contracts/assets/bobTestnet.ts +++ b/packages/contracts/src/contracts/assets/bobTestnet.ts @@ -42,7 +42,7 @@ export const bobTestnet: Array = [ }, { symbol: 'DAI', - address: '0x2528AC8426DdCbfd2E038D90532F52a9Ad5BD594', + address: '0x4e4e256D3a9789329AB540a7a3b2cd0c03C40431', name: 'DAI Stablecoin', decimals: 18, getIcon: async () => (await import('./icons/bob/dai')).default, @@ -58,7 +58,7 @@ export const bobTestnet: Array = [ symbol: 'USDC', address: '0x509AeFe02953BC2fB8abCa53Fd83C94D86c05922', name: 'USD Coin', - decimals: 6, + decimals: 18, getIcon: async () => (await import('./icons/bob/usdc')).default, }, { @@ -85,9 +85,17 @@ export const bobTestnet: Array = [ { symbol: 'aWETH', // TODO: properly adjust this variable - address: '0xAD51B64c2eeF5877CDF4AcdCC790DF6eaBC6ecbf', + address: '0x1b57354f10EFc441803639F74E9624CcA6Ab7abA', name: 'Aave Wrapped ETH', decimals: 18, getIcon: async () => (await import('./icons/bob/eth')).default, }, + { + symbol: 'WETH', + // TODO: properly adjust this variable + address: '0x936EA1bCF82Fbc1Dbe24c6AA140f136A7De15C2E', + name: 'Wrapped ETH', + decimals: 18, + getIcon: async () => (await import('./icons/bob/eth')).default, + }, ]; From 9d02403a77f3efea38af7638e31f139952622b8b Mon Sep 17 00:00:00 2001 From: Matias Zapata <42716817+matzapata@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:39:03 +0300 Subject: [PATCH 072/116] SAF 50 - E-Mode (#30) * cleanup * fixes * fixes. Connect provider. Fix repay adding proper allowance. Fix switch collateral token.address instead of token * refetch user data based on blocknumber * fix modal closing on lending positions change plus style adjustments * fix withdraw modals * modal fixes * fix calculation add balances and filter zero balances * use memo items * fix dependencies * data normalizers * Apply suggestions from code review Co-authored-by: Juan * Update apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx Co-authored-by: Juan * Update apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx Co-authored-by: Juan * wip refactor done. Missing hooks merge * implementations and cleanup * cleanup * cleanup * math safety * more math safety * cleanup * clenaup * emode mocks * enable emode working * emode working * fixes and cleanup * remove whitelist --------- Co-authored-by: Juan --- .../src/app/5_pages/AavePage/AavePage.tsx | 43 ++-- .../app/5_pages/AavePage/AavePage.utils.tsx | 5 + .../BorrowAssetsList/BorrowAssetsList.tsx | 14 +- .../components/BorrowModal/BorrowForm.tsx | 32 ++- .../BorrowPositionsList.tsx | 10 +- .../EfficiencyModeCard/EfficiencyModeCard.tsx | 175 ++++++++++++---- .../DisableEModeForm/DisableEModeForm.tsx | 165 +++++++++++++++ .../EnableEModeForm/EnableEModeForm.tsx | 108 ++++++++++ .../SwitchEModeForm/SwitchEModeForm.tsx | 191 ++++++++++++++++++ .../RepayForm/RepayWithWalletBalanceForm.tsx | 20 +- .../components/WithdrawForm/WithdrawForm.tsx | 12 +- apps/frontend/src/constants/aave.ts | 11 +- .../src/hooks/aave/useAaveEModeCategories.tsx | 29 +++ .../src/hooks/aave/useAaveReservesData.tsx | 5 +- .../src/hooks/aave/useAaveSetUserEMode.tsx | 54 +++++ .../hooks/aave/useAaveUserReservesData.tsx | 56 ++--- .../frontend/src/locales/en/translations.json | 22 +- apps/frontend/src/types/aave.ts | 18 ++ .../src/utils/aave/AaveEModeCategories.ts | 105 ++++++++++ .../aave/AaveEModeTransactionsFactory.ts | 63 ++++++ .../src/utils/aave/AaveUserReservesSummary.ts | 48 ++++- 21 files changed, 1040 insertions(+), 146 deletions(-) create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/EnableEModeForm/EnableEModeForm.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx create mode 100644 apps/frontend/src/hooks/aave/useAaveEModeCategories.tsx create mode 100644 apps/frontend/src/hooks/aave/useAaveSetUserEMode.tsx create mode 100644 apps/frontend/src/utils/aave/AaveEModeCategories.ts create mode 100644 apps/frontend/src/utils/aave/AaveEModeTransactionsFactory.ts diff --git a/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx b/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx index cb7c85f07..6c2a85522 100644 --- a/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx @@ -34,24 +34,24 @@ enum ActiveTab { const AavePage: FC = () => { const reserves = useAaveReservesData(); - const userReservesSummary = useAaveUserReservesData(); + const { summary } = useAaveUserReservesData(); const [activeTab, setActiveTab] = useState(ActiveTab.LEND); const lendPositions: LendPosition[] = useMemo( - () => normalizeLendPositions(userReservesSummary), - [userReservesSummary], + () => normalizeLendPositions(summary), + [summary], ); const borrowPositions: BorrowPosition[] = useMemo( - () => normalizeBorrowPositions(userReservesSummary), - [userReservesSummary], + () => normalizeBorrowPositions(summary), + [summary], ); const borrowPools: BorrowPoolDetails[] = useMemo( - () => normalizeBorrowPoolDetails(reserves, userReservesSummary), - [reserves, userReservesSummary], + () => normalizeBorrowPoolDetails(reserves, summary), + [reserves, summary], ); const lendPools: LendPoolDetails[] = useMemo( - () => normalizeLendPoolDetails(reserves, userReservesSummary), - [reserves, userReservesSummary], + () => normalizeLendPoolDetails(reserves, summary), + [reserves, summary], ); const tabsItems = useMemo( @@ -77,9 +77,9 @@ const AavePage: FC = () => {
@@ -103,9 +103,9 @@ const AavePage: FC = () => { >
@@ -119,12 +119,15 @@ const AavePage: FC = () => { > + -
diff --git a/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx b/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx index f7c0fbc06..8d6899a99 100644 --- a/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx @@ -52,6 +52,11 @@ export function normalizeBorrowPoolDetails( } else { return reserves .filter(r => r.borrowingEnabled) + .filter( + r => + userReservesSummary.eModeCategoryId === r.eModeCategoryId || + userReservesSummary.eModeCategoryId === 0, + ) .map(r => ({ asset: r.symbol, apy: Decimal.from(r.variableBorrowAPY).mul(100), diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx index da47c7ba4..c9ebfc3bf 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/BorrowAssetsList.tsx @@ -7,6 +7,8 @@ import { Dialog, DialogBody, DialogHeader, + ErrorBadge, + ErrorLevel, OrderOptions, Table, } from '@sovryn/ui'; @@ -22,10 +24,12 @@ const pageTranslations = translations.aavePage.borrowAssetsList; type BorrowAssetsListProps = { borrowPools: BorrowPoolDetails[]; + eModeEnabled: boolean; }; export const BorrowAssetsList: FC = ({ borrowPools, + eModeEnabled, }) => { const [open, setOpen] = useState(true); const [orderOptions, setOrderOptions] = useState(); @@ -76,8 +80,16 @@ export const BorrowAssetsList: FC = ({ open={open} onClick={setOpen} > + {eModeEnabled && ( + + )} +
= ({ asset, onSuccess }) => { - const userReservesSummary = useAaveUserReservesData(); + const { summary } = useAaveUserReservesData(); const [borrowAsset, setBorrowAsset] = useState(asset); const [borrowAmount, setBorrowAmount, borrowSize] = useDecimalAmountInput(''); const [acknowledge, setAcknowledge] = useState(false); @@ -44,7 +44,7 @@ export const BorrowForm: FC = ({ asset, onSuccess }) => { const borrowableAssetsOptions = useMemo( () => - userReservesSummary.reserves + summary.reserves .filter(r => r.reserve.borrowingEnabled) .map(r => ({ value: r.reserve.symbol, @@ -57,14 +57,12 @@ export const BorrowForm: FC = ({ asset, onSuccess }) => { /> ), })), - [userReservesSummary.reserves], + [summary.reserves], ); const borrowReserve = useMemo(() => { - return userReservesSummary.reserves.find( - r => r.reserve.symbol === borrowAsset, - ); - }, [userReservesSummary.reserves, borrowAsset]); + return summary.reserves.find(r => r.reserve.symbol === borrowAsset); + }, [summary.reserves, borrowAsset]); const borrowUsdAmount = useMemo(() => { return borrowSize.mul(borrowReserve?.reserve.priceInUSD ?? 0); @@ -76,25 +74,21 @@ export const BorrowForm: FC = ({ asset, onSuccess }) => { const newCollateralRatio = useMemo(() => { return AaveCalculations.computeCollateralRatio( - userReservesSummary.collateralBalance, - userReservesSummary.borrowBalance.add(borrowUsdAmount), + summary.collateralBalance, + summary.borrowBalance.add(borrowUsdAmount), ); - }, [ - userReservesSummary.collateralBalance, - userReservesSummary.borrowBalance, - borrowUsdAmount, - ]); + }, [summary.collateralBalance, summary.borrowBalance, borrowUsdAmount]); const liquidationPrice = useMemo(() => { return AaveCalculations.computeLiquidationPrice( borrowSize, - userReservesSummary.currentLiquidationThreshold, - userReservesSummary.collateralBalance, + summary.currentLiquidationThreshold, + summary.collateralBalance, ); }, [ borrowSize, - userReservesSummary.currentLiquidationThreshold, - userReservesSummary.collateralBalance, + summary.currentLiquidationThreshold, + summary.collateralBalance, ]); const isValidBorrowAmount = useMemo( @@ -173,7 +167,7 @@ export const BorrowForm: FC = ({ asset, onSuccess }) => { {t(translations.aavePage.borrowForm.acknowledge)}{' '} {/* TODO: Add proper learn more href */} diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index 3ce35d09c..cc2c02ab5 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -30,7 +30,7 @@ type BorrowPositionsListProps = { borrowBalance: Decimal; borrowWeightedApy: Decimal; borrowPowerUsed: Decimal; - eModeEnabled: boolean; + eModeCategoryId: Number; }; export const BorrowPositionsList: FC = ({ @@ -38,10 +38,10 @@ export const BorrowPositionsList: FC = ({ borrowBalance, borrowPowerUsed, borrowWeightedApy, - eModeEnabled, + eModeCategoryId, }) => { const { account } = useAccount(); - const [open, setOpen] = useState(true); + const [open, setOpen] = useState(true); const [orderOptions, setOrderOptions] = useState(); const [repayAssetDialog, setRepayAssetDialog] = useState< string | undefined @@ -86,7 +86,7 @@ export const BorrowPositionsList: FC = ({ {t(pageTranslations.borrowPositionsList.eMode)} - + } @@ -98,7 +98,7 @@ export const BorrowPositionsList: FC = ({ {account ? ( <>
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx index 069106293..e771c05a4 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx @@ -1,10 +1,14 @@ -import React, { FC } from 'react'; +import React, { FC, useCallback, useMemo, useState } from 'react'; import classNames from 'classnames'; import { t } from 'i18next'; import { Button, + ButtonStyle, + Dialog, + DialogBody, + DialogHeader, Icon, IconNames, Link, @@ -14,53 +18,150 @@ import { } from '@sovryn/ui'; import { EModeIcon } from '../../../../../../1_atoms/Icons/Icons'; +import { useAaveEModeCategories } from '../../../../../../../hooks/aave/useAaveEModeCategories'; import { translations } from '../../../../../../../locales/i18n'; +import { EModeCategory } from '../../../../../../../types/aave'; +import { DisableEModeForm } from './components/DisableEModeForm/DisableEModeForm'; +import { EnableEModeForm } from './components/EnableEModeForm/EnableEModeForm'; +import { SwitchEModeForm } from './components/SwitchEModeForm/SwitchEModeForm'; type EfficiencyModeCardProps = { className?: string; - enabled: boolean; + eModeCategoryId: Number; }; export const EfficiencyModeCard: FC = ({ className, - enabled, + eModeCategoryId, }) => { + const eModeCategories = useAaveEModeCategories(); + const [enableEModeOpen, setEnableEModeOpen] = useState(false); + const [disableEModeOpen, setDisableEModeOpen] = useState(false); + const [switchEModeOpen, setSwitchEModeOpen] = useState(false); + + const onEnableEModeClose = useCallback(() => { + setEnableEModeOpen(false); + }, []); + + const onDisableEModeClose = useCallback(() => { + setDisableEModeOpen(false); + }, []); + + const onSwitchEModeClose = useCallback(() => { + setSwitchEModeOpen(false); + }, []); + + const currentCategory = useMemo(() => { + return eModeCategories.find(c => c.id === eModeCategoryId); + }, [eModeCategories, eModeCategoryId]); + return ( - -
- - {t(translations.aavePage.eModeCard.title)} - - - {t(translations.aavePage.eModeCard.description)}{' '} - - + <> + +
+ + {t(translations.aavePage.eModeCard.title)} + + + {eModeCategoryId !== 0 && ( +
+ + {t(translations.aavePage.eMode.assetCategory)} + + +
+ + {currentCategory?.label} + +
+ + Enabled +
+
+
+ )} + + + {t(translations.aavePage.eModeCard.description)}{' '} + + +
+ {eModeCategoryId !== 0 ? ( +
+
+ ) : ( +
-
+ } + /> + + + + + + } + /> + + +
= ({ onClose={onBorrowClose} /> - + diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx index f48d44cfe..d9b5dccef 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx @@ -1,8 +1,7 @@ -import React, { FC, useMemo, useState } from 'react'; +import React, { FC, useCallback, useMemo, useState } from 'react'; import { t } from 'i18next'; -import { getAssetData } from '@sovryn/contracts'; import { Button, Checkbox, @@ -32,10 +31,10 @@ const pageTranslations = translations.aavePage; type BorrowFormProps = { asset: string; - onSuccess: () => void; + onComplete: () => void; }; -export const BorrowForm: FC = ({ asset, onSuccess }) => { +export const BorrowForm: FC = ({ asset, onComplete }) => { const { summary } = useAaveUserReservesData(); const [borrowAsset, setBorrowAsset] = useState(asset); const [borrowAmount, setBorrowAmount, borrowSize] = useDecimalAmountInput(''); @@ -99,6 +98,15 @@ export const BorrowForm: FC = ({ asset, onSuccess }) => { [borrowSize, maximumBorrowAmount], ); + const onConfirm = useCallback(() => { + handleBorrow( + borrowSize, + borrowReserve!.reserve.symbol, + BorrowRateMode.VARIABLE, + { onComplete }, + ); + }, [onComplete, borrowSize, borrowReserve, handleBorrow]); + return (
@@ -179,14 +187,7 @@ export const BorrowForm: FC = ({ asset, onSuccess }) => { />
= ({ onClose={onRepayClose} /> - + diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx index e771c05a4..9bdb44395 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx @@ -98,6 +98,7 @@ export const EfficiencyModeCard: FC = ({ {eModeCategoryId !== 0 ? (
= ({ lendPools }) => { onClose={onLendClose} /> - + diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendForm/LendForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendForm/LendForm.tsx index 0d492b254..12f96e27e 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendForm/LendForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendForm/LendForm.tsx @@ -1,8 +1,7 @@ -import React, { FC, useMemo, useState } from 'react'; +import React, { FC, useCallback, useMemo, useState } from 'react'; import { t } from 'i18next'; -import { getAssetData } from '@sovryn/contracts'; import { Button, ErrorBadge, @@ -28,15 +27,15 @@ const pageTranslations = translations.aavePage; type LendFormProps = { asset: string; - onSuccess: () => void; + onComplete: () => void; }; export const LendForm: FC = ({ asset: initialAsset, - onSuccess, + onComplete, }) => { const { account } = useAccount(); - const reserves = useAaveReservesData(); + const { reserves } = useAaveReservesData(); const [lendAsset, setLendAsset] = useState(initialAsset); const [lendAmount, setLendAmount, lendSize] = useDecimalAmountInput(''); const { balance: lendAssetBalance } = useAssetBalance( @@ -66,7 +65,7 @@ export const LendForm: FC = ({ return reserves.find(r => r.symbol === lendAsset) ?? reserves[0]; }, [reserves, lendAsset]); - const assetUsdValue: Decimal = useMemo(() => { + const assetUsdValue = useMemo(() => { return Decimal.from(reserve?.priceInUSD ?? 0).mul(lendSize); }, [reserve, lendSize]); @@ -75,6 +74,10 @@ export const LendForm: FC = ({ [lendSize, lendAssetBalance], ); + const onConfirm = useCallback(async () => { + handleDeposit(lendSize, reserve.symbol, { onComplete }); + }, [handleDeposit, lendSize, reserve, onComplete]); + return (
@@ -114,22 +117,16 @@ export const LendForm: FC = ({
= ({ diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/ToggleCollateralAction/ToggleCollateralAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/ToggleCollateralAction/ToggleCollateralAction.tsx index c1b6ba37c..46e4d21cc 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/ToggleCollateralAction/ToggleCollateralAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/ToggleCollateralAction/ToggleCollateralAction.tsx @@ -1,10 +1,7 @@ import React, { FC, useState } from 'react'; -import { getAssetData } from '@sovryn/contracts'; import { Toggle } from '@sovryn/ui'; -import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; - import { useAaveSupply } from '../../../../../../../hooks/aave/useAaveSupply'; import { LendPosition } from '../../LendPositionsList.types'; @@ -19,11 +16,9 @@ export const ToggleCollateralAction: FC = ({ const [isCollateral, setIsCollateral] = useState(position.collateral); const toggleCollateral = async () => { - await handleSwitchCollateral( - await getAssetData(position.asset, BOB_CHAIN_ID), - !isCollateral, - { onComplete: () => setIsCollateral(!isCollateral) }, - ); + await handleSwitchCollateral(position.asset, !isCollateral, { + onComplete: () => setIsCollateral(!isCollateral), + }); }; return ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx index 10dfc9808..630ce87c8 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx @@ -1,8 +1,7 @@ -import React, { FC, useMemo, useState } from 'react'; +import React, { FC, useCallback, useMemo, useState } from 'react'; import { t } from 'i18next'; -import { getAssetData } from '@sovryn/contracts'; import { Button, ErrorBadge, @@ -28,10 +27,10 @@ const pageTranslations = translations.aavePage; type WithdrawFormProps = { asset: string; - onSuccess: () => void; + onComplete: () => void; }; -export const WithdrawForm: FC = ({ asset }) => { +export const WithdrawForm: FC = ({ asset, onComplete }) => { const { handleWithdraw } = useAaveWithdraw(); const { summary } = useAaveUserReservesData(); const [withdrawAsset, setWithdrawAsset] = useState(asset); @@ -97,6 +96,21 @@ export const WithdrawForm: FC = ({ asset }) => { [], ); + const onConfirm = useCallback(() => { + handleWithdraw( + withdrawSize, + withdrawAsset, + withdrawSize.eq(maximumWithdrawAmount), + { onComplete }, + ); + }, [ + handleWithdraw, + withdrawSize, + withdrawAsset, + onComplete, + maximumWithdrawAmount, + ]); + return (
@@ -138,12 +152,7 @@ export const WithdrawForm: FC = ({ asset }) => {
- + ); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index 422f4d77c..8903daa54 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -112,12 +112,16 @@ export const BorrowPositionsList: FC = ({ /> = ({
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx index 823282578..a00b9a6cc 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx @@ -88,7 +88,10 @@ export const COLUMNS_CONFIG = (onLendClick: (asset: string) => unknown) => [ align: Align.center, title: ' ', cellRenderer: (pool: LendPoolDetails) => ( - onLendClick(pool.asset)} /> + onLendClick(pool.asset)} + /> ), }, ]; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx index fd9669155..dabc964ce 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx @@ -8,22 +8,27 @@ import { Button, ButtonStyle } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; type LendAssetActionProps = { + disabled: boolean; onLendClick: () => void; }; -export const LendAssetAction: FC = ({ onLendClick }) => { +export const LendAssetAction: FC = ({ + disabled, + onLendClick, +}) => { const navigate = useNavigate(); return (
- + ); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx index 0af177b59..98229eb9f 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.tsx @@ -105,6 +105,7 @@ export const LendPositionsList: FC = ({ /> = ({
); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx index 0f2ffe18c..c087ba1c6 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx @@ -54,6 +54,7 @@ export const BorrowAssetDetails: FC = ({ ); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx index a00b9a6cc..93d23297f 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/LendAssetsList.constants.tsx @@ -91,6 +91,7 @@ export const COLUMNS_CONFIG = (onLendClick: (asset: string) => unknown) => [ onLendClick(pool.asset)} + asset={pool.asset} /> ), }, diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx index dabc964ce..40e189e71 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx @@ -10,9 +10,11 @@ import { translations } from '../../../../../../../locales/i18n'; type LendAssetActionProps = { disabled: boolean; onLendClick: () => void; + asset: string; }; export const LendAssetAction: FC = ({ + asset, disabled, onLendClick, }) => { @@ -31,7 +33,7 @@ export const LendAssetAction: FC = ({ className="flex-grow lg:flex-grow-0 lg:w-min" text={t(translations.aavePage.common.details)} style={ButtonStyle.secondary} - onClick={() => navigate('/aave/reserve-overview')} + onClick={() => navigate(`/aave/reserve-overview?asset=${asset}`)} /> ); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx index 6775fa165..0bc9c1b20 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx @@ -57,6 +57,7 @@ export const LendAssetDetails: FC = ({ ); diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx index 1c9418098..55f11a7b2 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx @@ -1,17 +1,21 @@ -import React, { FC, useState } from 'react'; +import React, { FC, useMemo, useState } from 'react'; import classNames from 'classnames'; import { t } from 'i18next'; import { Helmet } from 'react-helmet-async'; +import { useSearchParams } from 'react-router-dom'; import { Paragraph, Tabs, TabSize, TabType } from '@sovryn/ui'; +import { Decimal } from '@sovryn/utils'; +import { useAaveInterestRatesData } from '../../../hooks/aave/useAaveRates'; +import { useAaveReservesData } from '../../../hooks/aave/useAaveReservesData'; import { translations } from '../../../locales/i18n'; import { BorrowDetailsGraph } from './components/BorrowDetailsGraph/BorrowDetailsGraph'; import { EModeDetails } from './components/EModeDetails/EModeDetails'; import { InterestRateModelGraph } from './components/InterestRateModelGraph/InterestRateModelGraph'; import { SupplyDetailsGraph } from './components/SupplyDetailsGraph/SupplyDetailsGraph'; -import { TopPanel } from './components/TopPanel/TopPanel'; +import { ReserveOverview, TopPanel } from './components/TopPanel/TopPanel'; import { WalletOverview } from './components/WalletOverview/WalletOverview'; const pageTranslations = translations.aaveReserveOverviewPage; @@ -22,19 +26,65 @@ enum OverviewTab { } const AaveReserveOverviewPage: FC = () => { - const [asset] = useState({ symbol: 'BTC', name: 'Bitcoin' }); // TODO: mock + const [searchParams] = useSearchParams(); + const symbol = searchParams.get('asset') || 'ETH'; + const { reserves } = useAaveReservesData(); + const { data: interestRatesData } = useAaveInterestRatesData(); const [activeOverviewTab, setActiveOverviewTab] = useState( OverviewTab.RESERVE, ); + const reserve = useMemo( + () => reserves.find(r => r.symbol.toLowerCase() === symbol.toLowerCase()), + [reserves, symbol], + ); + + const reserveOverview: ReserveOverview = useMemo(() => { + if (!reserve) { + return { + symbol, + name: symbol, + underlyingAsset: '', + aTokenAddress: '', + variableDebtTokenAddress: '', + stableDebtTokenAddress: '', + reserveSize: Decimal.from(0), + availableLiquidity: Decimal.from(0), + utilizationRate: Decimal.from(0), + oraclePrice: Decimal.from(0), + oracleAddress: '', + }; + } + + return { + symbol: reserve.symbol, + name: reserve.name, + underlyingAsset: reserve.underlyingAsset, + aTokenAddress: reserve.aTokenAddress, + variableDebtTokenAddress: reserve.variableDebtTokenAddress, + stableDebtTokenAddress: reserve.stableDebtTokenAddress, + reserveSize: Decimal.from(reserve?.availableLiquidityUSD ?? 0).add( + reserve?.totalDebtUSD ?? 0, + ), + availableLiquidity: Decimal.from(reserve.availableLiquidityUSD), + utilizationRate: Decimal.from(reserve.borrowUsageRatio), + oraclePrice: Decimal.from(reserve.priceInUSD), + oracleAddress: reserve.priceOracle, + }; + }, [reserve, symbol]); + return (
{t(pageTranslations.meta.title)} - - + + + {t(pageTranslations.reserveStatusTab.fullTitle)} @@ -70,7 +120,9 @@ const AaveReserveOverviewPage: FC = () => { - + {interestRatesData && ( + + )}
{/* wallet column */} @@ -80,7 +132,7 @@ const AaveReserveOverviewPage: FC = () => { 'lg:block space-y-4 w-[450px] shrink-0', )} > - + diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx index 3e75d41ad..db244de56 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx @@ -1,4 +1,4 @@ -import React, { FC, useMemo, useState } from 'react'; +import React, { FC, useState } from 'react'; import { t } from 'i18next'; @@ -7,33 +7,28 @@ import { Accordion, Link } from '@sovryn/ui'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; +import { IRatesDataResult } from '../../../../../hooks/aave/useAaveRates'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; import { Chart } from './components/Chart/Chart'; -import { harcodedData } from './components/Chart/Chart.constants'; -import { MockData } from './components/Chart/Chart.types'; const pageTranslations = translations.aaveReserveOverviewPage.interestRateModel; -export const InterestRateModelGraph: FC = () => { - const [open, setOpen] = useState(true); +type InterestRateModelGraphProps = { + rates: IRatesDataResult; +}; + +export const InterestRateModelGraph: FC = ({ + rates, +}) => { + const [open, setOpen] = useState(true); const { isMobile } = useIsMobile(); - // TODO: mocked amounts - const mockData: MockData<{ x: number; y: number }> = useMemo(() => { - const data = harcodedData.values; - const currentData = harcodedData.annotations.current; - const optimalData = harcodedData.annotations.optimal; + const meta = { + label: t(pageTranslations.chart.label1), + lineColor: theme.colors['primary-30'], + }; - return { - data1: data, - data2: currentData, - data3: optimalData, - label1: t(pageTranslations.chart.label1), - lineColor: theme.colors['primary-30'], - xLabels: data.map(() => ''), - }; - }, []); return ( { flatMode={!isMobile} dataAttribute="interest-rate-model" > -
-
- } - /> - -
+ {rates && ( +
+
+ + } + /> + +
- - {/* statistics */} -
- } - /> - } - /> + + {/* statistics */} +
+ } + /> + } + /> +
-
+ )} ); }; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts index 241c37c9b..b5fc9efd5 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts @@ -20,26 +20,3 @@ export const CUSTOM_CANVAS_BACKGROUND_COLOR = { ctx.restore(); }, }; - -export const harcodedData = { - values: [ - { x: 0, y: 0 }, // Start of the curve - { x: 10, y: 1 }, // Small increase - { x: 20, y: 2 }, // Gradual increase - { x: 30, y: 3 }, // Gradual increase - { x: 40, y: 5 }, // Gradual increase - { x: 60, y: 9 }, // Steeper increase - { x: 92, y: 15 }, // Significant rise, matching the 78.64% mark - { x: 100, y: 100 }, // Sharp rise at the 92% mark - ], - annotations: { - current: [ - { x: 78.64, y: 0 }, - { x: 78.64, y: 50 }, // Point at the origin for the line to the x-axis - ], - optimal: [ - { x: 92, y: 0 }, - { x: 92, y: 70 }, // Point at the origin for the line to the x-axis - ], - }, -}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx index aa372e47f..f2050954e 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx @@ -1,27 +1,80 @@ -import React, { FC, useCallback, useEffect, useRef } from 'react'; +import React, { FC, useCallback, useEffect, useMemo, useRef } from 'react'; import ChartLibrary from 'chart.js/auto'; import 'chartjs-adapter-date-fns'; import { theme } from '@sovryn/tailwindcss-config'; +import { IRatesDataResult } from '../../../../../../../hooks/aave/useAaveRates'; import { CUSTOM_CANVAS_BACKGROUND_COLOR, GRID_COLOR, TICK_COLOR, } from './Chart.constants'; -import { MockData } from './Chart.types'; import { htmlLegendPlugin } from './Chart.utils'; type ChartProps = { - mockData: MockData<{ x: number; y: number }>; - yLabel1: string; + meta: { + label: string; + lineColor: string; + }; + rates: IRatesDataResult; }; -export const Chart: FC = ({ mockData }) => { +const calcInterestRateModel = ( + u: number, + base: number, + optimal: number, + slope1: number, + slope2: number, +) => { + if (u === 0) return 0; + + if (u <= optimal) return base + (u / optimal) * slope1; + + return base + slope1 + ((u - optimal) / (1 - optimal)) * slope2; +}; + +const calcVariableInterestRateModel = (u: number, rates: IRatesDataResult) => { + const base = parseFloat(rates.baseVariableBorrowRate); + const optimal = parseFloat(rates.optimalUsageRatio); + const slope1 = parseFloat(rates.variableRateSlope1); + const slope2 = parseFloat(rates.variableRateSlope2); + + return calcInterestRateModel(u, base, optimal, slope1, slope2); +}; +const CHART_PERCENTAGES = [0, 0.25, 0.5, 0.75, 1]; + +export const Chart: FC = ({ meta, rates }) => { const canvas = useRef(null); const chartRef = useRef(null); + const variableValues = useMemo( + () => + CHART_PERCENTAGES.map(x => ({ + x: x * 100, + y: calcVariableInterestRateModel(x, rates) * 100, + })), + [rates], + ); + + const optimalPercentage = parseFloat(rates.optimalUsageRatio) * 100; + const currentPercentage = parseFloat(rates.currentUsageRatio) * 100; + + const optimalValue = useMemo(() => { + return [ + { x: optimalPercentage, y: 0 }, + { x: optimalPercentage, y: 75 }, + ]; + }, [optimalPercentage]); + + const currentValue = useMemo(() => { + return [ + { x: currentPercentage, y: 0 }, + { x: currentPercentage, y: 75 }, + ]; + }, [currentPercentage]); + useEffect(() => { if (chartRef.current) { chartRef.current.destroy(); @@ -34,34 +87,34 @@ export const Chart: FC = ({ mockData }) => { chartRef.current = new ChartLibrary(canvas.current, { type: 'line', data: { - labels: ['0%', '50%', '100%'], + labels: ['0 %', '25 %', '50 %', '75 %', '100 %'], datasets: [ { type: 'line', - label: mockData.label1, - data: mockData.data1, - backgroundColor: mockData.lineColor, - borderColor: mockData.lineColor, + label: meta.label, + data: variableValues, + backgroundColor: meta.lineColor, + borderColor: meta.lineColor, borderWidth: 2, fill: false, pointRadius: 0, }, { - label: 'Current 78.64%', + label: `Optimal ${optimalPercentage}%`, type: 'scatter', - data: mockData.data2, - borderColor: theme.colors.positive, - backgroundColor: theme.colors.positive, + data: optimalValue, + borderColor: theme.colors.success, + backgroundColor: theme.colors.success, showLine: true, borderDash: [1, 2], pointRadius: 0, }, { - label: 'Optimal 92%', + label: `Current ${currentPercentage}%`, type: 'scatter', - data: mockData.data3, - borderColor: theme.colors.success, - backgroundColor: theme.colors.success, + data: currentValue, + borderColor: theme.colors.positive, + backgroundColor: theme.colors.positive, showLine: true, borderDash: [1, 2], pointRadius: 0, @@ -79,8 +132,7 @@ export const Chart: FC = ({ mockData }) => { callback: function (value) { return value + '%'; }, - maxTicksLimit: 5, - align: 'center', + //maxTicksLimit: 5, }, }, y: { @@ -101,9 +153,7 @@ export const Chart: FC = ({ mockData }) => { }, }, }, - layout: { - padding: 20, - }, + responsive: true, plugins: { legend: { display: false, @@ -118,7 +168,14 @@ export const Chart: FC = ({ mockData }) => { chartRef.current.destroy(); } }; - }, [mockData]); + }, [ + meta, + variableValues, + currentPercentage, + optimalPercentage, + optimalValue, + currentValue, + ]); const stopPropagation = useCallback( (e: React.MouseEvent) => { diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx index 416736aa5..c4240d299 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx @@ -1,32 +1,88 @@ -import React, { FC } from 'react'; +import React, { FC, useCallback, useMemo } from 'react'; import classNames from 'classnames'; import { t } from 'i18next'; -import { Heading, Icon, IconNames, Paragraph, ParagraphSize } from '@sovryn/ui'; +import { + Heading, + Icon, + IconNames, + Paragraph, + ParagraphSize, + Tooltip, + TooltipPlacement, + TooltipTrigger, +} from '@sovryn/ui'; +import { Decimal } from '@sovryn/utils'; + +import { BOB_CHAIN_ID } from '../../../../../config/chains'; import { WalletIcon } from '../../../../1_atoms/Icons/Icons'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; +import { useNotifyError } from '../../../../../hooks/useNotifyError'; import { translations } from '../../../../../locales/i18n'; +import { getBobExplorerUrl } from '../../../../../utils/helpers'; +import { formatUsdAmount } from './TopPanel.utils'; +import { ReserveTokens } from './components/ReserveTokens/ReserveTokens'; const pageTranslations = translations.aaveReserveOverviewPage.topPanel; +export type ReserveOverview = { + symbol: string; + name: string; + underlyingAsset: string; + aTokenAddress: string; + variableDebtTokenAddress: string; + stableDebtTokenAddress: string; + reserveSize: Decimal; + availableLiquidity: Decimal; + utilizationRate: Decimal; + oraclePrice: Decimal; + oracleAddress: string; +}; + type TopPanelProps = { - asset: { - name: string; - symbol: string; - }; + reserve: ReserveOverview; className?: string; }; -export const TopPanel: FC = ({ asset, className }) => { - // TODO: Mocked data - const reserveSizeInM = 1234.58; - const availableLiquidityM = 1234.58; - const utilizationRate = 2.79; - const oraclePrice = 11.5; +export const TopPanel: FC = ({ reserve, className }) => { + const { notifyError } = useNotifyError(); + + const openInExplorer = useCallback((tokenAddress: string) => { + const explorer = getBobExplorerUrl(); + window.open(`${explorer}/address/${tokenAddress}`, '_blank'); + }, []); + + const addToWallet = useCallback( + (token: string) => { + try { + if (!(window as any)?.ethereum) { + throw new Error('Wallet not available'); + } + + (window as any)?.ethereum.request({ + method: 'wallet_watchAsset', + params: { + type: 'ERC20', + options: { + chainId: BOB_CHAIN_ID, + address: token, + }, + }, + }); + } catch (error) { + notifyError(error); + } + }, + [notifyError], + ); + + const oracleLink = useMemo(() => { + return getBobExplorerUrl() + '/address/' + reserve.oracleAddress; + }, [reserve.oracleAddress]); return (
@@ -39,31 +95,73 @@ export const TopPanel: FC = ({ asset, className }) => {
-
+
- {asset.name} + {reserve.name}
-
- - - - - - +
+ {/* show token in explorer */} + + } + > +
+ +
+
+ + {/* add token to wallet */} + + } + > +
+ +
+
@@ -72,9 +170,8 @@ export const TopPanel: FC = ({ asset, className }) => { value={ } /> @@ -83,9 +180,8 @@ export const TopPanel: FC = ({ asset, className }) => { value={ } /> @@ -94,18 +190,20 @@ export const TopPanel: FC = ({ asset, className }) => { value={ } /> } diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.utils.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.utils.tsx new file mode 100644 index 000000000..f766624e0 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.utils.tsx @@ -0,0 +1,14 @@ +import { Decimal } from '@sovryn/utils'; + +export function formatUsdAmount(value: Decimal): { + value: string; + suffix: string; +} { + if (value.gte(1e6)) { + return { value: value.div(1e6).toString(1), suffix: 'M' }; + } else if (value.gte(1e3)) { + return { value: value.div(1e3).toString(1), suffix: 'K' }; + } else { + return { value: value.toString(), suffix: '' }; + } +} diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx new file mode 100644 index 000000000..ff929aa9e --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx @@ -0,0 +1,84 @@ +import React, { FC, ReactElement, useEffect, useMemo } from 'react'; + +import classNames from 'classnames'; +import { t } from 'i18next'; + +import { getAssetData } from '@sovryn/contracts'; + +import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; + +import { translations } from '../../../../../../../locales/i18n'; +import { TokenButton } from './components/TokenButton/TokenButton'; + +type ReserveTokensProps = { + symbol: string; + underlyingTokenAddress: string; + aTokenAddress: string; + variableDebtTokenAddress: string; + stableDebtTokenAddress: string; + onClick: (tokenAddress: string) => void; + className?: string; +}; + +export const ReserveTokens: FC = ({ + symbol, + onClick, + underlyingTokenAddress, + aTokenAddress, + variableDebtTokenAddress, + stableDebtTokenAddress, + className, +}) => { + const [tokenLogo, setTokenLogo] = React.useState(); + + useEffect(() => { + getAssetData(symbol, BOB_CHAIN_ID).then(data => { + setTokenLogo(data.icon); + }); + }, [symbol]); + + const TokenLogo: ReactElement = useMemo(() => { + if (!tokenLogo) return <>; + else return
; + }, [tokenLogo]); + + return ( +
+ onClick(underlyingTokenAddress)} + /> + + onClick(aTokenAddress)} + /> + + onClick(variableDebtTokenAddress)} + /> + + onClick(stableDebtTokenAddress)} + /> +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/components/TokenButton/TokenButton.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/components/TokenButton/TokenButton.tsx new file mode 100644 index 000000000..e25fa0012 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/components/TokenButton/TokenButton.tsx @@ -0,0 +1,35 @@ +import React, { FC, ReactNode } from 'react'; + +import { Paragraph, ParagraphSize } from '@sovryn/ui'; + +type TokenButtonProps = { + label: string; + logo: ReactNode; + title: string; + onClick: () => void; +}; + +export const TokenButton: FC = ({ + title, + label, + logo, + onClick, +}) => ( +
+
+ + {title} + +
+ +
+); diff --git a/apps/frontend/src/hooks/aave/ReserveStrategy-rateStrategyStableOne.json b/apps/frontend/src/hooks/aave/ReserveStrategy-rateStrategyStableOne.json new file mode 100644 index 000000000..16e065227 --- /dev/null +++ b/apps/frontend/src/hooks/aave/ReserveStrategy-rateStrategyStableOne.json @@ -0,0 +1,505 @@ +{ + "address": "0x94b5304ec379Dc28f664c8762182d1d12d2ce4A1", + "abi": [ + { + "inputs": [ + { + "internalType": "contract IPoolAddressesProvider", + "name": "provider", + "type": "address" + }, + { + "internalType": "uint256", + "name": "optimalUsageRatio", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baseVariableBorrowRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "variableRateSlope1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "variableRateSlope2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stableRateSlope1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stableRateSlope2", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baseStableRateOffset", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stableRateExcessOffset", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "optimalStableToTotalDebtRatio", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ADDRESSES_PROVIDER", + "outputs": [ + { + "internalType": "contract IPoolAddressesProvider", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_EXCESS_USAGE_RATIO", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPTIMAL_USAGE_RATIO", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "unbacked", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidityAdded", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidityTaken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalStableDebt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalVariableDebt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "averageStableBorrowRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveFactor", + "type": "uint256" + }, + { + "internalType": "address", + "name": "reserve", + "type": "address" + }, + { + "internalType": "address", + "name": "aToken", + "type": "address" + } + ], + "internalType": "struct DataTypes.CalculateInterestRatesParams", + "name": "params", + "type": "tuple" + } + ], + "name": "calculateInterestRates", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBaseStableBorrowRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBaseVariableBorrowRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMaxVariableBorrowRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStableRateExcessOffset", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStableRateSlope1", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStableRateSlope2", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVariableRateSlope1", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVariableRateSlope2", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "transactionHash": "0x8232b5ee9f78a2d8f2acf291a7c8c5636e7a8cbff634159dc7e30d5bc14c33fc", + "receipt": { + "to": null, + "from": "0xb7ba1Dea4a3745e58959a2091b47096cc197be5A", + "contractAddress": "0x94b5304ec379Dc28f664c8762182d1d12d2ce4A1", + "transactionIndex": 1, + "gasUsed": "723108", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1777b89670c47f8c37c2848d37c3c885742b9259901c4eb71b27c0cf1016fc46", + "transactionHash": "0x8232b5ee9f78a2d8f2acf291a7c8c5636e7a8cbff634159dc7e30d5bc14c33fc", + "logs": [], + "blockNumber": 13795948, + "cumulativeGasUsed": "766971", + "status": 1, + "byzantium": true + }, + "args": [ + "0xAE73edfC71af8f5fb7d8840887E8EE4317989456", + "900000000000000000000000000", + "0", + "40000000000000000000000000", + "600000000000000000000000000", + "5000000000000000000000000", + "600000000000000000000000000", + "10000000000000000000000000", + "80000000000000000000000000", + "200000000000000000000000000" + ], + "numDeployments": 1, + "solcInputHash": "f83ccc030186e5b58439131350431de3", + "metadata": "{\"compiler\":{\"version\":\"0.8.10+commit.fc410830\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"contract IPoolAddressesProvider\",\"name\":\"provider\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"optimalUsageRatio\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"baseVariableBorrowRate\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"variableRateSlope1\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"variableRateSlope2\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"stableRateSlope1\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"stableRateSlope2\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"baseStableRateOffset\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"stableRateExcessOffset\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"optimalStableToTotalDebtRatio\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ADDRESSES_PROVIDER\",\"outputs\":[{\"internalType\":\"contract IPoolAddressesProvider\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_EXCESS_USAGE_RATIO\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OPTIMAL_USAGE_RATIO\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"unbacked\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidityAdded\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidityTaken\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalStableDebt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalVariableDebt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"averageStableBorrowRate\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reserveFactor\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"reserve\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"aToken\",\"type\":\"address\"}],\"internalType\":\"struct DataTypes.CalculateInterestRatesParams\",\"name\":\"params\",\"type\":\"tuple\"}],\"name\":\"calculateInterestRates\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBaseStableBorrowRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBaseVariableBorrowRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMaxVariableBorrowRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStableRateExcessOffset\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStableRateSlope1\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStableRateSlope2\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getVariableRateSlope1\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getVariableRateSlope2\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Aave\",\"details\":\"The model of interest rate is based on 2 slopes, one before the `OPTIMAL_USAGE_RATIO` point of usage and another from that one to 100%. - An instance of this same contract, can't be used across different Aave markets, due to the caching of the PoolAddressesProvider\",\"kind\":\"dev\",\"methods\":{\"calculateInterestRates((uint256,uint256,uint256,uint256,uint256,uint256,uint256,address,address))\":{\"params\":{\"params\":\"The parameters needed to calculate interest rates\"},\"returns\":{\"_0\":\"liquidityRate The liquidity rate expressed in rays\",\"_1\":\"stableBorrowRate The stable borrow rate expressed in rays\",\"_2\":\"variableBorrowRate The variable borrow rate expressed in rays\"}},\"constructor\":{\"details\":\"Constructor.\",\"params\":{\"baseStableRateOffset\":\"The premium on top of variable rate for base stable borrowing rate\",\"baseVariableBorrowRate\":\"The base variable borrow rate\",\"optimalStableToTotalDebtRatio\":\"The optimal stable debt to total debt ratio of the reserve\",\"optimalUsageRatio\":\"The optimal usage ratio\",\"provider\":\"The address of the PoolAddressesProvider contract\",\"stableRateExcessOffset\":\"The premium on top of stable rate when there stable debt surpass the threshold\",\"stableRateSlope1\":\"The stable rate slope below optimal usage ratio\",\"stableRateSlope2\":\"The stable rate slope above optimal usage ratio\",\"variableRateSlope1\":\"The variable rate slope below optimal usage ratio\",\"variableRateSlope2\":\"The variable rate slope above optimal usage ratio\"}},\"getBaseStableBorrowRate()\":{\"returns\":{\"_0\":\"The base stable borrow rate, expressed in ray\"}},\"getBaseVariableBorrowRate()\":{\"returns\":{\"_0\":\"The base variable borrow rate, expressed in ray\"}},\"getMaxVariableBorrowRate()\":{\"returns\":{\"_0\":\"The maximum variable borrow rate, expressed in ray\"}},\"getStableRateExcessOffset()\":{\"details\":\"It's an additional premium applied to the stable when stable debt > OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO\",\"returns\":{\"_0\":\"The stable rate excess offset, expressed in ray\"}},\"getStableRateSlope1()\":{\"details\":\"It's the stable rate when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO\",\"returns\":{\"_0\":\"The stable rate slope, expressed in ray\"}},\"getStableRateSlope2()\":{\"details\":\"It's the variable rate when usage ratio > OPTIMAL_USAGE_RATIO\",\"returns\":{\"_0\":\"The stable rate slope, expressed in ray\"}},\"getVariableRateSlope1()\":{\"details\":\"It's the variable rate when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO\",\"returns\":{\"_0\":\"The variable rate slope, expressed in ray\"}},\"getVariableRateSlope2()\":{\"details\":\"It's the variable rate when usage ratio > OPTIMAL_USAGE_RATIO\",\"returns\":{\"_0\":\"The variable rate slope, expressed in ray\"}}},\"stateVariables\":{\"ADDRESSES_PROVIDER\":{\"return\":\"The address of the PoolAddressesProvider contract\",\"returns\":{\"_0\":\"The address of the PoolAddressesProvider contract\"}},\"MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO\":{\"details\":\"It's always equal to 1-optimal stable to total debt ratio (added as constant for gas optimizations)\",\"return\":\"The max excess stable to total debt ratio, expressed in ray.\",\"returns\":{\"_0\":\"The max excess stable to total debt ratio, expressed in ray.\"}},\"MAX_EXCESS_USAGE_RATIO\":{\"details\":\"It's always equal to 1-optimal usage ratio (added as constant for gas optimizations)\",\"return\":\"The max excess usage ratio, expressed in ray.\",\"returns\":{\"_0\":\"The max excess usage ratio, expressed in ray.\"}},\"OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO\":{\"return\":\"The optimal stable to total debt ratio, expressed in ray.\",\"returns\":{\"_0\":\"The optimal stable to total debt ratio, expressed in ray.\"}},\"OPTIMAL_USAGE_RATIO\":{\"return\":\"The optimal usage ratio, expressed in ray.\",\"returns\":{\"_0\":\"The optimal usage ratio, expressed in ray.\"}}},\"title\":\"DefaultReserveInterestRateStrategy contract\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"ADDRESSES_PROVIDER()\":{\"notice\":\"Returns the address of the PoolAddressesProvider\"},\"MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO()\":{\"notice\":\"Returns the excess stable debt ratio above the optimal.\"},\"MAX_EXCESS_USAGE_RATIO()\":{\"notice\":\"Returns the excess usage ratio above the optimal.\"},\"OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO()\":{\"notice\":\"Returns the optimal stable to total debt ratio of the reserve.\"},\"OPTIMAL_USAGE_RATIO()\":{\"notice\":\"Returns the usage ratio at which the pool aims to obtain most competitive borrow rates.\"},\"calculateInterestRates((uint256,uint256,uint256,uint256,uint256,uint256,uint256,address,address))\":{\"notice\":\"Calculates the interest rates depending on the reserve's state and configurations\"},\"getBaseStableBorrowRate()\":{\"notice\":\"Returns the base stable borrow rate\"},\"getBaseVariableBorrowRate()\":{\"notice\":\"Returns the base variable borrow rate\"},\"getMaxVariableBorrowRate()\":{\"notice\":\"Returns the maximum variable borrow rate\"},\"getStableRateExcessOffset()\":{\"notice\":\"Returns the stable rate excess offset\"},\"getStableRateSlope1()\":{\"notice\":\"Returns the stable rate slope below optimal usage ratio\"},\"getStableRateSlope2()\":{\"notice\":\"Returns the stable rate slope above optimal usage ratio\"},\"getVariableRateSlope1()\":{\"notice\":\"Returns the variable rate slope below optimal usage ratio\"},\"getVariableRateSlope2()\":{\"notice\":\"Returns the variable rate slope above optimal usage ratio\"}},\"notice\":\"Implements the calculation of the interest rates depending on the reserve state\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"@aave/core-v3/contracts/protocol/pool/DefaultReserveInterestRateStrategy.sol\":\"DefaultReserveInterestRateStrategy\"},\"evmVersion\":\"berlin\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":100000},\"remappings\":[]},\"sources\":{\"@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: AGPL-3.0\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address recipient, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0xf57d62241e553696a1324d225663ba2e1a51db0a51ca236d0c1b009d89b6284c\",\"license\":\"AGPL-3.0\"},\"@aave/core-v3/contracts/interfaces/IDefaultInterestRateStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: AGPL-3.0\\npragma solidity ^0.8.0;\\n\\nimport {IReserveInterestRateStrategy} from './IReserveInterestRateStrategy.sol';\\nimport {IPoolAddressesProvider} from './IPoolAddressesProvider.sol';\\n\\n/**\\n * @title IDefaultInterestRateStrategy\\n * @author Aave\\n * @notice Defines the basic interface of the DefaultReserveInterestRateStrategy\\n */\\ninterface IDefaultInterestRateStrategy is IReserveInterestRateStrategy {\\n /**\\n * @notice Returns the usage ratio at which the pool aims to obtain most competitive borrow rates.\\n * @return The optimal usage ratio, expressed in ray.\\n */\\n function OPTIMAL_USAGE_RATIO() external view returns (uint256);\\n\\n /**\\n * @notice Returns the optimal stable to total debt ratio of the reserve.\\n * @return The optimal stable to total debt ratio, expressed in ray.\\n */\\n function OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO() external view returns (uint256);\\n\\n /**\\n * @notice Returns the excess usage ratio above the optimal.\\n * @dev It's always equal to 1-optimal usage ratio (added as constant for gas optimizations)\\n * @return The max excess usage ratio, expressed in ray.\\n */\\n function MAX_EXCESS_USAGE_RATIO() external view returns (uint256);\\n\\n /**\\n * @notice Returns the excess stable debt ratio above the optimal.\\n * @dev It's always equal to 1-optimal stable to total debt ratio (added as constant for gas optimizations)\\n * @return The max excess stable to total debt ratio, expressed in ray.\\n */\\n function MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO() external view returns (uint256);\\n\\n /**\\n * @notice Returns the address of the PoolAddressesProvider\\n * @return The address of the PoolAddressesProvider contract\\n */\\n function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);\\n\\n /**\\n * @notice Returns the variable rate slope below optimal usage ratio\\n * @dev It's the variable rate when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO\\n * @return The variable rate slope, expressed in ray\\n */\\n function getVariableRateSlope1() external view returns (uint256);\\n\\n /**\\n * @notice Returns the variable rate slope above optimal usage ratio\\n * @dev It's the variable rate when usage ratio > OPTIMAL_USAGE_RATIO\\n * @return The variable rate slope, expressed in ray\\n */\\n function getVariableRateSlope2() external view returns (uint256);\\n\\n /**\\n * @notice Returns the stable rate slope below optimal usage ratio\\n * @dev It's the stable rate when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO\\n * @return The stable rate slope, expressed in ray\\n */\\n function getStableRateSlope1() external view returns (uint256);\\n\\n /**\\n * @notice Returns the stable rate slope above optimal usage ratio\\n * @dev It's the variable rate when usage ratio > OPTIMAL_USAGE_RATIO\\n * @return The stable rate slope, expressed in ray\\n */\\n function getStableRateSlope2() external view returns (uint256);\\n\\n /**\\n * @notice Returns the stable rate excess offset\\n * @dev It's an additional premium applied to the stable when stable debt > OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO\\n * @return The stable rate excess offset, expressed in ray\\n */\\n function getStableRateExcessOffset() external view returns (uint256);\\n\\n /**\\n * @notice Returns the base stable borrow rate\\n * @return The base stable borrow rate, expressed in ray\\n */\\n function getBaseStableBorrowRate() external view returns (uint256);\\n\\n /**\\n * @notice Returns the base variable borrow rate\\n * @return The base variable borrow rate, expressed in ray\\n */\\n function getBaseVariableBorrowRate() external view returns (uint256);\\n\\n /**\\n * @notice Returns the maximum variable borrow rate\\n * @return The maximum variable borrow rate, expressed in ray\\n */\\n function getMaxVariableBorrowRate() external view returns (uint256);\\n}\\n\",\"keccak256\":\"0xb7351f5dc779d86fc6d4aafb2fe48622b2dae3a00724923b8cd92b5c676ca893\",\"license\":\"AGPL-3.0\"},\"@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol\":{\"content\":\"// SPDX-License-Identifier: AGPL-3.0\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title IPoolAddressesProvider\\n * @author Aave\\n * @notice Defines the basic interface for a Pool Addresses Provider.\\n */\\ninterface IPoolAddressesProvider {\\n /**\\n * @dev Emitted when the market identifier is updated.\\n * @param oldMarketId The old id of the market\\n * @param newMarketId The new id of the market\\n */\\n event MarketIdSet(string indexed oldMarketId, string indexed newMarketId);\\n\\n /**\\n * @dev Emitted when the pool is updated.\\n * @param oldAddress The old address of the Pool\\n * @param newAddress The new address of the Pool\\n */\\n event PoolUpdated(address indexed oldAddress, address indexed newAddress);\\n\\n /**\\n * @dev Emitted when the pool configurator is updated.\\n * @param oldAddress The old address of the PoolConfigurator\\n * @param newAddress The new address of the PoolConfigurator\\n */\\n event PoolConfiguratorUpdated(address indexed oldAddress, address indexed newAddress);\\n\\n /**\\n * @dev Emitted when the price oracle is updated.\\n * @param oldAddress The old address of the PriceOracle\\n * @param newAddress The new address of the PriceOracle\\n */\\n event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);\\n\\n /**\\n * @dev Emitted when the ACL manager is updated.\\n * @param oldAddress The old address of the ACLManager\\n * @param newAddress The new address of the ACLManager\\n */\\n event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);\\n\\n /**\\n * @dev Emitted when the ACL admin is updated.\\n * @param oldAddress The old address of the ACLAdmin\\n * @param newAddress The new address of the ACLAdmin\\n */\\n event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);\\n\\n /**\\n * @dev Emitted when the price oracle sentinel is updated.\\n * @param oldAddress The old address of the PriceOracleSentinel\\n * @param newAddress The new address of the PriceOracleSentinel\\n */\\n event PriceOracleSentinelUpdated(address indexed oldAddress, address indexed newAddress);\\n\\n /**\\n * @dev Emitted when the pool data provider is updated.\\n * @param oldAddress The old address of the PoolDataProvider\\n * @param newAddress The new address of the PoolDataProvider\\n */\\n event PoolDataProviderUpdated(address indexed oldAddress, address indexed newAddress);\\n\\n /**\\n * @dev Emitted when a new proxy is created.\\n * @param id The identifier of the proxy\\n * @param proxyAddress The address of the created proxy contract\\n * @param implementationAddress The address of the implementation contract\\n */\\n event ProxyCreated(\\n bytes32 indexed id,\\n address indexed proxyAddress,\\n address indexed implementationAddress\\n );\\n\\n /**\\n * @dev Emitted when a new non-proxied contract address is registered.\\n * @param id The identifier of the contract\\n * @param oldAddress The address of the old contract\\n * @param newAddress The address of the new contract\\n */\\n event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);\\n\\n /**\\n * @dev Emitted when the implementation of the proxy registered with id is updated\\n * @param id The identifier of the contract\\n * @param proxyAddress The address of the proxy contract\\n * @param oldImplementationAddress The address of the old implementation contract\\n * @param newImplementationAddress The address of the new implementation contract\\n */\\n event AddressSetAsProxy(\\n bytes32 indexed id,\\n address indexed proxyAddress,\\n address oldImplementationAddress,\\n address indexed newImplementationAddress\\n );\\n\\n /**\\n * @notice Returns the id of the Aave market to which this contract points to.\\n * @return The market id\\n */\\n function getMarketId() external view returns (string memory);\\n\\n /**\\n * @notice Associates an id with a specific PoolAddressesProvider.\\n * @dev This can be used to create an onchain registry of PoolAddressesProviders to\\n * identify and validate multiple Aave markets.\\n * @param newMarketId The market id\\n */\\n function setMarketId(string calldata newMarketId) external;\\n\\n /**\\n * @notice Returns an address by its identifier.\\n * @dev The returned address might be an EOA or a contract, potentially proxied\\n * @dev It returns ZERO if there is no registered address with the given id\\n * @param id The id\\n * @return The address of the registered for the specified id\\n */\\n function getAddress(bytes32 id) external view returns (address);\\n\\n /**\\n * @notice General function to update the implementation of a proxy registered with\\n * certain `id`. If there is no proxy registered, it will instantiate one and\\n * set as implementation the `newImplementationAddress`.\\n * @dev IMPORTANT Use this function carefully, only for ids that don't have an explicit\\n * setter function, in order to avoid unexpected consequences\\n * @param id The id\\n * @param newImplementationAddress The address of the new implementation\\n */\\n function setAddressAsProxy(bytes32 id, address newImplementationAddress) external;\\n\\n /**\\n * @notice Sets an address for an id replacing the address saved in the addresses map.\\n * @dev IMPORTANT Use this function carefully, as it will do a hard replacement\\n * @param id The id\\n * @param newAddress The address to set\\n */\\n function setAddress(bytes32 id, address newAddress) external;\\n\\n /**\\n * @notice Returns the address of the Pool proxy.\\n * @return The Pool proxy address\\n */\\n function getPool() external view returns (address);\\n\\n /**\\n * @notice Updates the implementation of the Pool, or creates a proxy\\n * setting the new `pool` implementation when the function is called for the first time.\\n * @param newPoolImpl The new Pool implementation\\n */\\n function setPoolImpl(address newPoolImpl) external;\\n\\n /**\\n * @notice Returns the address of the PoolConfigurator proxy.\\n * @return The PoolConfigurator proxy address\\n */\\n function getPoolConfigurator() external view returns (address);\\n\\n /**\\n * @notice Updates the implementation of the PoolConfigurator, or creates a proxy\\n * setting the new `PoolConfigurator` implementation when the function is called for the first time.\\n * @param newPoolConfiguratorImpl The new PoolConfigurator implementation\\n */\\n function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external;\\n\\n /**\\n * @notice Returns the address of the price oracle.\\n * @return The address of the PriceOracle\\n */\\n function getPriceOracle() external view returns (address);\\n\\n /**\\n * @notice Updates the address of the price oracle.\\n * @param newPriceOracle The address of the new PriceOracle\\n */\\n function setPriceOracle(address newPriceOracle) external;\\n\\n /**\\n * @notice Returns the address of the ACL manager.\\n * @return The address of the ACLManager\\n */\\n function getACLManager() external view returns (address);\\n\\n /**\\n * @notice Updates the address of the ACL manager.\\n * @param newAclManager The address of the new ACLManager\\n */\\n function setACLManager(address newAclManager) external;\\n\\n /**\\n * @notice Returns the address of the ACL admin.\\n * @return The address of the ACL admin\\n */\\n function getACLAdmin() external view returns (address);\\n\\n /**\\n * @notice Updates the address of the ACL admin.\\n * @param newAclAdmin The address of the new ACL admin\\n */\\n function setACLAdmin(address newAclAdmin) external;\\n\\n /**\\n * @notice Returns the address of the price oracle sentinel.\\n * @return The address of the PriceOracleSentinel\\n */\\n function getPriceOracleSentinel() external view returns (address);\\n\\n /**\\n * @notice Updates the address of the price oracle sentinel.\\n * @param newPriceOracleSentinel The address of the new PriceOracleSentinel\\n */\\n function setPriceOracleSentinel(address newPriceOracleSentinel) external;\\n\\n /**\\n * @notice Returns the address of the data provider.\\n * @return The address of the DataProvider\\n */\\n function getPoolDataProvider() external view returns (address);\\n\\n /**\\n * @notice Updates the address of the data provider.\\n * @param newDataProvider The address of the new DataProvider\\n */\\n function setPoolDataProvider(address newDataProvider) external;\\n}\\n\",\"keccak256\":\"0x33d4308d9407b4ee2297fc4ba5acce1a96a6c658189e2778a4f6b90e032fb3b5\",\"license\":\"AGPL-3.0\"},\"@aave/core-v3/contracts/interfaces/IReserveInterestRateStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: AGPL-3.0\\npragma solidity ^0.8.0;\\n\\nimport {DataTypes} from '../protocol/libraries/types/DataTypes.sol';\\n\\n/**\\n * @title IReserveInterestRateStrategy\\n * @author Aave\\n * @notice Interface for the calculation of the interest rates\\n */\\ninterface IReserveInterestRateStrategy {\\n /**\\n * @notice Calculates the interest rates depending on the reserve's state and configurations\\n * @param params The parameters needed to calculate interest rates\\n * @return liquidityRate The liquidity rate expressed in rays\\n * @return stableBorrowRate The stable borrow rate expressed in rays\\n * @return variableBorrowRate The variable borrow rate expressed in rays\\n */\\n function calculateInterestRates(\\n DataTypes.CalculateInterestRatesParams memory params\\n ) external view returns (uint256, uint256, uint256);\\n}\\n\",\"keccak256\":\"0x9028d29b6fda6f89b887a627ce5e03a401c4ccac98bfe14afcaf69ff09312202\",\"license\":\"AGPL-3.0\"},\"@aave/core-v3/contracts/protocol/libraries/helpers/Errors.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Errors library\\n * @author Aave\\n * @notice Defines the error messages emitted by the different contracts of the Aave protocol\\n */\\nlibrary Errors {\\n string public constant CALLER_NOT_POOL_ADMIN = '1'; // 'The caller of the function is not a pool admin'\\n string public constant CALLER_NOT_EMERGENCY_ADMIN = '2'; // 'The caller of the function is not an emergency admin'\\n string public constant CALLER_NOT_POOL_OR_EMERGENCY_ADMIN = '3'; // 'The caller of the function is not a pool or emergency admin'\\n string public constant CALLER_NOT_RISK_OR_POOL_ADMIN = '4'; // 'The caller of the function is not a risk or pool admin'\\n string public constant CALLER_NOT_ASSET_LISTING_OR_POOL_ADMIN = '5'; // 'The caller of the function is not an asset listing or pool admin'\\n string public constant CALLER_NOT_BRIDGE = '6'; // 'The caller of the function is not a bridge'\\n string public constant ADDRESSES_PROVIDER_NOT_REGISTERED = '7'; // 'Pool addresses provider is not registered'\\n string public constant INVALID_ADDRESSES_PROVIDER_ID = '8'; // 'Invalid id for the pool addresses provider'\\n string public constant NOT_CONTRACT = '9'; // 'Address is not a contract'\\n string public constant CALLER_NOT_POOL_CONFIGURATOR = '10'; // 'The caller of the function is not the pool configurator'\\n string public constant CALLER_NOT_ATOKEN = '11'; // 'The caller of the function is not an AToken'\\n string public constant INVALID_ADDRESSES_PROVIDER = '12'; // 'The address of the pool addresses provider is invalid'\\n string public constant INVALID_FLASHLOAN_EXECUTOR_RETURN = '13'; // 'Invalid return value of the flashloan executor function'\\n string public constant RESERVE_ALREADY_ADDED = '14'; // 'Reserve has already been added to reserve list'\\n string public constant NO_MORE_RESERVES_ALLOWED = '15'; // 'Maximum amount of reserves in the pool reached'\\n string public constant EMODE_CATEGORY_RESERVED = '16'; // 'Zero eMode category is reserved for volatile heterogeneous assets'\\n string public constant INVALID_EMODE_CATEGORY_ASSIGNMENT = '17'; // 'Invalid eMode category assignment to asset'\\n string public constant RESERVE_LIQUIDITY_NOT_ZERO = '18'; // 'The liquidity of the reserve needs to be 0'\\n string public constant FLASHLOAN_PREMIUM_INVALID = '19'; // 'Invalid flashloan premium'\\n string public constant INVALID_RESERVE_PARAMS = '20'; // 'Invalid risk parameters for the reserve'\\n string public constant INVALID_EMODE_CATEGORY_PARAMS = '21'; // 'Invalid risk parameters for the eMode category'\\n string public constant BRIDGE_PROTOCOL_FEE_INVALID = '22'; // 'Invalid bridge protocol fee'\\n string public constant CALLER_MUST_BE_POOL = '23'; // 'The caller of this function must be a pool'\\n string public constant INVALID_MINT_AMOUNT = '24'; // 'Invalid amount to mint'\\n string public constant INVALID_BURN_AMOUNT = '25'; // 'Invalid amount to burn'\\n string public constant INVALID_AMOUNT = '26'; // 'Amount must be greater than 0'\\n string public constant RESERVE_INACTIVE = '27'; // 'Action requires an active reserve'\\n string public constant RESERVE_FROZEN = '28'; // 'Action cannot be performed because the reserve is frozen'\\n string public constant RESERVE_PAUSED = '29'; // 'Action cannot be performed because the reserve is paused'\\n string public constant BORROWING_NOT_ENABLED = '30'; // 'Borrowing is not enabled'\\n string public constant STABLE_BORROWING_NOT_ENABLED = '31'; // 'Stable borrowing is not enabled'\\n string public constant NOT_ENOUGH_AVAILABLE_USER_BALANCE = '32'; // 'User cannot withdraw more than the available balance'\\n string public constant INVALID_INTEREST_RATE_MODE_SELECTED = '33'; // 'Invalid interest rate mode selected'\\n string public constant COLLATERAL_BALANCE_IS_ZERO = '34'; // 'The collateral balance is 0'\\n string public constant HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '35'; // 'Health factor is lesser than the liquidation threshold'\\n string public constant COLLATERAL_CANNOT_COVER_NEW_BORROW = '36'; // 'There is not enough collateral to cover a new borrow'\\n string public constant COLLATERAL_SAME_AS_BORROWING_CURRENCY = '37'; // 'Collateral is (mostly) the same currency that is being borrowed'\\n string public constant AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE = '38'; // 'The requested amount is greater than the max loan size in stable rate mode'\\n string public constant NO_DEBT_OF_SELECTED_TYPE = '39'; // 'For repayment of a specific type of debt, the user needs to have debt that type'\\n string public constant NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '40'; // 'To repay on behalf of a user an explicit amount to repay is needed'\\n string public constant NO_OUTSTANDING_STABLE_DEBT = '41'; // 'User does not have outstanding stable rate debt on this reserve'\\n string public constant NO_OUTSTANDING_VARIABLE_DEBT = '42'; // 'User does not have outstanding variable rate debt on this reserve'\\n string public constant UNDERLYING_BALANCE_ZERO = '43'; // 'The underlying balance needs to be greater than 0'\\n string public constant INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '44'; // 'Interest rate rebalance conditions were not met'\\n string public constant HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '45'; // 'Health factor is not below the threshold'\\n string public constant COLLATERAL_CANNOT_BE_LIQUIDATED = '46'; // 'The collateral chosen cannot be liquidated'\\n string public constant SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '47'; // 'User did not borrow the specified currency'\\n string public constant INCONSISTENT_FLASHLOAN_PARAMS = '49'; // 'Inconsistent flashloan parameters'\\n string public constant BORROW_CAP_EXCEEDED = '50'; // 'Borrow cap is exceeded'\\n string public constant SUPPLY_CAP_EXCEEDED = '51'; // 'Supply cap is exceeded'\\n string public constant UNBACKED_MINT_CAP_EXCEEDED = '52'; // 'Unbacked mint cap is exceeded'\\n string public constant DEBT_CEILING_EXCEEDED = '53'; // 'Debt ceiling is exceeded'\\n string public constant UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO = '54'; // 'Claimable rights over underlying not zero (aToken supply or accruedToTreasury)'\\n string public constant STABLE_DEBT_NOT_ZERO = '55'; // 'Stable debt supply is not zero'\\n string public constant VARIABLE_DEBT_SUPPLY_NOT_ZERO = '56'; // 'Variable debt supply is not zero'\\n string public constant LTV_VALIDATION_FAILED = '57'; // 'Ltv validation failed'\\n string public constant INCONSISTENT_EMODE_CATEGORY = '58'; // 'Inconsistent eMode category'\\n string public constant PRICE_ORACLE_SENTINEL_CHECK_FAILED = '59'; // 'Price oracle sentinel validation failed'\\n string public constant ASSET_NOT_BORROWABLE_IN_ISOLATION = '60'; // 'Asset is not borrowable in isolation mode'\\n string public constant RESERVE_ALREADY_INITIALIZED = '61'; // 'Reserve has already been initialized'\\n string public constant USER_IN_ISOLATION_MODE_OR_LTV_ZERO = '62'; // 'User is in isolation mode or ltv is zero'\\n string public constant INVALID_LTV = '63'; // 'Invalid ltv parameter for the reserve'\\n string public constant INVALID_LIQ_THRESHOLD = '64'; // 'Invalid liquidity threshold parameter for the reserve'\\n string public constant INVALID_LIQ_BONUS = '65'; // 'Invalid liquidity bonus parameter for the reserve'\\n string public constant INVALID_DECIMALS = '66'; // 'Invalid decimals parameter of the underlying asset of the reserve'\\n string public constant INVALID_RESERVE_FACTOR = '67'; // 'Invalid reserve factor parameter for the reserve'\\n string public constant INVALID_BORROW_CAP = '68'; // 'Invalid borrow cap for the reserve'\\n string public constant INVALID_SUPPLY_CAP = '69'; // 'Invalid supply cap for the reserve'\\n string public constant INVALID_LIQUIDATION_PROTOCOL_FEE = '70'; // 'Invalid liquidation protocol fee for the reserve'\\n string public constant INVALID_EMODE_CATEGORY = '71'; // 'Invalid eMode category for the reserve'\\n string public constant INVALID_UNBACKED_MINT_CAP = '72'; // 'Invalid unbacked mint cap for the reserve'\\n string public constant INVALID_DEBT_CEILING = '73'; // 'Invalid debt ceiling for the reserve\\n string public constant INVALID_RESERVE_INDEX = '74'; // 'Invalid reserve index'\\n string public constant ACL_ADMIN_CANNOT_BE_ZERO = '75'; // 'ACL admin cannot be set to the zero address'\\n string public constant INCONSISTENT_PARAMS_LENGTH = '76'; // 'Array parameters that should be equal length are not'\\n string public constant ZERO_ADDRESS_NOT_VALID = '77'; // 'Zero address not valid'\\n string public constant INVALID_EXPIRATION = '78'; // 'Invalid expiration'\\n string public constant INVALID_SIGNATURE = '79'; // 'Invalid signature'\\n string public constant OPERATION_NOT_SUPPORTED = '80'; // 'Operation not supported'\\n string public constant DEBT_CEILING_NOT_ZERO = '81'; // 'Debt ceiling is not zero'\\n string public constant ASSET_NOT_LISTED = '82'; // 'Asset is not listed'\\n string public constant INVALID_OPTIMAL_USAGE_RATIO = '83'; // 'Invalid optimal usage ratio'\\n string public constant INVALID_OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO = '84'; // 'Invalid optimal stable to total debt ratio'\\n string public constant UNDERLYING_CANNOT_BE_RESCUED = '85'; // 'The underlying asset cannot be rescued'\\n string public constant ADDRESSES_PROVIDER_ALREADY_ADDED = '86'; // 'Reserve has already been added to reserve list'\\n string public constant POOL_ADDRESSES_DO_NOT_MATCH = '87'; // 'The token implementation pool address and the pool address provided by the initializing pool do not match'\\n string public constant STABLE_BORROWING_ENABLED = '88'; // 'Stable borrowing is enabled'\\n string public constant SILOED_BORROWING_VIOLATION = '89'; // 'User is trying to borrow multiple assets including a siloed one'\\n string public constant RESERVE_DEBT_NOT_ZERO = '90'; // the total debt of the reserve needs to be 0\\n string public constant FLASHLOAN_DISABLED = '91'; // FlashLoaning for this asset is disabled\\n}\\n\",\"keccak256\":\"0x61757945ed506349f2cec8b99806124ef17f70644faba9860fb134df8ca34e86\",\"license\":\"BUSL-1.1\"},\"@aave/core-v3/contracts/protocol/libraries/math/PercentageMath.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title PercentageMath library\\n * @author Aave\\n * @notice Provides functions to perform percentage calculations\\n * @dev Percentages are defined by default with 2 decimals of precision (100.00). The precision is indicated by PERCENTAGE_FACTOR\\n * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.\\n */\\nlibrary PercentageMath {\\n // Maximum percentage factor (100.00%)\\n uint256 internal constant PERCENTAGE_FACTOR = 1e4;\\n\\n // Half percentage factor (50.00%)\\n uint256 internal constant HALF_PERCENTAGE_FACTOR = 0.5e4;\\n\\n /**\\n * @notice Executes a percentage multiplication\\n * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328\\n * @param value The value of which the percentage needs to be calculated\\n * @param percentage The percentage of the value to be calculated\\n * @return result value percentmul percentage\\n */\\n function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256 result) {\\n // to avoid overflow, value <= (type(uint256).max - HALF_PERCENTAGE_FACTOR) / percentage\\n assembly {\\n if iszero(\\n or(\\n iszero(percentage),\\n iszero(gt(value, div(sub(not(0), HALF_PERCENTAGE_FACTOR), percentage)))\\n )\\n ) {\\n revert(0, 0)\\n }\\n\\n result := div(add(mul(value, percentage), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)\\n }\\n }\\n\\n /**\\n * @notice Executes a percentage division\\n * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328\\n * @param value The value of which the percentage needs to be calculated\\n * @param percentage The percentage of the value to be calculated\\n * @return result value percentdiv percentage\\n */\\n function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256 result) {\\n // to avoid overflow, value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR\\n assembly {\\n if or(\\n iszero(percentage),\\n iszero(iszero(gt(value, div(sub(not(0), div(percentage, 2)), PERCENTAGE_FACTOR))))\\n ) {\\n revert(0, 0)\\n }\\n\\n result := div(add(mul(value, PERCENTAGE_FACTOR), div(percentage, 2)), percentage)\\n }\\n }\\n}\\n\",\"keccak256\":\"0x6a7dcf18e1af47b69c8dd58093b0134e3689bf719ba63eae485d8f9dfc10cac7\",\"license\":\"BUSL-1.1\"},\"@aave/core-v3/contracts/protocol/libraries/math/WadRayMath.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title WadRayMath library\\n * @author Aave\\n * @notice Provides functions to perform calculations with Wad and Ray units\\n * @dev Provides mul and div function for wads (decimal numbers with 18 digits of precision) and rays (decimal numbers\\n * with 27 digits of precision)\\n * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.\\n */\\nlibrary WadRayMath {\\n // HALF_WAD and HALF_RAY expressed with extended notation as constant with operations are not supported in Yul assembly\\n uint256 internal constant WAD = 1e18;\\n uint256 internal constant HALF_WAD = 0.5e18;\\n\\n uint256 internal constant RAY = 1e27;\\n uint256 internal constant HALF_RAY = 0.5e27;\\n\\n uint256 internal constant WAD_RAY_RATIO = 1e9;\\n\\n /**\\n * @dev Multiplies two wad, rounding half up to the nearest wad\\n * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328\\n * @param a Wad\\n * @param b Wad\\n * @return c = a*b, in wad\\n */\\n function wadMul(uint256 a, uint256 b) internal pure returns (uint256 c) {\\n // to avoid overflow, a <= (type(uint256).max - HALF_WAD) / b\\n assembly {\\n if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_WAD), b))))) {\\n revert(0, 0)\\n }\\n\\n c := div(add(mul(a, b), HALF_WAD), WAD)\\n }\\n }\\n\\n /**\\n * @dev Divides two wad, rounding half up to the nearest wad\\n * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328\\n * @param a Wad\\n * @param b Wad\\n * @return c = a/b, in wad\\n */\\n function wadDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {\\n // to avoid overflow, a <= (type(uint256).max - halfB) / WAD\\n assembly {\\n if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), WAD))))) {\\n revert(0, 0)\\n }\\n\\n c := div(add(mul(a, WAD), div(b, 2)), b)\\n }\\n }\\n\\n /**\\n * @notice Multiplies two ray, rounding half up to the nearest ray\\n * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328\\n * @param a Ray\\n * @param b Ray\\n * @return c = a raymul b\\n */\\n function rayMul(uint256 a, uint256 b) internal pure returns (uint256 c) {\\n // to avoid overflow, a <= (type(uint256).max - HALF_RAY) / b\\n assembly {\\n if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_RAY), b))))) {\\n revert(0, 0)\\n }\\n\\n c := div(add(mul(a, b), HALF_RAY), RAY)\\n }\\n }\\n\\n /**\\n * @notice Divides two ray, rounding half up to the nearest ray\\n * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328\\n * @param a Ray\\n * @param b Ray\\n * @return c = a raydiv b\\n */\\n function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {\\n // to avoid overflow, a <= (type(uint256).max - halfB) / RAY\\n assembly {\\n if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), RAY))))) {\\n revert(0, 0)\\n }\\n\\n c := div(add(mul(a, RAY), div(b, 2)), b)\\n }\\n }\\n\\n /**\\n * @dev Casts ray down to wad\\n * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328\\n * @param a Ray\\n * @return b = a converted to wad, rounded half up to the nearest wad\\n */\\n function rayToWad(uint256 a) internal pure returns (uint256 b) {\\n assembly {\\n b := div(a, WAD_RAY_RATIO)\\n let remainder := mod(a, WAD_RAY_RATIO)\\n if iszero(lt(remainder, div(WAD_RAY_RATIO, 2))) {\\n b := add(b, 1)\\n }\\n }\\n }\\n\\n /**\\n * @dev Converts wad up to ray\\n * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328\\n * @param a Wad\\n * @return b = a converted in ray\\n */\\n function wadToRay(uint256 a) internal pure returns (uint256 b) {\\n // to avoid overflow, b/WAD_RAY_RATIO == a\\n assembly {\\n b := mul(a, WAD_RAY_RATIO)\\n\\n if iszero(eq(div(b, WAD_RAY_RATIO), a)) {\\n revert(0, 0)\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x618fe1876e322a10269e4a96e61e516bbbec883cb79e20b508f8010027178f07\",\"license\":\"BUSL-1.1\"},\"@aave/core-v3/contracts/protocol/libraries/types/DataTypes.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nlibrary DataTypes {\\n struct ReserveData {\\n //stores the reserve configuration\\n ReserveConfigurationMap configuration;\\n //the liquidity index. Expressed in ray\\n uint128 liquidityIndex;\\n //the current supply rate. Expressed in ray\\n uint128 currentLiquidityRate;\\n //variable borrow index. Expressed in ray\\n uint128 variableBorrowIndex;\\n //the current variable borrow rate. Expressed in ray\\n uint128 currentVariableBorrowRate;\\n //the current stable borrow rate. Expressed in ray\\n uint128 currentStableBorrowRate;\\n //timestamp of last update\\n uint40 lastUpdateTimestamp;\\n //the id of the reserve. Represents the position in the list of the active reserves\\n uint16 id;\\n //aToken address\\n address aTokenAddress;\\n //stableDebtToken address\\n address stableDebtTokenAddress;\\n //variableDebtToken address\\n address variableDebtTokenAddress;\\n //address of the interest rate strategy\\n address interestRateStrategyAddress;\\n //the current treasury balance, scaled\\n uint128 accruedToTreasury;\\n //the outstanding unbacked aTokens minted through the bridging feature\\n uint128 unbacked;\\n //the outstanding debt borrowed against this asset in isolation mode\\n uint128 isolationModeTotalDebt;\\n }\\n\\n struct ReserveConfigurationMap {\\n //bit 0-15: LTV\\n //bit 16-31: Liq. threshold\\n //bit 32-47: Liq. bonus\\n //bit 48-55: Decimals\\n //bit 56: reserve is active\\n //bit 57: reserve is frozen\\n //bit 58: borrowing is enabled\\n //bit 59: stable rate borrowing enabled\\n //bit 60: asset is paused\\n //bit 61: borrowing in isolation mode is enabled\\n //bit 62: siloed borrowing enabled\\n //bit 63: flashloaning enabled\\n //bit 64-79: reserve factor\\n //bit 80-115 borrow cap in whole tokens, borrowCap == 0 => no cap\\n //bit 116-151 supply cap in whole tokens, supplyCap == 0 => no cap\\n //bit 152-167 liquidation protocol fee\\n //bit 168-175 eMode category\\n //bit 176-211 unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled\\n //bit 212-251 debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals\\n //bit 252-255 unused\\n\\n uint256 data;\\n }\\n\\n struct UserConfigurationMap {\\n /**\\n * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset.\\n * The first bit indicates if an asset is used as collateral by the user, the second whether an\\n * asset is borrowed by the user.\\n */\\n uint256 data;\\n }\\n\\n struct EModeCategory {\\n // each eMode category has a custom ltv and liquidation threshold\\n uint16 ltv;\\n uint16 liquidationThreshold;\\n uint16 liquidationBonus;\\n // each eMode category may or may not have a custom oracle to override the individual assets price oracles\\n address priceSource;\\n string label;\\n }\\n\\n enum InterestRateMode {NONE, STABLE, VARIABLE}\\n\\n struct ReserveCache {\\n uint256 currScaledVariableDebt;\\n uint256 nextScaledVariableDebt;\\n uint256 currPrincipalStableDebt;\\n uint256 currAvgStableBorrowRate;\\n uint256 currTotalStableDebt;\\n uint256 nextAvgStableBorrowRate;\\n uint256 nextTotalStableDebt;\\n uint256 currLiquidityIndex;\\n uint256 nextLiquidityIndex;\\n uint256 currVariableBorrowIndex;\\n uint256 nextVariableBorrowIndex;\\n uint256 currLiquidityRate;\\n uint256 currVariableBorrowRate;\\n uint256 reserveFactor;\\n ReserveConfigurationMap reserveConfiguration;\\n address aTokenAddress;\\n address stableDebtTokenAddress;\\n address variableDebtTokenAddress;\\n uint40 reserveLastUpdateTimestamp;\\n uint40 stableDebtLastUpdateTimestamp;\\n }\\n\\n struct ExecuteLiquidationCallParams {\\n uint256 reservesCount;\\n uint256 debtToCover;\\n address collateralAsset;\\n address debtAsset;\\n address user;\\n bool receiveAToken;\\n address priceOracle;\\n uint8 userEModeCategory;\\n address priceOracleSentinel;\\n }\\n\\n struct ExecuteSupplyParams {\\n address asset;\\n uint256 amount;\\n address onBehalfOf;\\n uint16 referralCode;\\n }\\n\\n struct ExecuteBorrowParams {\\n address asset;\\n address user;\\n address onBehalfOf;\\n uint256 amount;\\n InterestRateMode interestRateMode;\\n uint16 referralCode;\\n bool releaseUnderlying;\\n uint256 maxStableRateBorrowSizePercent;\\n uint256 reservesCount;\\n address oracle;\\n uint8 userEModeCategory;\\n address priceOracleSentinel;\\n }\\n\\n struct ExecuteRepayParams {\\n address asset;\\n uint256 amount;\\n InterestRateMode interestRateMode;\\n address onBehalfOf;\\n bool useATokens;\\n }\\n\\n struct ExecuteWithdrawParams {\\n address asset;\\n uint256 amount;\\n address to;\\n uint256 reservesCount;\\n address oracle;\\n uint8 userEModeCategory;\\n }\\n\\n struct ExecuteSetUserEModeParams {\\n uint256 reservesCount;\\n address oracle;\\n uint8 categoryId;\\n }\\n\\n struct FinalizeTransferParams {\\n address asset;\\n address from;\\n address to;\\n uint256 amount;\\n uint256 balanceFromBefore;\\n uint256 balanceToBefore;\\n uint256 reservesCount;\\n address oracle;\\n uint8 fromEModeCategory;\\n }\\n\\n struct FlashloanParams {\\n address receiverAddress;\\n address[] assets;\\n uint256[] amounts;\\n uint256[] interestRateModes;\\n address onBehalfOf;\\n bytes params;\\n uint16 referralCode;\\n uint256 flashLoanPremiumToProtocol;\\n uint256 flashLoanPremiumTotal;\\n uint256 maxStableRateBorrowSizePercent;\\n uint256 reservesCount;\\n address addressesProvider;\\n uint8 userEModeCategory;\\n bool isAuthorizedFlashBorrower;\\n }\\n\\n struct FlashloanSimpleParams {\\n address receiverAddress;\\n address asset;\\n uint256 amount;\\n bytes params;\\n uint16 referralCode;\\n uint256 flashLoanPremiumToProtocol;\\n uint256 flashLoanPremiumTotal;\\n }\\n\\n struct FlashLoanRepaymentParams {\\n uint256 amount;\\n uint256 totalPremium;\\n uint256 flashLoanPremiumToProtocol;\\n address asset;\\n address receiverAddress;\\n uint16 referralCode;\\n }\\n\\n struct CalculateUserAccountDataParams {\\n UserConfigurationMap userConfig;\\n uint256 reservesCount;\\n address user;\\n address oracle;\\n uint8 userEModeCategory;\\n }\\n\\n struct ValidateBorrowParams {\\n ReserveCache reserveCache;\\n UserConfigurationMap userConfig;\\n address asset;\\n address userAddress;\\n uint256 amount;\\n InterestRateMode interestRateMode;\\n uint256 maxStableLoanPercent;\\n uint256 reservesCount;\\n address oracle;\\n uint8 userEModeCategory;\\n address priceOracleSentinel;\\n bool isolationModeActive;\\n address isolationModeCollateralAddress;\\n uint256 isolationModeDebtCeiling;\\n }\\n\\n struct ValidateLiquidationCallParams {\\n ReserveCache debtReserveCache;\\n uint256 totalDebt;\\n uint256 healthFactor;\\n address priceOracleSentinel;\\n }\\n\\n struct CalculateInterestRatesParams {\\n uint256 unbacked;\\n uint256 liquidityAdded;\\n uint256 liquidityTaken;\\n uint256 totalStableDebt;\\n uint256 totalVariableDebt;\\n uint256 averageStableBorrowRate;\\n uint256 reserveFactor;\\n address reserve;\\n address aToken;\\n }\\n\\n struct InitReserveParams {\\n address asset;\\n address aTokenAddress;\\n address stableDebtAddress;\\n address variableDebtAddress;\\n address interestRateStrategyAddress;\\n uint16 reservesCount;\\n uint16 maxNumberReserves;\\n }\\n}\\n\",\"keccak256\":\"0x771cb99fd8519c974f7e12130387c4d9a997a6e8d0ac10e4303b842fe53efa88\",\"license\":\"BUSL-1.1\"},\"@aave/core-v3/contracts/protocol/pool/DefaultReserveInterestRateStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.10;\\n\\nimport {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';\\nimport {WadRayMath} from '../libraries/math/WadRayMath.sol';\\nimport {PercentageMath} from '../libraries/math/PercentageMath.sol';\\nimport {DataTypes} from '../libraries/types/DataTypes.sol';\\nimport {Errors} from '../libraries/helpers/Errors.sol';\\nimport {IDefaultInterestRateStrategy} from '../../interfaces/IDefaultInterestRateStrategy.sol';\\nimport {IReserveInterestRateStrategy} from '../../interfaces/IReserveInterestRateStrategy.sol';\\nimport {IPoolAddressesProvider} from '../../interfaces/IPoolAddressesProvider.sol';\\n\\n/**\\n * @title DefaultReserveInterestRateStrategy contract\\n * @author Aave\\n * @notice Implements the calculation of the interest rates depending on the reserve state\\n * @dev The model of interest rate is based on 2 slopes, one before the `OPTIMAL_USAGE_RATIO`\\n * point of usage and another from that one to 100%.\\n * - An instance of this same contract, can't be used across different Aave markets, due to the caching\\n * of the PoolAddressesProvider\\n */\\ncontract DefaultReserveInterestRateStrategy is IDefaultInterestRateStrategy {\\n using WadRayMath for uint256;\\n using PercentageMath for uint256;\\n\\n /// @inheritdoc IDefaultInterestRateStrategy\\n uint256 public immutable OPTIMAL_USAGE_RATIO;\\n\\n /// @inheritdoc IDefaultInterestRateStrategy\\n uint256 public immutable OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO;\\n\\n /// @inheritdoc IDefaultInterestRateStrategy\\n uint256 public immutable MAX_EXCESS_USAGE_RATIO;\\n\\n /// @inheritdoc IDefaultInterestRateStrategy\\n uint256 public immutable MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO;\\n\\n IPoolAddressesProvider public immutable ADDRESSES_PROVIDER;\\n\\n // Base variable borrow rate when usage rate = 0. Expressed in ray\\n uint256 internal immutable _baseVariableBorrowRate;\\n\\n // Slope of the variable interest curve when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO. Expressed in ray\\n uint256 internal immutable _variableRateSlope1;\\n\\n // Slope of the variable interest curve when usage ratio > OPTIMAL_USAGE_RATIO. Expressed in ray\\n uint256 internal immutable _variableRateSlope2;\\n\\n // Slope of the stable interest curve when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO. Expressed in ray\\n uint256 internal immutable _stableRateSlope1;\\n\\n // Slope of the stable interest curve when usage ratio > OPTIMAL_USAGE_RATIO. Expressed in ray\\n uint256 internal immutable _stableRateSlope2;\\n\\n // Premium on top of `_variableRateSlope1` for base stable borrowing rate\\n uint256 internal immutable _baseStableRateOffset;\\n\\n // Additional premium applied to stable rate when stable debt surpass `OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO`\\n uint256 internal immutable _stableRateExcessOffset;\\n\\n /**\\n * @dev Constructor.\\n * @param provider The address of the PoolAddressesProvider contract\\n * @param optimalUsageRatio The optimal usage ratio\\n * @param baseVariableBorrowRate The base variable borrow rate\\n * @param variableRateSlope1 The variable rate slope below optimal usage ratio\\n * @param variableRateSlope2 The variable rate slope above optimal usage ratio\\n * @param stableRateSlope1 The stable rate slope below optimal usage ratio\\n * @param stableRateSlope2 The stable rate slope above optimal usage ratio\\n * @param baseStableRateOffset The premium on top of variable rate for base stable borrowing rate\\n * @param stableRateExcessOffset The premium on top of stable rate when there stable debt surpass the threshold\\n * @param optimalStableToTotalDebtRatio The optimal stable debt to total debt ratio of the reserve\\n */\\n constructor(\\n IPoolAddressesProvider provider,\\n uint256 optimalUsageRatio,\\n uint256 baseVariableBorrowRate,\\n uint256 variableRateSlope1,\\n uint256 variableRateSlope2,\\n uint256 stableRateSlope1,\\n uint256 stableRateSlope2,\\n uint256 baseStableRateOffset,\\n uint256 stableRateExcessOffset,\\n uint256 optimalStableToTotalDebtRatio\\n ) {\\n require(WadRayMath.RAY >= optimalUsageRatio, Errors.INVALID_OPTIMAL_USAGE_RATIO);\\n require(\\n WadRayMath.RAY >= optimalStableToTotalDebtRatio,\\n Errors.INVALID_OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO\\n );\\n OPTIMAL_USAGE_RATIO = optimalUsageRatio;\\n MAX_EXCESS_USAGE_RATIO = WadRayMath.RAY - optimalUsageRatio;\\n OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO = optimalStableToTotalDebtRatio;\\n MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO = WadRayMath.RAY - optimalStableToTotalDebtRatio;\\n ADDRESSES_PROVIDER = provider;\\n _baseVariableBorrowRate = baseVariableBorrowRate;\\n _variableRateSlope1 = variableRateSlope1;\\n _variableRateSlope2 = variableRateSlope2;\\n _stableRateSlope1 = stableRateSlope1;\\n _stableRateSlope2 = stableRateSlope2;\\n _baseStableRateOffset = baseStableRateOffset;\\n _stableRateExcessOffset = stableRateExcessOffset;\\n }\\n\\n /// @inheritdoc IDefaultInterestRateStrategy\\n function getVariableRateSlope1() external view returns (uint256) {\\n return _variableRateSlope1;\\n }\\n\\n /// @inheritdoc IDefaultInterestRateStrategy\\n function getVariableRateSlope2() external view returns (uint256) {\\n return _variableRateSlope2;\\n }\\n\\n /// @inheritdoc IDefaultInterestRateStrategy\\n function getStableRateSlope1() external view returns (uint256) {\\n return _stableRateSlope1;\\n }\\n\\n /// @inheritdoc IDefaultInterestRateStrategy\\n function getStableRateSlope2() external view returns (uint256) {\\n return _stableRateSlope2;\\n }\\n\\n /// @inheritdoc IDefaultInterestRateStrategy\\n function getStableRateExcessOffset() external view returns (uint256) {\\n return _stableRateExcessOffset;\\n }\\n\\n /// @inheritdoc IDefaultInterestRateStrategy\\n function getBaseStableBorrowRate() public view returns (uint256) {\\n return _variableRateSlope1 + _baseStableRateOffset;\\n }\\n\\n /// @inheritdoc IDefaultInterestRateStrategy\\n function getBaseVariableBorrowRate() external view override returns (uint256) {\\n return _baseVariableBorrowRate;\\n }\\n\\n /// @inheritdoc IDefaultInterestRateStrategy\\n function getMaxVariableBorrowRate() external view override returns (uint256) {\\n return _baseVariableBorrowRate + _variableRateSlope1 + _variableRateSlope2;\\n }\\n\\n struct CalcInterestRatesLocalVars {\\n uint256 availableLiquidity;\\n uint256 totalDebt;\\n uint256 currentVariableBorrowRate;\\n uint256 currentStableBorrowRate;\\n uint256 currentLiquidityRate;\\n uint256 borrowUsageRatio;\\n uint256 supplyUsageRatio;\\n uint256 stableToTotalDebtRatio;\\n uint256 availableLiquidityPlusDebt;\\n }\\n\\n /// @inheritdoc IReserveInterestRateStrategy\\n function calculateInterestRates(\\n DataTypes.CalculateInterestRatesParams memory params\\n ) public view override returns (uint256, uint256, uint256) {\\n CalcInterestRatesLocalVars memory vars;\\n\\n vars.totalDebt = params.totalStableDebt + params.totalVariableDebt;\\n\\n vars.currentLiquidityRate = 0;\\n vars.currentVariableBorrowRate = _baseVariableBorrowRate;\\n vars.currentStableBorrowRate = getBaseStableBorrowRate();\\n\\n if (vars.totalDebt != 0) {\\n vars.stableToTotalDebtRatio = params.totalStableDebt.rayDiv(vars.totalDebt);\\n vars.availableLiquidity =\\n IERC20(params.reserve).balanceOf(params.aToken) +\\n params.liquidityAdded -\\n params.liquidityTaken;\\n\\n vars.availableLiquidityPlusDebt = vars.availableLiquidity + vars.totalDebt;\\n vars.borrowUsageRatio = vars.totalDebt.rayDiv(vars.availableLiquidityPlusDebt);\\n vars.supplyUsageRatio = vars.totalDebt.rayDiv(\\n vars.availableLiquidityPlusDebt + params.unbacked\\n );\\n }\\n\\n if (vars.borrowUsageRatio > OPTIMAL_USAGE_RATIO) {\\n uint256 excessBorrowUsageRatio = (vars.borrowUsageRatio - OPTIMAL_USAGE_RATIO).rayDiv(\\n MAX_EXCESS_USAGE_RATIO\\n );\\n\\n vars.currentStableBorrowRate +=\\n _stableRateSlope1 +\\n _stableRateSlope2.rayMul(excessBorrowUsageRatio);\\n\\n vars.currentVariableBorrowRate +=\\n _variableRateSlope1 +\\n _variableRateSlope2.rayMul(excessBorrowUsageRatio);\\n } else {\\n vars.currentStableBorrowRate += _stableRateSlope1.rayMul(vars.borrowUsageRatio).rayDiv(\\n OPTIMAL_USAGE_RATIO\\n );\\n\\n vars.currentVariableBorrowRate += _variableRateSlope1.rayMul(vars.borrowUsageRatio).rayDiv(\\n OPTIMAL_USAGE_RATIO\\n );\\n }\\n\\n if (vars.stableToTotalDebtRatio > OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO) {\\n uint256 excessStableDebtRatio = (vars.stableToTotalDebtRatio -\\n OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO).rayDiv(MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO);\\n vars.currentStableBorrowRate += _stableRateExcessOffset.rayMul(excessStableDebtRatio);\\n }\\n\\n vars.currentLiquidityRate = _getOverallBorrowRate(\\n params.totalStableDebt,\\n params.totalVariableDebt,\\n vars.currentVariableBorrowRate,\\n params.averageStableBorrowRate\\n ).rayMul(vars.supplyUsageRatio).percentMul(\\n PercentageMath.PERCENTAGE_FACTOR - params.reserveFactor\\n );\\n\\n return (\\n vars.currentLiquidityRate,\\n vars.currentStableBorrowRate,\\n vars.currentVariableBorrowRate\\n );\\n }\\n\\n /**\\n * @dev Calculates the overall borrow rate as the weighted average between the total variable debt and total stable\\n * debt\\n * @param totalStableDebt The total borrowed from the reserve at a stable rate\\n * @param totalVariableDebt The total borrowed from the reserve at a variable rate\\n * @param currentVariableBorrowRate The current variable borrow rate of the reserve\\n * @param currentAverageStableBorrowRate The current weighted average of all the stable rate loans\\n * @return The weighted averaged borrow rate\\n */\\n function _getOverallBorrowRate(\\n uint256 totalStableDebt,\\n uint256 totalVariableDebt,\\n uint256 currentVariableBorrowRate,\\n uint256 currentAverageStableBorrowRate\\n ) internal pure returns (uint256) {\\n uint256 totalDebt = totalStableDebt + totalVariableDebt;\\n\\n if (totalDebt == 0) return 0;\\n\\n uint256 weightedVariableRate = totalVariableDebt.wadToRay().rayMul(currentVariableBorrowRate);\\n\\n uint256 weightedStableRate = totalStableDebt.wadToRay().rayMul(currentAverageStableBorrowRate);\\n\\n uint256 overallBorrowRate = (weightedVariableRate + weightedStableRate).rayDiv(\\n totalDebt.wadToRay()\\n );\\n\\n return overallBorrowRate;\\n }\\n}\\n\",\"keccak256\":\"0x01d746c72a9ace142997f4f66226b3a0005fbdc7b0e828915b837e156c4f520a\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x61020060405234801561001157600080fd5b5060405162000f7538038062000f7583398101604081905261003291610146565b886b033b2e3c9fd0803ce8000000101560405180604001604052806002815260200161383360f01b815250906100845760405162461bcd60e51b815260040161007b91906101d1565b60405180910390fd5b50806b033b2e3c9fd0803ce80000001015604051806040016040528060028152602001610e0d60f21b815250906100ce5760405162461bcd60e51b815260040161007b91906101d1565b5060808990526100ea896b033b2e3c9fd0803ce8000000610226565b60c05260a0819052610108816b033b2e3c9fd0803ce8000000610226565b60e052506001600160a01b0390981661010052610120959095526101409390935261016091909152610180526101a0526101c052506101e05261024b565b6000806000806000806000806000806101408b8d03121561016657600080fd5b8a516001600160a01b038116811461017d57600080fd5b809a505060208b0151985060408b0151975060608b0151965060808b0151955060a08b0151945060c08b0151935060e08b015192506101008b015191506101208b015190509295989b9194979a5092959850565b600060208083528351808285015260005b818110156101fe578581018301518582016040015282016101e2565b81811115610210576000604083870101525b50601f01601f1916929092016040019392505050565b60008282101561024657634e487b7160e01b600052601160045260246000fd5b500390565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e051610c0d62000368600039600081816102710152610821015260006108c601526000818161017201526105ec0152600081816102970152818161061701526106ec0152600081816102bd0152818161030c0152610654015260008181610142015281816103300152818161067f0152818161075e01526108e70152600081816101980152818161035101526103fa0152600060f40152600081816102e601526107cb01526000818161024501526105900152600081816101e80152818161079a01526107ec0152600081816101c10152818161055f015281816105b1015281816106c301526107380152610c0d6000f3fe608060405234801561001057600080fd5b50600436106100ea5760003560e01c8063a58987091161008c578063bc62690811610066578063bc6269081461026f578063d5cd739114610295578063f4202409146102bb578063fe5fd698146102e157600080fd5b8063a589870914610212578063a9c622f814610240578063acd786861461026757600080fd5b806334762ca5116100c857806334762ca51461019657806354c365c6146101bc5780636fb92589146101e357806380031e371461020a57600080fd5b80630542975c146100ef5780630b3429a21461014057806314e32da414610170575b600080fd5b6101167f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b7f00000000000000000000000000000000000000000000000000000000000000005b604051908152602001610137565b7f0000000000000000000000000000000000000000000000000000000000000000610162565b7f0000000000000000000000000000000000000000000000000000000000000000610162565b6101627f000000000000000000000000000000000000000000000000000000000000000081565b6101627f000000000000000000000000000000000000000000000000000000000000000081565b610162610308565b610225610220366004610adb565b610384565b60408051938452602084019290925290820152606001610137565b6101627f000000000000000000000000000000000000000000000000000000000000000081565b6101626108bf565b7f0000000000000000000000000000000000000000000000000000000000000000610162565b7f0000000000000000000000000000000000000000000000000000000000000000610162565b7f0000000000000000000000000000000000000000000000000000000000000000610162565b6101627f000000000000000000000000000000000000000000000000000000000000000081565b60007f00000000000000000000000000000000000000000000000000000000000000006103757f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610b8f565b61037f9190610b8f565b905090565b60008060006103d86040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b846080015185606001516103ec9190610b8f565b6020820152600060808201527f000000000000000000000000000000000000000000000000000000000000000060408201526104266108bf565b606082015260208101511561055d57602081015160608601516104489161090b565b60e08083019190915260408087015160208801519288015161010089015192517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff938416600482015291939216906370a0823190602401602060405180830381865afa1580156104d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104f79190610ba7565b6105019190610b8f565b61050b9190610bc0565b808252602082015161051c91610b8f565b610100820181905260208201516105329161090b565b60a082015284516101008201516105579161054c91610b8f565b60208301519061090b565b60c08201525b7f00000000000000000000000000000000000000000000000000000000000000008160a0015111156106be5760006105e57f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008460a001516105df9190610bc0565b9061090b565b90506106117f00000000000000000000000000000000000000000000000000000000000000008261094a565b61063b907f0000000000000000000000000000000000000000000000000000000000000000610b8f565b8260600181815161064c9190610b8f565b9052506106797f00000000000000000000000000000000000000000000000000000000000000008261094a565b6106a3907f0000000000000000000000000000000000000000000000000000000000000000610b8f565b826040018181516106b49190610b8f565b9052506107989050565b6107197f00000000000000000000000000000000000000000000000000000000000000006105df8360a001517f000000000000000000000000000000000000000000000000000000000000000061094a90919063ffffffff16565b8160600181815161072a9190610b8f565b90525060a0810151610783907f0000000000000000000000000000000000000000000000000000000000000000906105df907f00000000000000000000000000000000000000000000000000000000000000009061094a565b816040018181516107949190610b8f565b9052505b7f00000000000000000000000000000000000000000000000000000000000000008160e00151111561085c57600061081a7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008460e001516105df9190610bc0565b90506108467f00000000000000000000000000000000000000000000000000000000000000008261094a565b826060018181516108579190610b8f565b905250505b6108a18560c001516127106108719190610bc0565b61089b8360c0015161089589606001518a6080015187604001518c60a001516109a1565b9061094a565b90610a08565b60808201819052606082015160409092015190969195509350915050565b600061037f7f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610b8f565b600081156b033b2e3c9fd0803ce80000006002840419048411171561092f57600080fd5b506b033b2e3c9fd0803ce80000009190910260028204010490565b600081157ffffffffffffffffffffffffffffffffffffffffffe6268e1b017bfe18bffffff8390048411151761097f57600080fd5b506b033b2e3c9fd0803ce800000091026b019d971e4fe8401e74000000010490565b6000806109ae8587610b8f565b9050806109bf576000915050610a00565b60006109ce8561089588610a4b565b905060006109df856108958a610a4b565b905060006109f96109ef85610a4b565b6105df8486610b8f565b9450505050505b949350505050565b600081157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec7783900484111517610a3d57600080fd5b506127109102611388010490565b633b9aca008181029081048214610a6157600080fd5b919050565b604051610120810167ffffffffffffffff81118282101715610ab1577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405290565b803573ffffffffffffffffffffffffffffffffffffffff81168114610a6157600080fd5b60006101208284031215610aee57600080fd5b610af6610a66565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c0820152610b4260e08401610ab7565b60e0820152610100610b55818501610ab7565b908201529392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115610ba257610ba2610b60565b500190565b600060208284031215610bb957600080fd5b5051919050565b600082821015610bd257610bd2610b60565b50039056fea26469706673582212202429128531aba381237b8b5630526b46e16fae03dbb463f5a83786873419101a64736f6c634300080a0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100ea5760003560e01c8063a58987091161008c578063bc62690811610066578063bc6269081461026f578063d5cd739114610295578063f4202409146102bb578063fe5fd698146102e157600080fd5b8063a589870914610212578063a9c622f814610240578063acd786861461026757600080fd5b806334762ca5116100c857806334762ca51461019657806354c365c6146101bc5780636fb92589146101e357806380031e371461020a57600080fd5b80630542975c146100ef5780630b3429a21461014057806314e32da414610170575b600080fd5b6101167f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b7f00000000000000000000000000000000000000000000000000000000000000005b604051908152602001610137565b7f0000000000000000000000000000000000000000000000000000000000000000610162565b7f0000000000000000000000000000000000000000000000000000000000000000610162565b6101627f000000000000000000000000000000000000000000000000000000000000000081565b6101627f000000000000000000000000000000000000000000000000000000000000000081565b610162610308565b610225610220366004610adb565b610384565b60408051938452602084019290925290820152606001610137565b6101627f000000000000000000000000000000000000000000000000000000000000000081565b6101626108bf565b7f0000000000000000000000000000000000000000000000000000000000000000610162565b7f0000000000000000000000000000000000000000000000000000000000000000610162565b7f0000000000000000000000000000000000000000000000000000000000000000610162565b6101627f000000000000000000000000000000000000000000000000000000000000000081565b60007f00000000000000000000000000000000000000000000000000000000000000006103757f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610b8f565b61037f9190610b8f565b905090565b60008060006103d86040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b846080015185606001516103ec9190610b8f565b6020820152600060808201527f000000000000000000000000000000000000000000000000000000000000000060408201526104266108bf565b606082015260208101511561055d57602081015160608601516104489161090b565b60e08083019190915260408087015160208801519288015161010089015192517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff938416600482015291939216906370a0823190602401602060405180830381865afa1580156104d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104f79190610ba7565b6105019190610b8f565b61050b9190610bc0565b808252602082015161051c91610b8f565b610100820181905260208201516105329161090b565b60a082015284516101008201516105579161054c91610b8f565b60208301519061090b565b60c08201525b7f00000000000000000000000000000000000000000000000000000000000000008160a0015111156106be5760006105e57f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008460a001516105df9190610bc0565b9061090b565b90506106117f00000000000000000000000000000000000000000000000000000000000000008261094a565b61063b907f0000000000000000000000000000000000000000000000000000000000000000610b8f565b8260600181815161064c9190610b8f565b9052506106797f00000000000000000000000000000000000000000000000000000000000000008261094a565b6106a3907f0000000000000000000000000000000000000000000000000000000000000000610b8f565b826040018181516106b49190610b8f565b9052506107989050565b6107197f00000000000000000000000000000000000000000000000000000000000000006105df8360a001517f000000000000000000000000000000000000000000000000000000000000000061094a90919063ffffffff16565b8160600181815161072a9190610b8f565b90525060a0810151610783907f0000000000000000000000000000000000000000000000000000000000000000906105df907f00000000000000000000000000000000000000000000000000000000000000009061094a565b816040018181516107949190610b8f565b9052505b7f00000000000000000000000000000000000000000000000000000000000000008160e00151111561085c57600061081a7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008460e001516105df9190610bc0565b90506108467f00000000000000000000000000000000000000000000000000000000000000008261094a565b826060018181516108579190610b8f565b905250505b6108a18560c001516127106108719190610bc0565b61089b8360c0015161089589606001518a6080015187604001518c60a001516109a1565b9061094a565b90610a08565b60808201819052606082015160409092015190969195509350915050565b600061037f7f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610b8f565b600081156b033b2e3c9fd0803ce80000006002840419048411171561092f57600080fd5b506b033b2e3c9fd0803ce80000009190910260028204010490565b600081157ffffffffffffffffffffffffffffffffffffffffffe6268e1b017bfe18bffffff8390048411151761097f57600080fd5b506b033b2e3c9fd0803ce800000091026b019d971e4fe8401e74000000010490565b6000806109ae8587610b8f565b9050806109bf576000915050610a00565b60006109ce8561089588610a4b565b905060006109df856108958a610a4b565b905060006109f96109ef85610a4b565b6105df8486610b8f565b9450505050505b949350505050565b600081157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec7783900484111517610a3d57600080fd5b506127109102611388010490565b633b9aca008181029081048214610a6157600080fd5b919050565b604051610120810167ffffffffffffffff81118282101715610ab1577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405290565b803573ffffffffffffffffffffffffffffffffffffffff81168114610a6157600080fd5b60006101208284031215610aee57600080fd5b610af6610a66565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c0820152610b4260e08401610ab7565b60e0820152610100610b55818501610ab7565b908201529392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115610ba257610ba2610b60565b500190565b600060208284031215610bb957600080fd5b5051919050565b600082821015610bd257610bd2610b60565b50039056fea26469706673582212202429128531aba381237b8b5630526b46e16fae03dbb463f5a83786873419101a64736f6c634300080a0033", + "devdoc": { + "author": "Aave", + "details": "The model of interest rate is based on 2 slopes, one before the `OPTIMAL_USAGE_RATIO` point of usage and another from that one to 100%. - An instance of this same contract, can't be used across different Aave markets, due to the caching of the PoolAddressesProvider", + "kind": "dev", + "methods": { + "calculateInterestRates((uint256,uint256,uint256,uint256,uint256,uint256,uint256,address,address))": { + "params": { + "params": "The parameters needed to calculate interest rates" + }, + "returns": { + "_0": "liquidityRate The liquidity rate expressed in rays", + "_1": "stableBorrowRate The stable borrow rate expressed in rays", + "_2": "variableBorrowRate The variable borrow rate expressed in rays" + } + }, + "constructor": { + "details": "Constructor.", + "params": { + "baseStableRateOffset": "The premium on top of variable rate for base stable borrowing rate", + "baseVariableBorrowRate": "The base variable borrow rate", + "optimalStableToTotalDebtRatio": "The optimal stable debt to total debt ratio of the reserve", + "optimalUsageRatio": "The optimal usage ratio", + "provider": "The address of the PoolAddressesProvider contract", + "stableRateExcessOffset": "The premium on top of stable rate when there stable debt surpass the threshold", + "stableRateSlope1": "The stable rate slope below optimal usage ratio", + "stableRateSlope2": "The stable rate slope above optimal usage ratio", + "variableRateSlope1": "The variable rate slope below optimal usage ratio", + "variableRateSlope2": "The variable rate slope above optimal usage ratio" + } + }, + "getBaseStableBorrowRate()": { + "returns": { + "_0": "The base stable borrow rate, expressed in ray" + } + }, + "getBaseVariableBorrowRate()": { + "returns": { + "_0": "The base variable borrow rate, expressed in ray" + } + }, + "getMaxVariableBorrowRate()": { + "returns": { + "_0": "The maximum variable borrow rate, expressed in ray" + } + }, + "getStableRateExcessOffset()": { + "details": "It's an additional premium applied to the stable when stable debt > OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO", + "returns": { + "_0": "The stable rate excess offset, expressed in ray" + } + }, + "getStableRateSlope1()": { + "details": "It's the stable rate when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO", + "returns": { + "_0": "The stable rate slope, expressed in ray" + } + }, + "getStableRateSlope2()": { + "details": "It's the variable rate when usage ratio > OPTIMAL_USAGE_RATIO", + "returns": { + "_0": "The stable rate slope, expressed in ray" + } + }, + "getVariableRateSlope1()": { + "details": "It's the variable rate when usage ratio > 0 and <= OPTIMAL_USAGE_RATIO", + "returns": { + "_0": "The variable rate slope, expressed in ray" + } + }, + "getVariableRateSlope2()": { + "details": "It's the variable rate when usage ratio > OPTIMAL_USAGE_RATIO", + "returns": { + "_0": "The variable rate slope, expressed in ray" + } + } + }, + "stateVariables": { + "ADDRESSES_PROVIDER": { + "return": "The address of the PoolAddressesProvider contract", + "returns": { + "_0": "The address of the PoolAddressesProvider contract" + } + }, + "MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO": { + "details": "It's always equal to 1-optimal stable to total debt ratio (added as constant for gas optimizations)", + "return": "The max excess stable to total debt ratio, expressed in ray.", + "returns": { + "_0": "The max excess stable to total debt ratio, expressed in ray." + } + }, + "MAX_EXCESS_USAGE_RATIO": { + "details": "It's always equal to 1-optimal usage ratio (added as constant for gas optimizations)", + "return": "The max excess usage ratio, expressed in ray.", + "returns": { + "_0": "The max excess usage ratio, expressed in ray." + } + }, + "OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO": { + "return": "The optimal stable to total debt ratio, expressed in ray.", + "returns": { + "_0": "The optimal stable to total debt ratio, expressed in ray." + } + }, + "OPTIMAL_USAGE_RATIO": { + "return": "The optimal usage ratio, expressed in ray.", + "returns": { + "_0": "The optimal usage ratio, expressed in ray." + } + } + }, + "title": "DefaultReserveInterestRateStrategy contract", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "ADDRESSES_PROVIDER()": { + "notice": "Returns the address of the PoolAddressesProvider" + }, + "MAX_EXCESS_STABLE_TO_TOTAL_DEBT_RATIO()": { + "notice": "Returns the excess stable debt ratio above the optimal." + }, + "MAX_EXCESS_USAGE_RATIO()": { + "notice": "Returns the excess usage ratio above the optimal." + }, + "OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO()": { + "notice": "Returns the optimal stable to total debt ratio of the reserve." + }, + "OPTIMAL_USAGE_RATIO()": { + "notice": "Returns the usage ratio at which the pool aims to obtain most competitive borrow rates." + }, + "calculateInterestRates((uint256,uint256,uint256,uint256,uint256,uint256,uint256,address,address))": { + "notice": "Calculates the interest rates depending on the reserve's state and configurations" + }, + "getBaseStableBorrowRate()": { + "notice": "Returns the base stable borrow rate" + }, + "getBaseVariableBorrowRate()": { + "notice": "Returns the base variable borrow rate" + }, + "getMaxVariableBorrowRate()": { + "notice": "Returns the maximum variable borrow rate" + }, + "getStableRateExcessOffset()": { + "notice": "Returns the stable rate excess offset" + }, + "getStableRateSlope1()": { + "notice": "Returns the stable rate slope below optimal usage ratio" + }, + "getStableRateSlope2()": { + "notice": "Returns the stable rate slope above optimal usage ratio" + }, + "getVariableRateSlope1()": { + "notice": "Returns the variable rate slope below optimal usage ratio" + }, + "getVariableRateSlope2()": { + "notice": "Returns the variable rate slope above optimal usage ratio" + } + }, + "notice": "Implements the calculation of the interest rates depending on the reserve state", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} diff --git a/apps/frontend/src/hooks/aave/constants.ts b/apps/frontend/src/hooks/aave/constants.ts new file mode 100644 index 000000000..71536a314 --- /dev/null +++ b/apps/frontend/src/hooks/aave/constants.ts @@ -0,0 +1 @@ +export const BIG_NUMBER_PRECISION_TWENTY_SEVEN = 27; diff --git a/apps/frontend/src/hooks/aave/useAaveRates.tsx b/apps/frontend/src/hooks/aave/useAaveRates.tsx new file mode 100644 index 000000000..d339c08e6 --- /dev/null +++ b/apps/frontend/src/hooks/aave/useAaveRates.tsx @@ -0,0 +1,149 @@ +import { useMemo, useState } from 'react'; + +import { BigNumber, Contract } from 'ethers'; +import { formatUnits } from 'ethers/lib/utils'; +import { useSearchParams } from 'react-router-dom'; + +import { useAccount } from '../useAccount'; +import rateStrategy from './ReserveStrategy-rateStrategyStableOne.json'; +import { BIG_NUMBER_PRECISION_TWENTY_SEVEN } from './constants'; +import { useAaveReservesData } from './useAaveReservesData'; + +export interface IRatesDataResult { + currentUsageRatio: string; + baseStableBorrowRate: string; + optimalUsageRatio: string; + baseVariableBorrowRate: string; + variableRateSlope1: string; + variableRateSlope2: string; + stableRateSlope1: string; + stableRateSlope2: string; + //baseStableRateOffset: string; + stableRateExcessOffset: string; + optimalStableToTotalDebtRatio: string; + underlyingAsset: string; + name: string; + symbol: string; + decimals: string; +} + +function calculateUtilizationRate( + totalDebt: string, + availableLiquidity: string, +): string { + // Convert inputs to BigNumber + const _totalDebt: BigNumber = BigNumber.from(totalDebt); + const _liquidity: BigNumber = BigNumber.from(availableLiquidity); + + return _totalDebt.div(_totalDebt.add(_liquidity)).toString(); +} + +export const useAaveInterestRatesData = (): { + data: IRatesDataResult | null; + error: string | null; + loading: boolean; +} => { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const { provider } = useAccount(); + + // const { provider } = useAccount(); + const [searchParams] = useSearchParams(); + const symbol = searchParams.get('asset') || ''; + const { reserves } = useAaveReservesData(); + const reserveAsset = reserves.find( + r => r.symbol.toLocaleLowerCase() === symbol.toLocaleLowerCase(), + ); + const interestRateStrategyAddress = reserveAsset?.interestRateStrategyAddress; + console.log('reserves', reserves); + console.log('reserveAsset', reserveAsset); + console.log('interestRateStrategyAddress', interestRateStrategyAddress); + + useMemo(() => { + const fetchData = async () => { + try { + if (!interestRateStrategyAddress) { + console.log( + 'Interest Rate Strategy Address not found', + reserveAsset, + reserveAsset?.interestRateStrategyAddress, + ); + setError('Interest Rate Strategy Address not found'); + return; + } + const ratesStrategy = new Contract( + interestRateStrategyAddress as string, + rateStrategy.abi, + provider, + ); + const utilizationRate = calculateUtilizationRate( + reserveAsset.totalDebt, + reserveAsset.availableLiquidity, + ); + console.log('utilizationRate', utilizationRate); + + const [stableRateExcessOffset, optimalStableToTotalDebtRatio] = + await Promise.all([ + ratesStrategy.getStableRateExcessOffset(), + ratesStrategy.OPTIMAL_STABLE_TO_TOTAL_DEBT_RATIO(), + ]); + + const ratesData = { + currentUsageRatio: formatUnits(utilizationRate, 2).toString(), + optimalUsageRatio: formatUnits( + reserveAsset.optimalUsageRatio, + BIG_NUMBER_PRECISION_TWENTY_SEVEN, + ).toString(), + baseVariableBorrowRate: formatUnits( + reserveAsset.baseVariableBorrowRate, + BIG_NUMBER_PRECISION_TWENTY_SEVEN, + ).toString(), + variableRateSlope1: formatUnits( + reserveAsset.variableRateSlope1, + BIG_NUMBER_PRECISION_TWENTY_SEVEN, + ).toString(), + variableRateSlope2: formatUnits( + reserveAsset.variableRateSlope2, + BIG_NUMBER_PRECISION_TWENTY_SEVEN, + ).toString(), + stableRateSlope1: formatUnits( + reserveAsset.stableRateSlope1, + BIG_NUMBER_PRECISION_TWENTY_SEVEN, + ).toString(), + stableRateSlope2: formatUnits( + reserveAsset.stableRateSlope2, + BIG_NUMBER_PRECISION_TWENTY_SEVEN, + ).toString(), + baseStableBorrowRate: formatUnits( + reserveAsset.baseStableBorrowRate, + BIG_NUMBER_PRECISION_TWENTY_SEVEN, + ).toString(), + stableRateExcessOffset: formatUnits( + stableRateExcessOffset, + BIG_NUMBER_PRECISION_TWENTY_SEVEN, + ).toString(), + optimalStableToTotalDebtRatio: formatUnits( + optimalStableToTotalDebtRatio, + BIG_NUMBER_PRECISION_TWENTY_SEVEN, + ).toString(), + underlyingAsset: reserveAsset.underlyingAsset.toString(), + name: reserveAsset.name.toString(), + symbol: reserveAsset.symbol.toString(), + decimals: reserveAsset.decimals.toString(), + }; + setData(ratesData); + } catch (error) { + setError(error.message); + } finally { + setLoading(false); + } + + return data; + }; + fetchData(); + }, [data, interestRateStrategyAddress, provider, reserveAsset]); + + return { data, loading, error }; +}; diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index cc44fef38..ba97e0049 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -945,7 +945,13 @@ "reserveSize": "Reserve size", "availableLiquidity": "Available liquidity", "utilizationRate": "Utilization rate", - "oraclePrice": "Oracle price" + "oraclePrice": "Oracle price", + "underlyingToken": "Underlying token", + "aToken": "aToken", + "variableDebtToken": "Variable debt token", + "variableDebtTokenName": "Variable debt {{symbol}}", + "stableDebtToken": "Stable debt token", + "stableDebtTokenName": "Stable debt {{symbol}}" }, "reserveStatusTab": { "title": "Reserve status", From d8842457e3e0208626453b31152fb2c40e7928c4 Mon Sep 17 00:00:00 2001 From: Matias Zapata <42716817+matzapata@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:58:31 +0300 Subject: [PATCH 077/116] Saf 20/pool details integration sidebar (#41) * cleanup * fixes * fixes. Connect provider. Fix repay adding proper allowance. Fix switch collateral token.address instead of token * refetch user data based on blocknumber * fix modal closing on lending positions change plus style adjustments * fix withdraw modals * modal fixes * fix calculation add balances and filter zero balances * use memo items * fix dependencies * data normalizers * Apply suggestions from code review Co-authored-by: Juan * Update apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx Co-authored-by: Juan * Update apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx Co-authored-by: Juan * wip refactor done. Missing hooks merge * implementations and cleanup * cleanup * cleanup * math safety * more math safety * cleanup * clenaup * emode mocks * enable emode working * emode working * fixes and cleanup * remove whitelist * toggle lend mode working * fixes and cleanup * add collateral check * fix collateral * fixes * Fix. Missing fix on borrow. * fix borrow power to match desired collateral ratio * rollback to working contracts for borrow eth * fixes * fixes and max amount * loading when account changes * close emode on complete * disable emode if just one category * error handling * fixes. Connect provider. Fix repay adding proper allowance. Fix switch collateral token.address instead of token * refetch user data based on blocknumber * fix calculation add balances and filter zero balances * [SAF-20] wip testing rates data from contract Reserve Rate Strategy * [SAF-20] wip integrating useAaveRates hook * [SAF-20] fix hook useAaveRates * connect topbar * tokens links * add link to oracle * [SAF_20] fix: parse totalDebt using decimals * sidebar integration * Connect topbar, add token links and add to wallet (#37) * connect topbar * tokens links * add link to oracle * fix merge error --------- Co-authored-by: Juan Co-authored-by: Luciano Perez Cerra --- .../AaveReserveOverviewPage.tsx | 7 +- .../InterestRateModelGraph.tsx | 4 +- .../WalletOverview/WalletOverview.tsx | 101 +++++++++--------- .../components/BorrowAction/BorrowAction.tsx | 50 +++++++-- .../components/SupplyAction/SupplyAction.tsx | 50 +++++++-- .../frontend/src/locales/en/translations.json | 4 +- .../src/utils/aave/AaveUserReservesSummary.ts | 2 + 7 files changed, 141 insertions(+), 77 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx index 55f11a7b2..6b43554d9 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx @@ -121,7 +121,10 @@ const AaveReserveOverviewPage: FC = () => { {interestRatesData && ( - + )}
@@ -132,7 +135,7 @@ const AaveReserveOverviewPage: FC = () => { 'lg:block space-y-4 w-[450px] shrink-0', )} > - +
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx index db244de56..d850c613d 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx @@ -16,10 +16,12 @@ const pageTranslations = translations.aaveReserveOverviewPage.interestRateModel; type InterestRateModelGraphProps = { rates: IRatesDataResult; + reserveFactor: string | undefined; }; export const InterestRateModelGraph: FC = ({ rates, + reserveFactor, }) => { const [open, setOpen] = useState(true); const { isMobile } = useIsMobile(); @@ -64,7 +66,7 @@ export const InterestRateModelGraph: FC = ({ } + value={} /> = ({ - asset: initialAsset, -}) => { - const [asset, setAsset] = useState(initialAsset.symbol); +export const WalletOverview: FC = ({ symbol }) => { + const [asset, setAsset] = useState(); const { account, connectWallet, pending } = useWalletConnect(); + const { summary } = useAaveUserReservesData(); + + useEffect(() => { + getAssetData(symbol, BOB_CHAIN_ID).then(setAsset); + }, [symbol]); - const assetBalance = Decimal.from(0); // TODO: mocked - const maxSupplyCap = true; // TODO: mocked + const reserveSummary = useMemo(() => { + return summary.reserves.find(r => r.reserve.symbol === symbol); + }, [summary, symbol]); return (
@@ -50,47 +49,47 @@ export const WalletOverview: FC = ({ {account ? ( <>
- {ETH_ASSET_SYMBOLS.includes(asset) && ( - setAsset(ETH_ASSET_SYMBOLS[e])} - items={ETH_ASSET_SYMBOLS.map(s => ({ - activeClassName: 'text-primary-20', - dataAttribute: s, - label: s, - }))} - size={TabSize.normal} - type={TabType.secondary} + + {t(pageTranslations.yourWalletTab.walletBalance)} + +
+ - )} - -
- - {t(pageTranslations.yourWalletTab.walletBalance)} - -
- -
-
+
{/* Supply */} - + {/* Borrow */} - +
- {assetBalance.lte(0) && ( + {reserveSummary?.walletBalance.lte(0) && ( = ({ /> )} - {maxSupplyCap && ( + {Decimal.from(reserveSummary?.reserve.supplyUsageRatio ?? 0).gte( + 1, + ) && ( = ({ asset }) => { - const availableToBorrow = 0; // TODO: this is mocked - const availableToBorrowUsd = 0; +export const BorrowAction: FC = ({ + asset, + availableToBorrow, + availableToBorrowUsd, +}) => { + const [open, setOpen] = useState(false); - const isBorrowDisabled = useMemo(() => { - // TODO: add conditions - return Decimal.from(availableToBorrow).lte(0); - }, [availableToBorrow]); + const onBorrowClose = useCallback(() => { + setOpen(false); + }, []); + + const onBorrowOpen = useCallback(() => { + setOpen(true); + }, []); return (
-
+
= ({ asset }) => {
); }; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/components/SupplyAction/SupplyAction.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/components/SupplyAction/SupplyAction.tsx index 02f730782..44d2e78de 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/components/SupplyAction/SupplyAction.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/components/SupplyAction/SupplyAction.tsx @@ -1,31 +1,48 @@ -import React, { FC, useMemo } from 'react'; +import React, { FC, useCallback, useState } from 'react'; import { t } from 'i18next'; -import { Button, HelperButton, Paragraph, ParagraphSize } from '@sovryn/ui'; +import { + Button, + Dialog, + DialogBody, + DialogHeader, + HelperButton, + Paragraph, + ParagraphSize, +} from '@sovryn/ui'; import { Decimal } from '@sovryn/utils'; import { AssetAmountPriceRenderer } from '../../../../../../2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer'; import { translations } from '../../../../../../../locales/i18n'; +import { LendForm } from '../../../../../AavePage/components/LendAssetsList/components/LendForm/LendForm'; const pageTranslations = translations.aaveReserveOverviewPage; type SupplyActionProps = { asset: string; + availableToSupply: Decimal; + availableToSupplyUSD: Decimal; }; -export const SupplyAction: FC = ({ asset }) => { - const availableToSupply = 0; // TODO: this is mocked. - const availableToSupplyUSD = 0; // TODO: this is mocked. +export const SupplyAction: FC = ({ + asset, + availableToSupply, + availableToSupplyUSD, +}) => { + const [open, setOpen] = useState(false); - const isSupplyDisabled = useMemo(() => { - // TODO: add conditions - return Decimal.from(availableToSupply).lte(0); - }, [availableToSupply]); + const onSupplyClose = useCallback(() => { + setOpen(false); + }, []); + + const onSupplyOpen = useCallback(() => { + setOpen(true); + }, []); return (
-
+
{t(pageTranslations.yourWalletTab.availableToSupply)}{' '} @@ -44,9 +61,20 @@ export const SupplyAction: FC = ({ asset }) => {
); }; diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index ba97e0049..646b4d232 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -961,9 +961,9 @@ "title": "Your wallet", "walletBalance": "Wallet balance", "availableToSupply": "Available to supply", - "availableToSupplyInfo": "How much you can borrow of this token with this wallet", + "availableToSupplyInfo": "This is the total amount that you are able to supply to in this reserve. You are able to supply your wallet balance up until the supply cap is reached.", "availableToBorrow": "Available to borrow", - "availableToBorrowInfo": "How much you can borrow of this token with this wallet", + "availableToBorrowInfo": "This is the total amount available for you to borrow. You can borrow based on your collateral and until the borrow cap is reached.", "supply": "Supply", "borrow": "Borrow", "yourWalletIsEmpty": "Your wallet is empty. Purchase or transfer assets", diff --git a/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts b/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts index 9c51c2caa..eb7444296 100644 --- a/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts +++ b/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts @@ -40,6 +40,7 @@ export type ReserveSummary = { borrowedUSD: Decimal; borrowRateMode: BorrowRateMode; availableToBorrow: Decimal; + availableToBorrowUSD: Decimal; }; export type AaveUserReservesSummary = { @@ -99,6 +100,7 @@ export class AaveUserReservesSummaryFactory { borrowedUSD: Decimal.ZERO, borrowRateMode: BorrowRateMode.VARIABLE, availableToBorrow: Decimal.ZERO, + availableToBorrowUSD: Decimal.ZERO, })), }; } From 7b729323bdd353ab0c1c552c76b5c677c4a803b2 Mon Sep 17 00:00:00 2001 From: Matias Zapata <42716817+matzapata@users.noreply.github.com> Date: Fri, 6 Sep 2024 15:28:32 +0300 Subject: [PATCH 078/116] Saf 20/pool details integration graphs stats (#42) * cleanup * fixes * fixes. Connect provider. Fix repay adding proper allowance. Fix switch collateral token.address instead of token * refetch user data based on blocknumber * fix modal closing on lending positions change plus style adjustments * fix withdraw modals * modal fixes * fix calculation add balances and filter zero balances * use memo items * fix dependencies * data normalizers * Apply suggestions from code review Co-authored-by: Juan * Update apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetAction/LendAssetAction.tsx Co-authored-by: Juan * Update apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/LendPositionsList.constants.tsx Co-authored-by: Juan * wip refactor done. Missing hooks merge * implementations and cleanup * cleanup * cleanup * math safety * more math safety * cleanup * clenaup * emode mocks * enable emode working * emode working * fixes and cleanup * remove whitelist * toggle lend mode working * fixes and cleanup * add collateral check * fix collateral * fixes * Fix. Missing fix on borrow. * fix borrow power to match desired collateral ratio * rollback to working contracts for borrow eth * fixes * fixes and max amount * loading when account changes * close emode on complete * disable emode if just one category * error handling * fixes. Connect provider. Fix repay adding proper allowance. Fix switch collateral token.address instead of token * refetch user data based on blocknumber * fix calculation add balances and filter zero balances * [SAF-20] wip testing rates data from contract Reserve Rate Strategy * [SAF-20] wip integrating useAaveRates hook * [SAF-20] fix hook useAaveRates * [SAF_20] fix: parse totalDebt using decimals * Connect topbar, add token links and add to wallet (#37) * connect topbar * tokens links * add link to oracle * Money Market Mockups (#990) * aave page static components, mobile first * responsive panels * functional static borrow asset list * cleanup * renaming * lend assets list * Lend positions list * fixes and cleanup * cleanup * cleanup * cleanup * static ui done. missing validations * add validations * add validations * move errors down * borrow form modal * pr comments * pr comments * fix typo * Update apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx Co-authored-by: Juan Dahl * repay modals * lend modal * use simple table row * asset amount input * add missing translations * fix translation * units and prices mock * setup layout for pool details screen * wallet overview panel * emode card mock * max with for emode category * top bar mock * network switch * cleanup * ui fixes * ui fixes, usd prices * Efficiency card * ordering * fix filter 0 balances checkbox * pr comment * token * Mocked table cards plus refactor of statistics card (#14) * mocked table cards plus refactor of statistics card * ui fixes * Saf 41 charts (#16) * mocked table cards plus refactor of statistics card * ui fixes * [SAF-41] give each graph it's own chart component * [SAF-41] add htmlLegend plugin in charts; remove comments and refactor some colors moving them to constants --------- Co-authored-by: matzapata * removed view transactions button (out of scope) * fix breakpoints * [SAF-41] fix color constant and background for mobile vs desktop views (#20) * [SAF-41] fix color constant and background for mobile vs desktop views * [SAF-41] use theme variables for colors and breakpoint usage for better consistency * Apply suggestions from code review Co-authored-by: Juan Dahl --------- Co-authored-by: Juan Dahl * Update apps/frontend/package.json Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Update apps/frontend/src/app/2_molecules/AmountTransition/AmountTransition.tsx Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Update apps/frontend/src/app/2_molecules/AssetAmountPriceRenderer/AssetAmountPriceRenderer.tsx Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Update apps/frontend/src/app/2_molecules/NetworkSwitch/NetworkSwitch.tsx Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Update apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Update apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Update apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * remove LinkIcon * Apply suggestions from code review Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * pr comments * fixes --------- Co-authored-by: matzapata Co-authored-by: Matias Zapata <42716817+matzapata@users.noreply.github.com> Co-authored-by: Juan Dahl Co-authored-by: Luciano Perez Cerra Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> * chore: cleanup * connected graphs * cleanup * cleanup * cleanup --------- Co-authored-by: Juan Co-authored-by: Luciano Perez Cerra Co-authored-by: Christian Escalante Co-authored-by: Luciano Perez Cerra Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> Co-authored-by: Pietro --- apps/frontend/package.json | 6 +- .../AssetAmountInput/AssetAmountInput.tsx | 8 +- .../src/app/5_pages/AavePage/AavePage.tsx | 24 +-- .../app/5_pages/AavePage/AavePage.utils.tsx | 40 ++-- .../BorrowAssetDetails/BorrowAssetDetails.tsx | 2 +- .../components/BorrowModal/BorrowForm.tsx | 13 +- .../BorrowModal/BorrowForm.utils.ts | 14 ++ .../BorrowModal/BorrowModalContainer.tsx | 28 +++ .../BorrowPositionsList.tsx | 4 +- .../BorrowRateModeSelect.tsx | 1 + .../EfficiencyModeCard/EfficiencyModeCard.tsx | 53 ++--- .../DisableEModeForm/DisableEModeForm.tsx | 10 +- .../EnableEModeForm/EnableEModeForm.tsx | 2 +- .../SwitchEModeForm/SwitchEModeForm.tsx | 8 +- .../RepayForm/RepayWithCollateralForm.tsx | 4 + .../RepayForm/RepayWithWalletBalanceForm.tsx | 1 + .../RepayModal/RepayModalContainer.tsx | 77 +++++++ .../RepayModal/RepayWithCollateralForm.tsx | 197 ++++++++++++++++++ .../RepayModal/RepayWithWalletBalanceForm.tsx | 140 +++++++++++++ .../LendAssetsList.constants.tsx | 2 +- .../components/AssetBalance/AssetBalance.tsx | 27 --- .../LendAssetAction/LendAssetAction.tsx | 8 +- .../LendAssetDetails/LendAssetDetails.tsx | 3 +- .../components/LendForm/LendForm.tsx | 10 +- .../components/LendModal/LendForm.tsx | 102 +++++++++ .../LendModal/LendModalContainer.tsx | 28 +++ .../LendPositionsList/LendPositionsList.tsx | 21 +- .../LendPositionDetails.tsx | 2 +- .../ToggleCollateralAction.tsx | 8 +- .../components/WithdrawForm/WithdrawForm.tsx | 1 + .../components/WithdrawModal/WithdrawForm.tsx | 161 ++++++++++++++ .../WithdrawModal/WithdrawModalContainer.tsx | 28 +++ .../AaveReserveOverviewPage.tsx | 61 +----- .../BorrowDetailsGraph/BorrowDetailsGraph.tsx | 103 +++++++-- .../BorrowDetailsGraph.utils.ts | 21 ++ .../components/EModeDetails/EModeDetails.tsx | 40 +++- .../EModeDetails/EModeDetails.utils.ts | 15 ++ .../InterestRateModelGraph.tsx | 87 ++++---- .../components/Chart/Chart.tsx | 1 - .../SupplyDetailsGraph/SupplyDetailsGraph.tsx | 120 +++++++++-- .../SupplyDetailsGraph.utils.tsx | 25 +++ .../components/TopPanel/TopPanel.tsx | 60 +++--- .../components/TopPanel/TopPanel.utils.tsx | 14 -- .../ReserveTokens/ReserveTokens.tsx | 12 +- .../components/TokenButton/TokenButton.tsx | 1 + .../components/SupplyAction/SupplyAction.tsx | 2 +- apps/frontend/src/constants/aave.ts | 2 + apps/frontend/src/hooks/aave/constants.ts | 1 - .../frontend/src/hooks/aave/useAaveBorrow.tsx | 28 ++- .../src/hooks/aave/useAaveEModeCategories.tsx | 25 +-- apps/frontend/src/hooks/aave/useAaveRates.tsx | 42 ++-- .../src/hooks/aave/useAaveReservesData.tsx | 67 +++--- .../src/hooks/aave/useAaveSetUserEMode.tsx | 24 ++- .../hooks/aave/useAaveUserReservesData.tsx | 20 +- .../src/hooks/useAddTokenToWallet.tsx | 35 ++++ .../frontend/src/locales/en/translations.json | 2 +- .../src/utils/aave/AaveEModeCategories.ts | 6 +- .../aave/AaveRepayTransactionsFactory.ts | 4 +- .../src/utils/aave/AaveUserReservesSummary.ts | 11 +- .../aave/AaveWithdrawTransactionsFactory.ts | 2 +- apps/frontend/src/utils/math.ts | 23 ++ 61 files changed, 1436 insertions(+), 451 deletions(-) create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.utils.ts create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowModalContainer.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx delete mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/AssetBalance/AssetBalance.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendModal/LendForm.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendModal/LendModalContainer.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawForm.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawModalContainer.tsx create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.utils.ts create mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.utils.tsx delete mode 100644 apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.utils.tsx delete mode 100644 apps/frontend/src/hooks/aave/constants.ts create mode 100644 apps/frontend/src/hooks/useAddTokenToWallet.tsx diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 02a7f905c..29c9c8048 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -4,8 +4,8 @@ "homepage": ".", "private": true, "dependencies": { - "@aave/contract-helpers": "^1.29.1", - "@aave/math-utils": "^1.29.1", + "@aave/contract-helpers": "1.29.1", + "@aave/math-utils": "1.29.1", "@apollo/client": "3.7.1", "@apollo/react-hooks": "4.0.0", "@loadable/component": "5.15.2", @@ -57,7 +57,7 @@ "react-slider": "2.0.6", "react-timer-hook": "3.0.7", "reactjs-localstorage": "1.0.1", - "reflect-metadata": "^0.2.2", + "reflect-metadata": "0.2.2", "remark-gfm": "3.0.1", "rxjs": "7.5.6", "sanitize-html": "2.11.0", diff --git a/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx b/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx index fd500f1d9..5e664e374 100644 --- a/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx +++ b/apps/frontend/src/app/2_molecules/AssetAmountInput/AssetAmountInput.tsx @@ -3,14 +3,13 @@ import React, { FC, useCallback } from 'react'; import { AmountInput, Paragraph, Select, SelectOption } from '@sovryn/ui'; import { Decimal, Decimalish } from '@sovryn/utils'; -import { BOB_CHAIN_ID } from '../../../config/chains'; - import { AmountRenderer } from '../AmountRenderer/AmountRenderer'; import { AssetRenderer } from '../AssetRenderer/AssetRenderer'; import { MaxButton } from '../MaxButton/MaxButton'; type AssetAmountInputProps = { label?: string; + chainId?: string | undefined; maxAmount?: Decimalish; invalid?: boolean; amountLabel?: string; @@ -24,6 +23,7 @@ type AssetAmountInputProps = { export const AssetAmountInput: FC = ({ label, + chainId, maxAmount, amountLabel, amountValue, @@ -37,13 +37,13 @@ export const AssetAmountInput: FC = ({ const assetOptionRenderer = useCallback( ({ value }) => ( ), - [], + [chainId], ); return ( diff --git a/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx b/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx index 3def8a6e7..f7644b114 100644 --- a/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/AavePage.tsx @@ -14,6 +14,7 @@ import { normalizeBorrowPositions, normalizeLendPoolDetails, normalizeLendPositions, + tabsItems, } from './AavePage.utils'; import { BorrowAssetsList } from './components/BorrowAssetsList/BorrowAssetsList'; import { BorrowPoolDetails } from './components/BorrowAssetsList/BorrowAssetsList.types'; @@ -54,22 +55,6 @@ const AavePage: FC = () => { [reserves, summary], ); - const tabsItems = useMemo( - () => [ - { - activeClassName: 'text-primary-20', - dataAttribute: 'lending', - label: t(pageTranslations.common.lend), - }, - { - activeClassName: 'text-primary-20', - dataAttribute: 'borrowing', - label: t(pageTranslations.common.borrow), - }, - ], - [], - ); - return (
@@ -77,9 +62,9 @@ const AavePage: FC = () => {
@@ -140,5 +125,4 @@ const AavePage: FC = () => {
); }; - export default AavePage; diff --git a/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx b/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx index 0f09428db..5b01426ef 100644 --- a/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx @@ -1,6 +1,9 @@ +import { t } from 'i18next'; + import { Decimal } from '@sovryn/utils'; import { Reserve } from '../../../hooks/aave/useAaveReservesData'; +import { translations } from '../../../locales/i18n'; import { BorrowRateMode } from '../../../types/aave'; import { AaveUserReservesSummary } from '../../../utils/aave/AaveUserReservesSummary'; import { BorrowPoolDetails } from './components/BorrowAssetsList/BorrowAssetsList.types'; @@ -8,9 +11,22 @@ import { BorrowPosition } from './components/BorrowPositionsList/BorrowPositions import { LendPoolDetails } from './components/LendAssetsList/LendAssetsList.types'; import { LendPosition } from './components/LendPositionsList/LendPositionsList.types'; -export function normalizeLendPositions( +export const tabsItems = [ + { + activeClassName: 'text-primary-20', + dataAttribute: 'lending', + label: t(translations.aavePage.common.lend), + }, + { + activeClassName: 'text-primary-20', + dataAttribute: 'borrowing', + label: t(translations.aavePage.common.borrow), + }, +]; + +export const normalizeLendPositions = ( userReservesSummary: AaveUserReservesSummary, -): LendPosition[] { +): LendPosition[] => { return userReservesSummary.reserves.reduce((acc, r) => { if (r.supplied.gt(0)) { acc.push({ @@ -23,11 +39,11 @@ export function normalizeLendPositions( } return acc; }, [] as LendPosition[]); -} +}; -export function normalizeBorrowPositions( +export const normalizeBorrowPositions = ( userReservesSummary: AaveUserReservesSummary, -): BorrowPosition[] { +): BorrowPosition[] => { return userReservesSummary.reserves.reduce((acc, r) => { if (r.borrowed.gt(0)) { acc.push({ @@ -48,12 +64,12 @@ export function normalizeBorrowPositions( } return acc; }, [] as BorrowPosition[]); -} +}; -export function normalizeBorrowPoolDetails( +export const normalizeBorrowPoolDetails = ( reserves: Reserve[], userReservesSummary: AaveUserReservesSummary, -): BorrowPoolDetails[] { +): BorrowPoolDetails[] => { if (userReservesSummary.reserves.length === 0) { return reserves.reduce((acc, r) => { if (r.borrowingEnabled) { @@ -84,12 +100,12 @@ export function normalizeBorrowPoolDetails( return acc; }, [] as BorrowPoolDetails[]); } -} +}; -export function normalizeLendPoolDetails( +export const normalizeLendPoolDetails = ( reserves: Reserve[], userReservesSummary: AaveUserReservesSummary, -): LendPoolDetails[] { +): LendPoolDetails[] => { if (userReservesSummary.reserves.length === 0) { return reserves.map(r => ({ asset: r.symbol, @@ -105,4 +121,4 @@ export function normalizeLendPoolDetails( walletBalance: r.walletBalance, })); } -} +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx index c087ba1c6..620e7deea 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetDetails/BorrowAssetDetails.tsx @@ -31,7 +31,7 @@ export const BorrowAssetDetails: FC = ({ } - value={} + value={} /> {/* Available */} diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx index d9b5dccef..b56df01bd 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx @@ -93,6 +93,10 @@ export const BorrowForm: FC = ({ asset, onComplete }) => { summary.collateralBalance, ]); + const borrowApr = useMemo(() => { + return Decimal.from(borrowReserve?.reserve.variableBorrowAPR ?? 0).mul(100); + }, [borrowReserve?.reserve.variableBorrowAPR]); + const isValidBorrowAmount = useMemo( () => (borrowSize.gt(0) ? borrowSize.lte(maximumBorrowAmount) : true), [borrowSize, maximumBorrowAmount], @@ -112,6 +116,7 @@ export const BorrowForm: FC = ({ asset, onComplete }) => {
= ({ asset, onComplete }) => { - } + value={} /> diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.utils.ts b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.utils.ts new file mode 100644 index 000000000..f55b40d9e --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.utils.ts @@ -0,0 +1,14 @@ +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS } from '../../../../../../../constants/lending'; + +export const getCollateralRatioThresholds = () => { + // TODO: recheck this and adjust based on aave + const minimumCollateralRatio = + MINIMUM_COLLATERAL_RATIO_LENDING_POOLS.mul(100); + + return { + START: minimumCollateralRatio.mul(0.9).toNumber(), + MIDDLE_START: minimumCollateralRatio.toNumber() - 0.1, + MIDDLE_END: minimumCollateralRatio.mul(1.2).toNumber(), + END: minimumCollateralRatio.mul(1.6).toNumber(), + }; +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowModalContainer.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowModalContainer.tsx new file mode 100644 index 000000000..5927b58df --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowModalContainer.tsx @@ -0,0 +1,28 @@ +import React, { FC } from 'react'; + +import { t } from 'i18next'; + +import { Dialog, DialogBody, DialogHeader } from '@sovryn/ui'; + +import { translations } from '../../../../../../../locales/i18n'; +import { BorrowForm } from './BorrowForm'; + +type BorrowModalContainerProps = { + isOpen: boolean; + handleCloseModal: () => void; +}; + +export const BorrowModalContainer: FC = ({ + isOpen, + handleCloseModal, +}) => ( + + + + + + +); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index 8903daa54..e0c699256 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -45,9 +45,7 @@ export const BorrowPositionsList: FC = ({ const { account } = useAccount(); const [open, setOpen] = useState(true); const [orderOptions, setOrderOptions] = useState(); - const [repayAssetDialog, setRepayAssetDialog] = useState< - string | undefined - >(); + const [repayAssetDialog, setRepayAssetDialog] = useState(); const onRepayClick = useCallback((asset: string) => { setRepayAssetDialog(asset); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowRateModeSelect/BorrowRateModeSelect.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowRateModeSelect/BorrowRateModeSelect.tsx index aa08eb1ff..e57ef2ce8 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowRateModeSelect/BorrowRateModeSelect.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/BorrowRateModeSelect/BorrowRateModeSelect.tsx @@ -32,6 +32,7 @@ export const BorrowRateModeSelect: FC = ({ ]; if ( + // cannot be collateral for stable borrows (position.stableBorrowEnabled && !position.isCollateral) || position.borrowRateMode === BorrowRateMode.STABLE ) { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx index 82d1a9079..72a9d44f6 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx @@ -20,7 +20,6 @@ import { import { EModeIcon } from '../../../../../../1_atoms/Icons/Icons'; import { useAaveEModeCategories } from '../../../../../../../hooks/aave/useAaveEModeCategories'; import { translations } from '../../../../../../../locales/i18n'; -import { EModeCategory } from '../../../../../../../types/aave'; import { DisableEModeForm } from './components/DisableEModeForm/DisableEModeForm'; import { EnableEModeForm } from './components/EnableEModeForm/EnableEModeForm'; import { SwitchEModeForm } from './components/SwitchEModeForm/SwitchEModeForm'; @@ -146,32 +145,36 @@ export const EfficiencyModeCard: FC = ({ - - - - + - - - - - - - + + + + )} + + {currentCategory && ( + + - - + + + + + )} ); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx index 30a0e83b9..e06229584 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx @@ -69,6 +69,7 @@ export const DisableEModeForm: FC = ({ if (!newSummary) { return Decimal.from(0); } + return AaveCalculations.computeCollateralRatio( Decimal.from(newSummary.totalCollateralUSD), Decimal.from(newSummary.totalBorrowsUSD), @@ -76,10 +77,9 @@ export const DisableEModeForm: FC = ({ }, [newSummary]); const liquidationRisk = useMemo(() => { - if (newSummary?.healthFactor === '-1') { - return false; - } - return Decimal.from(newSummary?.healthFactor ?? 0).lte(1); + // if health factor is below 1 we're at risk. Negative means doesn't apply + const healthFactor = Decimal.from(newSummary?.healthFactor ?? 0); + return healthFactor.lte(1) && healthFactor.gt(0); }, [newSummary?.healthFactor]); const confirmEnabled = useMemo(() => { @@ -138,7 +138,7 @@ export const DisableEModeForm: FC = ({ value={
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/EnableEModeForm/EnableEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/EnableEModeForm/EnableEModeForm.tsx index 4e1331450..11809741e 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/EnableEModeForm/EnableEModeForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/EnableEModeForm/EnableEModeForm.tsx @@ -83,7 +83,7 @@ export const EnableEModeForm: FC = ({ label={t(translations.aavePage.eMode.maxLoanToValue)} value={ diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx index 9d33fa198..f6014d3ac 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx @@ -153,17 +153,13 @@ export const SwitchEModeForm: FC = ({ label={t(translations.aavePage.eMode.maxLoanToValue)} value={
- + diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx index 5fd392d66..6bd3e9754 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx @@ -12,6 +12,8 @@ import { } from '@sovryn/ui'; import { Decimal } from '@sovryn/utils'; +import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; + import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { AmountTransition } from '../../../../../../2_molecules/AmountTransition/AmountTransition'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; @@ -98,6 +100,7 @@ export const RepayWithCollateralForm: FC = () => { = () => {
void; +}; + +enum RepayWith { + BALANCE = 0, + COLLATERAL, +} + +export const RepayModalContainer: FC = ({ + isOpen, + handleCloseModal, +}) => { + const [activeTab, setActiveTab] = useState(RepayWith.BALANCE); + + return ( + + + + + {t(translations.aavePage.repayModal.repayWith)} + + + + + {activeTab === RepayWith.BALANCE ? ( + + ) : ( + + )} + + + ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx new file mode 100644 index 000000000..c38e71287 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx @@ -0,0 +1,197 @@ +import React, { FC, useMemo, useState } from 'react'; + +import { t } from 'i18next'; + +import { + Button, + ErrorBadge, + ErrorLevel, + HelperButton, + SimpleTable, + SimpleTableRow, +} from '@sovryn/ui'; +import { Decimal } from '@sovryn/utils'; + +import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; + +import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; +import { AmountTransition } from '../../../../../../2_molecules/AmountTransition/AmountTransition'; +import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; +import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; +import { config } from '../../../../../../../constants/aave'; +import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; +import { translations } from '../../../../../../../locales/i18n'; +import { CollateralRatioHealthBar } from '../../../CollateralRatioHealthBar/CollateralRatioHealthBar'; + +const pageTranslations = translations.aavePage; + +type RepayWithCollateralFormProps = { + onSuccess: () => void; +}; + +export const RepayWithCollateralForm: FC = () => { + const assetPrice = 3258.47; // TODO: this is mocked data. Replace with proper hook + const totalBorrowed = Decimal.from(10); // TODO: this is mocked data. Replace with proper hook + const collateralToLoanRate = Decimal.from(10); // TODO: this is mocked data. Replace with proper hook + const collateralSize = Decimal.from(10); // TODO: this is mockd data. Replace with proper hook + const assets = useMemo(() => ['BTC', 'SOV'], []); // TODO: this is mocked data. Replace with proper hook + const [maximumRepayAmount] = useState(Decimal.from(10)); // TODO: this is mocked data. Replace with proper hook + const [maximumRepayWithAmount] = useState(Decimal.from(10)); // TODO: this is mocked data. Replace with proper hook + const [repayAsset, setRepayAsset] = useState(assets[0]); + const [repayAmount, setRepayAmount, repaySize] = useDecimalAmountInput(''); + const [repayWithAsset, setRepayWithAsset] = useState(assets[0]); + const [repayWithAmount, setRepayWithAmount, repayWithSize] = + useDecimalAmountInput(''); + + const repayAssetsOptions = useMemo( + () => + assets.map(token => ({ + value: token, + label: ( + + ), + })), + [assets], + ); + + const remainingDebt = useMemo( + () => maximumRepayAmount.sub(repaySize), + [repaySize, maximumRepayAmount], + ); + + const collateralRatio = useMemo(() => { + if ([collateralSize, totalBorrowed, repaySize].some(v => v.isZero())) { + return Decimal.from(0); + } + + return collateralSize.mul(collateralToLoanRate).div(totalBorrowed).mul(100); + }, [collateralSize, totalBorrowed, repaySize, collateralToLoanRate]); + + // TODO: add more validations + const isValidRepayAmount = useMemo( + () => (repaySize.gt(0) ? repaySize.lte(maximumRepayAmount) : true), + [repaySize, maximumRepayAmount], + ); + + // TODO: add more validations + const isValidRepayWithAmount = useMemo( + () => + repayWithSize.gt(0) ? repayWithSize.lte(maximumRepayWithAmount) : true, + [repayWithSize, maximumRepayWithAmount], + ); + + // TODO: add more validations + const submitButtonDisabled = useMemo( + () => + !isValidRepayAmount || + repaySize.lte(0) || + !isValidRepayWithAmount || + repayWithSize.lte(0), + [isValidRepayAmount, repaySize, isValidRepayWithAmount, repayWithSize], + ); + + return ( +
+
+ + + {!isValidRepayAmount && ( + + )} +
+ +
+ + + {!isValidRepayWithAmount && ( + + )} +
+ + + + + + } + /> + + + +
+ } + /> + + {t(translations.aavePage.repayModal.priceImpact)}{' '} + +
+ } + value={} + /> + + +
+ } + /> + + +
); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx index 0bc9c1b20..f14b941a7 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx @@ -8,7 +8,6 @@ import { HelperButton, Icon, IconNames, SimpleTableRow } from '@sovryn/ui'; import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { translations } from '../../../../../../../locales/i18n'; import { LendPoolDetails } from '../../LendAssetsList.types'; -import { AssetBalanceRenderer } from '../AssetBalance/AssetBalance'; import { LendAssetAction } from '../LendAssetAction/LendAssetAction'; type LendAssetDetailsProps = { @@ -25,7 +24,7 @@ export const LendAssetDetails: FC = ({ {/* Available */} } + value={} /> = ({ [lendSize, lendAssetBalance], ); - const onConfirm = useCallback(async () => { + const isDepositEnabled = useMemo( + () => lendSize.gt(0) && isValidLendAmount, + [lendSize, isValidLendAmount], + ); + + const onConfirm = useCallback(() => { handleDeposit(lendSize, reserve.symbol, { onComplete }); }, [handleDeposit, lendSize, reserve, onComplete]); @@ -82,6 +87,7 @@ export const LendForm: FC = ({
= ({
= ({ /> } - value={} + value={} /> {/* Can be collateral */} diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/ToggleCollateralAction/ToggleCollateralAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/ToggleCollateralAction/ToggleCollateralAction.tsx index 46e4d21cc..d6d4a477c 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/ToggleCollateralAction/ToggleCollateralAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/ToggleCollateralAction/ToggleCollateralAction.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, { FC, useCallback, useState } from 'react'; import { Toggle } from '@sovryn/ui'; @@ -15,11 +15,11 @@ export const ToggleCollateralAction: FC = ({ const { handleSwitchCollateral } = useAaveSupply(); const [isCollateral, setIsCollateral] = useState(position.collateral); - const toggleCollateral = async () => { - await handleSwitchCollateral(position.asset, !isCollateral, { + const toggleCollateral = useCallback(() => { + handleSwitchCollateral(position.asset, !isCollateral, { onComplete: () => setIsCollateral(!isCollateral), }); - }; + }, [handleSwitchCollateral, setIsCollateral, isCollateral, position.asset]); return ( = ({ asset, onComplete }) => {
void; +}; + +export const WithdrawForm: FC = ({ asset, onComplete }) => { + const { handleWithdraw } = useAaveWithdraw(); + const { summary } = useAaveUserReservesData(); + const [withdrawAsset, setWithdrawAsset] = useState(asset); + const [withdrawAmount, setWithdrawAmount, withdrawSize] = + useDecimalAmountInput(''); + + const withdrawableAssetsOptions = useMemo( + () => + summary.reserves + .filter(r => r.supplied.gt(0)) + .map(sa => ({ + value: sa.asset, + label: ( + + ), + })), + [summary], + ); + + const withdrawReserve = useMemo(() => { + return summary.reserves.find(r => r.reserve.symbol === withdrawAsset); + }, [withdrawAsset, summary]); + + const maximumWithdrawAmount: Decimal = useMemo(() => { + return withdrawReserve ? withdrawReserve.supplied : Decimal.from(0); + }, [withdrawReserve]); + + const withdrawAmountUsd: Decimal = useMemo(() => { + return withdrawReserve + ? withdrawSize.mul(withdrawReserve.reserve.priceInUSD) + : Decimal.from(0); + }, [withdrawSize, withdrawReserve]); + + const remainingSupply = useMemo( + () => maximumWithdrawAmount.sub(withdrawSize), + [withdrawSize, maximumWithdrawAmount], + ); + + const isValidWithdrawAmount = useMemo( + () => (withdrawSize.gt(0) ? withdrawSize.lte(maximumWithdrawAmount) : true), + [withdrawSize, maximumWithdrawAmount], + ); + + const submitButtonDisabled = useMemo( + () => !isValidWithdrawAmount || withdrawSize.lte(0), + [isValidWithdrawAmount, withdrawSize], + ); + + const tabItems = useMemo( + () => [ + // For now just withdraw is supported + { + activeClassName: 'text-primary-20', + dataAttribute: 'withdraw', + label: t(translations.common.withdraw), + }, + ], + [], + ); + + const onConfirm = useCallback(() => { + handleWithdraw( + withdrawSize, + withdrawAsset, + withdrawSize.eq(maximumWithdrawAmount), + { onComplete }, + ); + }, [ + handleWithdraw, + withdrawSize, + withdrawAsset, + onComplete, + maximumWithdrawAmount, + ]); + + return ( +
+
+ + +
+ + {!isValidWithdrawAmount && ( + + )} +
+
+ + + + } + /> + + +
@@ -88,16 +142,29 @@ export const BorrowDetailsGraph: FC = () => { {t(pageTranslations.collectorInfo)} - {/* statistics */} + {/* reserve factor */}
} + value={ + + } /> + } + value={ + + } />
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts new file mode 100644 index 000000000..82cbf0f72 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts @@ -0,0 +1,21 @@ +import { Decimal } from '@sovryn/utils'; + +import { config } from '../../../../../constants/aave'; +import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; +import { getBobExplorerUrl } from '../../../../../utils/helpers'; + +export const normalizeBorrowStats = (reserve: Reserve) => ({ + apr: Decimal.from(reserve.variableBorrowAPR).mul(100), + totalBorrowed: Decimal.from(reserve.totalDebt), + totalBorrowedUSD: Decimal.from(reserve.totalDebtUSD), + borrowCap: Decimal.from(reserve.borrowCap), + borrowCapUSD: Decimal.from(reserve.borrowCapUSD), + borrowedPercentage: Decimal.from(reserve.totalDebtUSD) + .div(Decimal.from(reserve.debtCeilingUSD)) + .mul(100) + .toString(0), + collectorContractLink: `${getBobExplorerUrl()}/address/${ + config.TreasuryAddress + }`, + reserveFactor: Decimal.from(reserve.reserveFactor).mul(100), +}); diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx index e29fc6d04..b9e16f9f4 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, { FC, useMemo, useState } from 'react'; import { t } from 'i18next'; @@ -7,20 +7,26 @@ import { Accordion, Icon, Link, Paragraph } from '@sovryn/ui'; import { EModeIcon } from '../../../../1_atoms/Icons/Icons'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; +import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; +import { normalizeEModeStats } from './EModeDetails.utils'; const pageTranslations = translations.aaveReserveOverviewPage.eModeDetails; -export const EModeDetails: FC = () => { +export type EModeDetailsProps = { + reserve: Reserve; +}; + +export const EModeDetails: FC = ({ reserve }) => { const [open, setOpen] = useState(true); const { isMobile } = useIsMobile(); - // TODO: All this data is mocked - const maxLtv = 93; - const liquidationThreshold = 94; - const liquidationPenalty = 1; + const eModeStats = useMemo(() => { + return normalizeEModeStats(reserve); + }, [reserve]); + if (!eModeStats.enabled) return null; return ( {
- {t(pageTranslations.ethCorrelatedCategory)} + {eModeStats.label}
@@ -53,19 +59,33 @@ export const EModeDetails: FC = () => { label={t(pageTranslations.maxLtv)} className="space-y-2" help={t(pageTranslations.maxLtvInfo)} - value={} + value={ + + } /> } + value={ + + } /> } + value={ + + } /> diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.utils.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.utils.ts new file mode 100644 index 000000000..773dfac5b --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/EModeDetails/EModeDetails.utils.ts @@ -0,0 +1,15 @@ +import { Decimal } from '@sovryn/utils'; + +import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; + +export const normalizeEModeStats = (reserve: Reserve) => ({ + label: reserve.eModeLabel, + enabled: reserve.eModeCategoryId !== 0, + ltv: Decimal.from(reserve.formattedEModeLtv).mul(100), + liquidationThreshold: Decimal.from( + reserve.formattedEModeLiquidationThreshold, + ).mul(100), + liquidationPenalty: Decimal.from(reserve.formattedEModeLiquidationBonus).mul( + 100, + ), +}); diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx index d850c613d..c6acc0f1e 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx @@ -4,10 +4,12 @@ import { t } from 'i18next'; import { theme } from '@sovryn/tailwindcss-config'; import { Accordion, Link } from '@sovryn/ui'; +import { Decimal } from '@sovryn/utils'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; -import { IRatesDataResult } from '../../../../../hooks/aave/useAaveRates'; +import { useAaveInterestRatesData } from '../../../../../hooks/aave/useAaveRates'; +import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; import { Chart } from './components/Chart/Chart'; @@ -15,22 +17,17 @@ import { Chart } from './components/Chart/Chart'; const pageTranslations = translations.aaveReserveOverviewPage.interestRateModel; type InterestRateModelGraphProps = { - rates: IRatesDataResult; - reserveFactor: string | undefined; + reserve: Reserve; }; export const InterestRateModelGraph: FC = ({ - rates, - reserveFactor, + reserve, }) => { - const [open, setOpen] = useState(true); const { isMobile } = useIsMobile(); + const [open, setOpen] = useState(true); + const { rates } = useAaveInterestRatesData(); - const meta = { - label: t(pageTranslations.chart.label1), - lineColor: theme.colors['primary-30'], - }; - + if (!rates) return null; return ( = ({ flatMode={!isMobile} dataAttribute="interest-rate-model" > - {rates && ( -
-
- - } - /> - -
+
+
+ + } + /> + +
+ + - - {/* statistics */} -
- } - /> - } - /> -
+ {/* statistics */} +
+ + } + /> + } + />
- )} +
); }; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx index f2050954e..3ebca6814 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx @@ -132,7 +132,6 @@ export const Chart: FC = ({ meta, rates }) => { callback: function (value) { return value + '%'; }, - //maxTicksLimit: 5, }, }, y: { diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx index ad1e28006..8dd6287f4 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx @@ -1,5 +1,6 @@ import React, { FC, useMemo, useState } from 'react'; +import classNames from 'classnames'; import { t } from 'i18next'; import { theme } from '@sovryn/tailwindcss-config'; @@ -7,17 +8,27 @@ import { Accordion, Icon, IconNames, Paragraph } from '@sovryn/ui'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; +import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; +import { formatAmountWithSuffix } from '../../../../../utils/math'; +import { normalizeSupplyStats } from './SupplyDetailsGraph.utils'; import { Chart } from './components/Chart/Chart'; import { harcodedData } from './components/Chart/Chart.constants'; import { MockData } from './components/Chart/Chart.types'; const pageTranslations = translations.aaveReserveOverviewPage.supplyDetails; -export const SupplyDetailsGraph: FC = () => { +export type SupplyDetailsGraphProps = { + reserve: Reserve; +}; + +export const SupplyDetailsGraph: FC = ({ + reserve, +}) => { const [open, setOpen] = useState(true); const { isMobile } = useIsMobile(); + // TODO: mocked amounts const mockData: MockData<{ x: string; y: number }> = useMemo(() => { const data1 = harcodedData; @@ -30,6 +41,10 @@ export const SupplyDetailsGraph: FC = () => { }; }, []); + const supplyStats = useMemo(() => { + return normalizeSupplyStats(reserve); + }, [reserve]); + return ( { value={
- - {t(pageTranslations.of)} - + + {supplyStats.supplyCap.gt(0) && ( + <> + {t(pageTranslations.of)} + + + )}
+
- - {t(pageTranslations.of)} - + + {supplyStats.supplyCap.gt(0) && ( + <> + {t(pageTranslations.of)} + + + )}
{/* Progress bar */} -
-
-
+ {supplyStats.supplyCap.gt(0) && ( +
+
+
+ )}
} /> } + value={ + + } />
- {/* heading */} + {/* collateral usage */}
{t(pageTranslations.collateralUsage)}
- {t(pageTranslations.canBeCollateral)} + {t( + reserve.usageAsCollateralEnabled + ? pageTranslations.canBeCollateral + : pageTranslations.cannotBeCollateral, + )}
{/* statistics */}
+ {/* maxLTV */} } + value={ + + } /> + + {/* liquidation threshold */} } + value={ + + } /> + + {/* liquidation penalty */} } + value={ + + } />
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.utils.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.utils.tsx new file mode 100644 index 000000000..7430758d0 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.utils.tsx @@ -0,0 +1,25 @@ +import { Decimal } from '@sovryn/utils'; + +import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; + +export const normalizeSupplyStats = (reserve: Reserve) => ({ + totalSupplied: Decimal.from(reserve.formattedAvailableLiquidity).add( + reserve.totalDebt, + ), + totalSuppliedUSD: Decimal.from(reserve.availableLiquidityUSD).add( + reserve.totalDebtUSD, + ), + supplyApy: Decimal.from(reserve.supplyAPY).mul(100), + supplyCap: Decimal.from(reserve.supplyCap), + supplyCapUSD: Decimal.from(reserve.supplyCapUSD), + suppliedPercentage: Decimal.from(reserve.supplyCapUSD) + .div(Decimal.from(reserve.supplyCapUSD)) + .mul(100), + maxLTV: Decimal.from(reserve.baseLTVasCollateral).div(100), + liquidationThreshold: Decimal.from( + reserve.formattedReserveLiquidationThreshold, + ).mul(100), + liquidationPenalty: Decimal.from( + reserve.formattedReserveLiquidationBonus, + ).mul(100), +}); diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx index c4240d299..041a8d0bb 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx @@ -21,10 +21,11 @@ import { WalletIcon } from '../../../../1_atoms/Icons/Icons'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; -import { useNotifyError } from '../../../../../hooks/useNotifyError'; +import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; +import { useAddTokenToWallet } from '../../../../../hooks/useAddTokenToWallet'; import { translations } from '../../../../../locales/i18n'; import { getBobExplorerUrl } from '../../../../../utils/helpers'; -import { formatUsdAmount } from './TopPanel.utils'; +import { formatAmountWithSuffix } from '../../../../../utils/math'; import { ReserveTokens } from './components/ReserveTokens/ReserveTokens'; const pageTranslations = translations.aaveReserveOverviewPage.topPanel; @@ -44,45 +45,32 @@ export type ReserveOverview = { }; type TopPanelProps = { - reserve: ReserveOverview; + reserve: Reserve; className?: string; }; export const TopPanel: FC = ({ reserve, className }) => { - const { notifyError } = useNotifyError(); + const { addTokenToWallet } = useAddTokenToWallet(); const openInExplorer = useCallback((tokenAddress: string) => { const explorer = getBobExplorerUrl(); window.open(`${explorer}/address/${tokenAddress}`, '_blank'); }, []); - const addToWallet = useCallback( - (token: string) => { - try { - if (!(window as any)?.ethereum) { - throw new Error('Wallet not available'); - } - - (window as any)?.ethereum.request({ - method: 'wallet_watchAsset', - params: { - type: 'ERC20', - options: { - chainId: BOB_CHAIN_ID, - address: token, - }, - }, - }); - } catch (error) { - notifyError(error); - } - }, - [notifyError], - ); - const oracleLink = useMemo(() => { - return getBobExplorerUrl() + '/address/' + reserve.oracleAddress; - }, [reserve.oracleAddress]); + return getBobExplorerUrl() + '/address/' + reserve.priceOracle; + }, [reserve.priceOracle]); + + const reserveSize = useMemo(() => { + return Decimal.from(reserve.availableLiquidityUSD).add( + reserve.totalDebtUSD, + ); + }, [reserve]); + + const onTokenClick = useCallback( + (tokenAddress: string) => addTokenToWallet(tokenAddress, BOB_CHAIN_ID), + [addTokenToWallet], + ); return (
@@ -124,7 +112,7 @@ export const TopPanel: FC = ({ reserve, className }) => { underlyingTokenAddress={reserve.underlyingAsset} variableDebtTokenAddress={reserve.variableDebtTokenAddress} stableDebtTokenAddress={reserve.stableDebtTokenAddress} - onClick={openInExplorer} + onTokenClick={openInExplorer} /> } > @@ -150,7 +138,7 @@ export const TopPanel: FC = ({ reserve, className }) => { underlyingTokenAddress={reserve.underlyingAsset} variableDebtTokenAddress={reserve.variableDebtTokenAddress} stableDebtTokenAddress={reserve.stableDebtTokenAddress} - onClick={addToWallet} + onTokenClick={onTokenClick} /> } > @@ -171,7 +159,7 @@ export const TopPanel: FC = ({ reserve, className }) => { } /> @@ -181,7 +169,7 @@ export const TopPanel: FC = ({ reserve, className }) => { } /> @@ -190,7 +178,7 @@ export const TopPanel: FC = ({ reserve, className }) => { value={ @@ -202,7 +190,7 @@ export const TopPanel: FC = ({ reserve, className }) => { value={ diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.utils.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.utils.tsx deleted file mode 100644 index f766624e0..000000000 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.utils.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Decimal } from '@sovryn/utils'; - -export function formatUsdAmount(value: Decimal): { - value: string; - suffix: string; -} { - if (value.gte(1e6)) { - return { value: value.div(1e6).toString(1), suffix: 'M' }; - } else if (value.gte(1e3)) { - return { value: value.div(1e3).toString(1), suffix: 'K' }; - } else { - return { value: value.toString(), suffix: '' }; - } -} diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx index ff929aa9e..6523b7382 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx @@ -16,13 +16,13 @@ type ReserveTokensProps = { aTokenAddress: string; variableDebtTokenAddress: string; stableDebtTokenAddress: string; - onClick: (tokenAddress: string) => void; + onTokenClick: (tokenAddress: string) => void; className?: string; }; export const ReserveTokens: FC = ({ symbol, - onClick, + onTokenClick, underlyingTokenAddress, aTokenAddress, variableDebtTokenAddress, @@ -48,14 +48,14 @@ export const ReserveTokens: FC = ({ title={t(translations.aaveReserveOverviewPage.topPanel.underlyingToken)} label={symbol} logo={TokenLogo} - onClick={() => onClick(underlyingTokenAddress)} + onClick={() => onTokenClick(underlyingTokenAddress)} /> onClick(aTokenAddress)} + onClick={() => onTokenClick(aTokenAddress)} /> = ({ { symbol }, )} logo={TokenLogo} - onClick={() => onClick(variableDebtTokenAddress)} + onClick={() => onTokenClick(variableDebtTokenAddress)} /> = ({ { symbol }, )} logo={TokenLogo} - onClick={() => onClick(stableDebtTokenAddress)} + onClick={() => onTokenClick(stableDebtTokenAddress)} />
); diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/components/TokenButton/TokenButton.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/components/TokenButton/TokenButton.tsx index e25fa0012..b8c4f3758 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/components/TokenButton/TokenButton.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/components/TokenButton/TokenButton.tsx @@ -25,6 +25,7 @@ export const TokenButton: FC = ({
unknown) => [ ), cellRenderer: (position: BorrowPosition) => ( - + ), }, { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx index e0c699256..5baec6ea6 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/BorrowPositionsList.tsx @@ -47,10 +47,6 @@ export const BorrowPositionsList: FC = ({ const [orderOptions, setOrderOptions] = useState(); const [repayAssetDialog, setRepayAssetDialog] = useState(); - const onRepayClick = useCallback((asset: string) => { - setRepayAssetDialog(asset); - }, []); - const onRepayClose = useCallback(() => { setRepayAssetDialog(undefined); }, []); @@ -71,10 +67,10 @@ export const BorrowPositionsList: FC = ({ p => ( onRepayClick(p.asset)} + onRepayClick={() => setRepayAssetDialog(p.asset)} /> ), - [onRepayClick], + [setRepayAssetDialog], ); return ( @@ -128,7 +124,7 @@ export const BorrowPositionsList: FC = ({
{ + if (!reservesData || !userReservesData || !timestamp) { + return { + ltv: Decimal.from(0), + collateralRatio: Decimal.from(0), + liquidationRisk: false, + }; + } + + const { + marketReferenceCurrencyDecimals, + marketReferenceCurrencyPriceInUsd: marketReferencePriceInUsd, + } = reservesData.baseCurrencyData; + const summary = formatUserSummary({ + userEmodeCategoryId: eModeCategoryId, + currentTimestamp: timestamp, + marketReferencePriceInUsd, + marketReferenceCurrencyDecimals, + userReserves: userReservesData.userReserves, + formattedReserves: formatReserves({ + currentTimestamp: timestamp, + marketReferencePriceInUsd, + marketReferenceCurrencyDecimals, + reserves: reservesData.reservesData, + }), + }); + + // if health factor is below 1 we're at risk. Negative means doesn't apply + const healthFactor = Decimal.from(summary.healthFactor); + const liquidationRisk = healthFactor.lte(1) && healthFactor.gt(0); + + const collateralRatio = AaveCalculations.computeCollateralRatio( + Decimal.from(summary.totalCollateralUSD), + Decimal.from(summary.totalBorrowsUSD), + ); + + return { + ltv: Decimal.from(summary.currentLoanToValue).mul(100), + collateralRatio, + liquidationRisk, + }; +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx index 72a9d44f6..fb4fc18d0 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/EfficiencyModeCard.tsx @@ -169,7 +169,7 @@ export const EfficiencyModeCard: FC = ({ /> diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx index e06229584..cca226a60 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx @@ -1,5 +1,3 @@ -import { formatReserves, formatUserSummary } from '@aave/math-utils'; - import React, { FC, useCallback, useMemo } from 'react'; import { t } from 'i18next'; @@ -21,8 +19,8 @@ import { useAaveSetUserEMode } from '../../../../../../../../../hooks/aave/useAa import { useAaveUserReservesData } from '../../../../../../../../../hooks/aave/useAaveUserReservesData'; import { translations } from '../../../../../../../../../locales/i18n'; import { EModeCategory } from '../../../../../../../../../types/aave'; -import { AaveCalculations } from '../../../../../../../../../utils/aave/AaveCalculations'; import { CollateralRatioHealthBar } from '../../../../../CollateralRatioHealthBar/CollateralRatioHealthBar'; +import { normalizeEModeSummary } from '../../EfficencyModeCard.utils'; type DisableEModeFormProps = { current: EModeCategory; @@ -41,55 +39,23 @@ export const DisableEModeForm: FC = ({ handleDisableUserEMode({ onComplete }); }, [handleDisableUserEMode, onComplete]); - const newSummary = useMemo(() => { - if (!userReservesData || !reservesData) { - return; - } - - const { - marketReferenceCurrencyDecimals, - marketReferenceCurrencyPriceInUsd: marketReferencePriceInUsd, - } = reservesData.baseCurrencyData; - return formatUserSummary({ - userEmodeCategoryId: 0, // disable mode - currentTimestamp: timestamp, - marketReferencePriceInUsd, - marketReferenceCurrencyDecimals, - userReserves: userReservesData.userReserves, - formattedReserves: formatReserves({ - currentTimestamp: timestamp, - marketReferencePriceInUsd, - marketReferenceCurrencyDecimals, - reserves: reservesData.reservesData, - }), - }); - }, [userReservesData, reservesData, timestamp]); - - const newCollateralRatio = useMemo(() => { - if (!newSummary) { - return Decimal.from(0); - } - - return AaveCalculations.computeCollateralRatio( - Decimal.from(newSummary.totalCollateralUSD), - Decimal.from(newSummary.totalBorrowsUSD), + const summaryAfterDisabled = useMemo(() => { + return normalizeEModeSummary( + 0, // disabled + reservesData, + userReservesData, + timestamp, ); - }, [newSummary]); - - const liquidationRisk = useMemo(() => { - // if health factor is below 1 we're at risk. Negative means doesn't apply - const healthFactor = Decimal.from(newSummary?.healthFactor ?? 0); - return healthFactor.lte(1) && healthFactor.gt(0); - }, [newSummary?.healthFactor]); + }, [userReservesData, reservesData, timestamp]); const confirmEnabled = useMemo(() => { // cannot disable if undercollateralized - return !liquidationRisk; - }, [liquidationRisk]); + return !summaryAfterDisabled.liquidationRisk; + }, [summaryAfterDisabled.liquidationRisk]); return (
- {liquidationRisk && ( + {summaryAfterDisabled.liquidationRisk && ( = ({ )} @@ -147,9 +113,7 @@ export const DisableEModeForm: FC = ({ className="h-2 flex-shrink-0" /> diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx index f6014d3ac..766f8abb6 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx @@ -1,5 +1,3 @@ -import { formatReserves, formatUserSummary } from '@aave/math-utils'; - import React, { FC, useCallback, useMemo, useState } from 'react'; import { t } from 'i18next'; @@ -23,8 +21,8 @@ import { useAaveSetUserEMode } from '../../../../../../../../../hooks/aave/useAa import { useAaveUserReservesData } from '../../../../../../../../../hooks/aave/useAaveUserReservesData'; import { translations } from '../../../../../../../../../locales/i18n'; import { EModeCategory } from '../../../../../../../../../types/aave'; -import { AaveCalculations } from '../../../../../../../../../utils/aave/AaveCalculations'; import { CollateralRatioHealthBar } from '../../../../../CollateralRatioHealthBar/CollateralRatioHealthBar'; +import { normalizeEModeSummary } from '../../EfficencyModeCard.utils'; type SwitchEModeFormProps = { current: EModeCategory; @@ -55,39 +53,14 @@ export const SwitchEModeForm: FC = ({ return categories.find(c => c.id === Number(category)); }, [category, categories]); - const newSummary = useMemo(() => { - if (!userReservesData || !reservesData) { - return; - } - - const { - marketReferenceCurrencyDecimals, - marketReferenceCurrencyPriceInUsd: marketReferencePriceInUsd, - } = reservesData.baseCurrencyData; - return formatUserSummary({ - userEmodeCategoryId: selectedCategory?.id ?? 0, - currentTimestamp: timestamp, - marketReferencePriceInUsd, - marketReferenceCurrencyDecimals, - userReserves: userReservesData.userReserves, - formattedReserves: formatReserves({ - currentTimestamp: timestamp, - marketReferencePriceInUsd, - marketReferenceCurrencyDecimals, - reserves: reservesData.reservesData, - }), - }); - }, [userReservesData, reservesData, timestamp, selectedCategory?.id]); - - const newCollateralRatio = useMemo(() => { - if (!newSummary) { - return Decimal.from(0); - } - return AaveCalculations.computeCollateralRatio( - Decimal.from(newSummary.totalCollateralUSD), - Decimal.from(newSummary.totalBorrowsUSD), + const summaryAfterSwitch = useMemo(() => { + return normalizeEModeSummary( + selectedCategory?.id ?? 0, + reservesData, + userReservesData, + timestamp, ); - }, [newSummary]); + }, [userReservesData, reservesData, timestamp, selectedCategory?.id]); const positionsInOtherCategories = useMemo(() => { return summary.reserves.some( @@ -98,11 +71,11 @@ export const SwitchEModeForm: FC = ({ const confirmEnabled = useMemo(() => { // cannot switch if undercollateralized or have positions in other categories return ( - Decimal.from(newSummary?.healthFactor ?? 0).lte(1) && + !summaryAfterSwitch.liquidationRisk && !positionsInOtherCategories && selectedCategory ); - }, [newSummary, positionsInOtherCategories, selectedCategory]); + }, [summaryAfterSwitch, positionsInOtherCategories, selectedCategory]); const onConfirm = useCallback(() => { if (!selectedCategory) return; @@ -125,7 +98,7 @@ export const SwitchEModeForm: FC = ({
diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.constants.ts b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.constants.ts new file mode 100644 index 000000000..cfee00c96 --- /dev/null +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.constants.ts @@ -0,0 +1,17 @@ +import { t } from 'i18next'; + +import { translations } from '../../../../../../../locales/i18n'; + +export const tabItems = [ + { + activeClassName: 'text-primary-20', + dataAttribute: 'wallet-balance', + label: t(translations.aavePage.repayModal.walletBalance), + }, + { + activeClassName: 'text-primary-20', + dataAttribute: 'collateral', + label: t(translations.aavePage.common.collateral), + disabled: true, + }, +]; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx index e964088b5..fa3be9c91 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx @@ -1,10 +1,11 @@ -import React, { FC, useMemo, useState } from 'react'; +import React, { FC, useState } from 'react'; import { t } from 'i18next'; import { Paragraph, ParagraphSize, Tabs, TabType } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; +import { tabItems } from './RepayForm.constants'; import { RepayWithCollateralForm } from './RepayWithCollateralForm'; import { RepayWithWalletBalanceForm } from './RepayWithWalletBalanceForm'; @@ -21,22 +22,6 @@ enum RepayWith { export const RepayForm: FC = ({ asset, onComplete }) => { const [activeTab, setActiveTab] = useState(RepayWith.BALANCE); - const tabItems = useMemo(() => { - return [ - { - activeClassName: 'text-primary-20', - dataAttribute: 'wallet-balance', - label: t(translations.aavePage.repayModal.walletBalance), - }, - { - activeClassName: 'text-primary-20', - dataAttribute: 'collateral', - label: t(translations.aavePage.common.collateral), - disabled: true, - }, - ]; - }, []); - return (
= ({ const [orderOptions, setOrderOptions] = useState(); const [lendAssetDialog, setLendAssetDialog] = useState(); - const onLendClick = useCallback((asset: string) => { - setLendAssetDialog(asset); - }, []); - const onLendClose = useCallback(() => { setLendAssetDialog(undefined); }, []); const mobileRenderer = useCallback( - p => onLendClick(p.asset)} />, - [onLendClick], + p => ( + setLendAssetDialog(p.asset)} + /> + ), + [setLendAssetDialog], ); const rowTitleRenderer = useCallback( @@ -62,10 +63,10 @@ export const LendAssetsList: FC = ({ ); const filteredLendPools = useMemo(() => { - if (!showZeroBalances) { - return lendPools.filter(p => p.walletBalance.gt(0)); + if (showZeroBalances) { + return lendPools; } - return lendPools; + return lendPools.filter(p => p.walletBalance.gt(0)); }, [lendPools, showZeroBalances]); return ( @@ -89,7 +90,7 @@ export const LendAssetsList: FC = ({
= ({ }) => { const { account } = useAccount(); const { reserves } = useAaveReservesData(); - const [lendAsset, setLendAsset] = useState(initialAsset); + const [lendAsset, setLendAsset] = useState(initialAsset); const [lendAmount, setLendAmount, lendSize] = useDecimalAmountInput(''); const { balance: lendAssetBalance } = useAssetBalance( lendAsset, @@ -69,6 +69,10 @@ export const LendForm: FC = ({ return Decimal.from(reserve?.priceInUSD ?? 0).mul(lendSize); }, [reserve, lendSize]); + const supplyApy = useMemo(() => { + return Decimal.from(reserve?.supplyAPY ?? 0).mul(100); + }, [reserve]); + const isValidLendAmount = useMemo( () => (lendSize.gt(0) ? lendSize.lte(lendAssetBalance) : true), [lendSize, lendAssetBalance], @@ -112,13 +116,7 @@ export const LendForm: FC = ({ - } + value={} /> = ({ asset, onComplete }) => { const { handleWithdraw } = useAaveWithdraw(); const { summary } = useAaveUserReservesData(); - const [withdrawAsset, setWithdrawAsset] = useState(asset); + const [withdrawAsset, setWithdrawAsset] = useState(asset); const [withdrawAmount, setWithdrawAmount, withdrawSize] = useDecimalAmountInput(''); @@ -84,18 +85,6 @@ export const WithdrawForm: FC = ({ asset, onComplete }) => { [isValidWithdrawAmount, withdrawSize], ); - const tabItems = useMemo( - () => [ - // For now just withdraw is supported - { - activeClassName: 'text-primary-20', - dataAttribute: 'withdraw', - label: t(translations.common.withdraw), - }, - ], - [], - ); - const onConfirm = useCallback(() => { handleWithdraw( withdrawSize, diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx index f6ae1dc64..cc69a4756 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx @@ -78,9 +78,9 @@ const AaveReserveOverviewPage: FC = () => { 'lg:block space-y-4 flex-grow w-min', )} > - + - + diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx index 736e438f1..747f2f774 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx @@ -4,72 +4,32 @@ import { t } from 'i18next'; import { theme } from '@sovryn/tailwindcss-config'; import { Accordion, Link, Paragraph } from '@sovryn/ui'; -import { Decimal } from '@sovryn/utils'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; -import { config } from '../../../../../constants/aave'; import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; -import { FormattedReserveHistoryItem } from '../../../../../hooks/aave/useReservesHistory'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; -import { getBobExplorerUrl } from '../../../../../utils/helpers'; import { formatAmountWithSuffix } from '../../../../../utils/math'; +import { normalizeBorrowStats } from './BorrowDetailsGraph.utils'; import { Chart } from './components/Chart/Chart'; const pageTranslations = translations.aaveReserveOverviewPage.borrowDetails; type BorrowDetailsGraphProps = { - history: FormattedReserveHistoryItem[]; reserve: Reserve; }; export const BorrowDetailsGraph: FC = ({ reserve, - history, }) => { - const [open, setOpen] = useState(true); const { isMobile } = useIsMobile(); + const [open, setOpen] = useState(true); - const inputData = useMemo(() => { - const data = history.map(i => ({ - x: i.date, - y: i.variableBorrowRate * 100, - })); - return { - data, - label: t(pageTranslations.chart.label1), - lineColor: theme.colors['primary-30'], - }; - }, [history]); - - const totalBorrowed = useMemo(() => { - return Decimal.from(reserve.totalDebt); - }, [reserve]); - - const totalBorrowedUSD = useMemo(() => { - return Decimal.from(reserve.totalDebtUSD); - }, [reserve]); - - const debtCap = useMemo(() => { - return Decimal.from(reserve.debtCeiling); - }, [reserve]); - - const debtCapUSD = useMemo(() => { - return Decimal.from(reserve.debtCeilingUSD); + const borrowStats = useMemo(() => { + return normalizeBorrowStats(reserve); }, [reserve]); - const borrowedPercentage = useMemo(() => { - return Decimal.from(totalBorrowedUSD) - .div(Decimal.from(reserve.debtCeilingUSD)) - .mul(100) - .toString(0); - }, [reserve, totalBorrowedUSD]); - - const collectorContractLink = useMemo(() => { - return `${getBobExplorerUrl()}/address/${config.TreasuryAddress}`; - }, []); - return ( = ({
- {debtCap.gt(0) && ( + {borrowStats.borrowCap.gt(0) && ( <> {t(pageTranslations.of)} )} @@ -110,25 +70,27 @@ export const BorrowDetailsGraph: FC = ({ - {debtCap.gt(0) && ( + {borrowStats.borrowCap.gt(0) && ( <> {t(pageTranslations.of)} )}
{/* Progress bar */} - {debtCap.gt(0) && ( + {borrowStats.borrowCap.gt(0) && (
)} @@ -147,13 +109,13 @@ export const BorrowDetailsGraph: FC = ({ } /> - {debtCap.gt(0) && ( + {borrowStats.borrowCap.gt(0) && ( } /> @@ -161,7 +123,14 @@ export const BorrowDetailsGraph: FC = ({ - +
@@ -187,7 +156,7 @@ export const BorrowDetailsGraph: FC = ({ value={ } diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts index 82cbf0f72..66e576501 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts @@ -12,10 +12,9 @@ export const normalizeBorrowStats = (reserve: Reserve) => ({ borrowCapUSD: Decimal.from(reserve.borrowCapUSD), borrowedPercentage: Decimal.from(reserve.totalDebtUSD) .div(Decimal.from(reserve.debtCeilingUSD)) - .mul(100) - .toString(0), + .mul(100), + reserveFactor: Decimal.from(reserve.reserveFactor).mul(100), collectorContractLink: `${getBobExplorerUrl()}/address/${ config.TreasuryAddress }`, - reserveFactor: Decimal.from(reserve.reserveFactor).mul(100), }); diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx index bf051e141..77febba4a 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.tsx @@ -8,11 +8,11 @@ import { GRID_COLOR, TICK_COLOR, } from './Chart.constants'; -import { InputData } from './Chart.types'; +import { ChartData } from './Chart.types'; import { htmlLegendPlugin } from './Chart.utils'; type ChartProps = { - input: InputData; + input: ChartData; }; export const Chart: FC = ({ input }) => { diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.types.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.types.ts index 32de155a8..fdaeb8387 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.types.ts +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/components/Chart/Chart.types.ts @@ -1,5 +1,5 @@ -export type InputData = { - data: T[]; +export type ChartData = { + data: { x: number; y: number }[]; label: string; lineColor: string; }; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx index 0405ce4a0..0a1246913 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx @@ -4,16 +4,17 @@ import { t } from 'i18next'; import { theme } from '@sovryn/tailwindcss-config'; import { Accordion, Link } from '@sovryn/ui'; +import { Decimal } from '@sovryn/utils'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; import { config } from '../../../../../constants/aave'; -import { useAaveInterestRatesData } from '../../../../../hooks/aave/useAaveRates'; import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; import { getBobExplorerUrl } from '../../../../../utils/helpers'; import { Chart } from './components/Chart/Chart'; +import { RatesData } from './components/Chart/Chart.types'; const pageTranslations = translations.aaveReserveOverviewPage.interestRateModel; @@ -25,21 +26,18 @@ export const InterestRateModelGraph: FC = ({ reserve, }) => { const { isMobile } = useIsMobile(); + const [open, setOpen] = useState(true); + const interestRateStrategyUrl = useMemo(() => { return `${getBobExplorerUrl()}/address/${ config.InterestRateStrategyAddress }`; }, []); - const [open, setOpen] = useState(true); - const { data: rates } = useAaveInterestRatesData(); - - const meta = { - label: t(pageTranslations.chart.label1), - lineColor: theme.colors['primary-30'], - }; + const currentUsageRatio = useMemo(() => { + return Decimal.from(reserve.borrowUsageRatio).mul(100); + }, [reserve.borrowUsageRatio]); - if (!rates) return null; return ( = ({ flatMode={!isMobile} dataAttribute="interest-rate-model" > - {rates && ( -
-
- - } - /> - -
+
+
+ + } + /> + +
+ + - - {/* statistics */} -
- - } - /> - } - /> -
+ {/* statistics */} +
+ + } + /> + } + />
- )} +
); }; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx index 7b1fee072..8b4895318 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx @@ -5,12 +5,12 @@ import 'chartjs-adapter-date-fns'; import { theme } from '@sovryn/tailwindcss-config'; -import { RatesDataResult } from '../../../../../../../hooks/aave/useAaveRates'; import { CUSTOM_CANVAS_BACKGROUND_COLOR, GRID_COLOR, TICK_COLOR, } from './Chart.constants'; +import { RatesData } from './Chart.types'; import { htmlLegendPlugin } from './Chart.utils'; type ChartProps = { @@ -18,7 +18,7 @@ type ChartProps = { label: string; lineColor: string; }; - rates: RatesDataResult; + rates: RatesData; }; const calcInterestRateModel = ( @@ -35,7 +35,7 @@ const calcInterestRateModel = ( return base + slope1 + ((u - optimal) / (1 - optimal)) * slope2; }; -const calcVariableInterestRateModel = (u: number, rates: RatesDataResult) => { +const calcVariableInterestRateModel = (u: number, rates: RatesData) => { const base = parseFloat(rates.baseVariableBorrowRate); const optimal = parseFloat(rates.optimalUsageRatio); const slope1 = parseFloat(rates.variableRateSlope1); diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.types.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.types.ts index cc3d258c6..8a45374fa 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.types.ts +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.types.ts @@ -6,3 +6,15 @@ export type MockData = { lineColor: string; xLabels: string[]; }; + +export interface RatesData { + currentUsageRatio: string; + optimalUsageRatio: string; + baseVariableBorrowRate: string; + variableRateSlope1: string; + variableRateSlope2: string; + underlyingAsset: string; + name: string; + symbol: string; + decimals: string; +} diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx index 3e1ae6e76..4678feea7 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx @@ -10,7 +10,6 @@ import { Decimal } from '@sovryn/utils'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; -import { FormattedReserveHistoryItem } from '../../../../../hooks/aave/useReservesHistory'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; import { formatAmountWithSuffix } from '../../../../../utils/math'; @@ -19,26 +18,15 @@ import { Chart } from './components/Chart/Chart'; const pageTranslations = translations.aaveReserveOverviewPage.supplyDetails; type SupplyDetailsGraphProps = { - history: FormattedReserveHistoryItem[]; reserve: Reserve; }; export const SupplyDetailsGraph: FC = ({ - history, reserve, }) => { const [open, setOpen] = useState(true); const { isMobile } = useIsMobile(); - const inputData = useMemo(() => { - const data = history.map(i => ({ x: i.date, y: i.liquidityRate * 100 })); - return { - data, - label: t(pageTranslations.chart.label1), - lineColor: theme.colors['primary-30'], - }; - }, [history]); - const totalSupplied = useMemo(() => { return Decimal.from(reserve.availableLiquidity).add(reserve.totalDebt); }, [reserve]); @@ -146,7 +134,14 @@ export const SupplyDetailsGraph: FC = ({ />
- +
{/* collateral usage */} diff --git a/apps/frontend/src/hooks/aave/constants.ts b/apps/frontend/src/hooks/aave/constants.ts deleted file mode 100644 index 596d3e9b9..000000000 --- a/apps/frontend/src/hooks/aave/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const BIG_NUMBER_PRECISION_TWENTY_SEVEN = 27; -export const BIG_NUMBER_PRECISION_EIGHTEEN = 18; diff --git a/apps/frontend/src/hooks/aave/useAaveRates.tsx b/apps/frontend/src/hooks/aave/useAaveRates.tsx deleted file mode 100644 index 9e5bebd29..000000000 --- a/apps/frontend/src/hooks/aave/useAaveRates.tsx +++ /dev/null @@ -1,300 +0,0 @@ -import { useEffect, useMemo, useState } from 'react'; - -import { Contract, utils } from 'ethers'; -import { formatUnits } from 'ethers/lib/utils'; -import { useSearchParams } from 'react-router-dom'; - -import { useAccount } from '../useAccount'; -import { - BIG_NUMBER_PRECISION_EIGHTEEN, - BIG_NUMBER_PRECISION_TWENTY_SEVEN, -} from './constants'; -import { Reserve, useAaveReservesData } from './useAaveReservesData'; - -const INTEREST_RATE_STRATEGY_ABI = [ - { - inputs: [ - { - internalType: 'contract IPoolAddressesProvider', - name: 'provider', - type: 'address', - }, - { - internalType: 'uint256', - name: 'optimalUsageRatio', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'baseVariableBorrowRate', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'variableRateSlope1', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'variableRateSlope2', - type: 'uint256', - }, - ], - stateMutability: 'nonpayable', - type: 'constructor', - }, - { - inputs: [], - name: 'ADDRESSES_PROVIDER', - outputs: [ - { - internalType: 'contract IPoolAddressesProvider', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'MAX_EXCESS_USAGE_RATIO', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'OPTIMAL_USAGE_RATIO', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - components: [ - { - internalType: 'uint256', - name: 'unbacked', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'liquidityAdded', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'liquidityTaken', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'totalVariableDebt', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'reserveFactor', - type: 'uint256', - }, - { - internalType: 'address', - name: 'reserve', - type: 'address', - }, - { - internalType: 'address', - name: 'aToken', - type: 'address', - }, - ], - internalType: 'struct DataTypes.CalculateInterestRatesParams', - name: 'params', - type: 'tuple', - }, - ], - name: 'calculateInterestRates', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'getBaseVariableBorrowRate', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'getMaxVariableBorrowRate', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'getVariableRateSlope1', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'getVariableRateSlope2', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, -]; - -export interface RatesDataResult { - currentUsageRatio: string; - optimalUsageRatio: string; - baseVariableBorrowRate: string; - variableRateSlope1: string; - variableRateSlope2: string; - underlyingAsset: string; - name: string; - symbol: string; - decimals: string; -} - -const calculateUtilizationRate = ( - decimals: number, - totalDebt: string, - availableLiquidity: string, -): bigint => { - // Create BigNumber instances - const totalBorrow = BigInt(utils.parseUnits(totalDebt, decimals).toString()); - const totalSupply = BigInt(availableLiquidity) + totalBorrow; - // Perform division - return (totalBorrow * BigInt(10 ** 18)) / totalSupply; -}; - -export const useAaveInterestRatesData = (): { - data: RatesDataResult | null; - error: string | null; -} => { - const [data, setData] = useState(null); - const [error, setError] = useState(null); - - const { provider } = useAccount(); - - const [searchParams] = useSearchParams(); - const symbol = searchParams.get('asset') || 'ETH'; - const { reserves, loading } = useAaveReservesData(); - const reserve: Reserve | undefined = reserves.find( - r => r.symbol.toLocaleLowerCase() === symbol.toLocaleLowerCase(), - ); - const interestRateStrategyAddress = reserve?.interestRateStrategyAddress; - - const rateContract = useMemo( - () => - provider && interestRateStrategyAddress && !loading - ? new Contract( - interestRateStrategyAddress, - INTEREST_RATE_STRATEGY_ABI, - provider, - ) - : null, - [loading, interestRateStrategyAddress, provider], - ); - - useEffect(() => { - if (loading || !rateContract || !reserve) return; - let ratesData: RatesDataResult; - try { - const utilizationRate = calculateUtilizationRate( - reserve.decimals, - reserve.totalDebt, - reserve.availableLiquidity, - ); - ratesData = { - currentUsageRatio: formatUnits( - utilizationRate, - BIG_NUMBER_PRECISION_EIGHTEEN, - ), - optimalUsageRatio: formatUnits( - reserve.optimalUsageRatio, - BIG_NUMBER_PRECISION_TWENTY_SEVEN, - ).toString(), - baseVariableBorrowRate: formatUnits( - reserve.baseVariableBorrowRate, - BIG_NUMBER_PRECISION_TWENTY_SEVEN, - ).toString(), - variableRateSlope1: formatUnits( - reserve.variableRateSlope1, - BIG_NUMBER_PRECISION_TWENTY_SEVEN, - ).toString(), - variableRateSlope2: formatUnits( - reserve.variableRateSlope2, - BIG_NUMBER_PRECISION_TWENTY_SEVEN, - ).toString(), - underlyingAsset: reserve.underlyingAsset, - name: reserve.name, - symbol: reserve.symbol, - decimals: reserve.decimals.toString(), - }; - setData(ratesData); - } catch (error) { - setError(error.message); - } - }, [symbol, loading, reserve, rateContract]); - - return { data, error }; -}; diff --git a/apps/frontend/src/hooks/aave/useReservesHistory.tsx b/apps/frontend/src/hooks/aave/useReservesHistory.tsx deleted file mode 100644 index f35fb0c5d..000000000 --- a/apps/frontend/src/hooks/aave/useReservesHistory.tsx +++ /dev/null @@ -1,173 +0,0 @@ -/** - * This hook is used for getting historical reserve data, and it is primarily used with charts. - * In particular, this hook is used in the ̶A̶p̶y̶G̶r̶a̶p̶h̶ AaveReserveOverviewPage (chartJS). - */ -import { useCallback, useEffect, useState } from 'react'; - -import dayjs from 'dayjs'; - -import { config } from '../../constants/aave'; -import { makeCancelable } from './utils'; - -export enum ESupportedTimeRanges { - OneMonth = '1m', - ThreeMonths = '3m', - SixMonths = '6m', - OneYear = '1y', - TwoYears = '2y', - FiveYears = '5y', -} - -export const reserveRateTimeRangeOptions = [ - ESupportedTimeRanges.OneMonth, - ESupportedTimeRanges.SixMonths, - ESupportedTimeRanges.OneYear, -]; -export type ReserveRateTimeRange = typeof reserveRateTimeRangeOptions[number]; - -type RatesHistoryParams = { - from: number; - resolutionInHours: number; -}; - -type APIResponse = { - liquidityRate_avg: number; - variableBorrowRate_avg: number; - stableBorrowRate_avg: number; - utilizationRate_avg: number; - x: { year: number; month: number; date: number; hours: number }; -}; - -const fetchStats = async ( - address: string, - timeRange: ReserveRateTimeRange, - endpointURL: string, -): Promise => { - const { from, resolutionInHours } = resolutionForTimeRange(timeRange); - - try { - const url = `${endpointURL}?reserveId=${address}&from=${from}&resolutionInHours=${resolutionInHours}`; - const result = await fetch(url); - const json = await result.json(); - return json; - } catch (e) { - return []; - } -}; - -// TODO: there is possibly a bug here, as Polygon and Avalanche v2 data is coming through empty and erroring in our hook -// The same asset without the 'from' field comes through just fine. -const resolutionForTimeRange = ( - timeRange: ReserveRateTimeRange, -): RatesHistoryParams => { - // Return today as a fallback - let calculatedDate = dayjs().unix(); - switch (timeRange) { - case ESupportedTimeRanges.OneMonth: - calculatedDate = dayjs().subtract(30, 'day').unix(); - return { - from: calculatedDate, - resolutionInHours: 6, - }; - case ESupportedTimeRanges.SixMonths: - calculatedDate = dayjs().subtract(6, 'month').unix(); - return { - from: calculatedDate, - resolutionInHours: 24, - }; - case ESupportedTimeRanges.OneYear: - calculatedDate = dayjs().subtract(1, 'year').unix(); - return { - from: calculatedDate, - resolutionInHours: 24, - }; - default: - return { - from: calculatedDate, - resolutionInHours: 6, - }; - } -}; - -export type FormattedReserveHistoryItem = { - date: number; - liquidityRate: number; - utilizationRate: number; - stableBorrowRate: number; - variableBorrowRate: number; -}; - -// TODO: api need to be altered to expect chainId underlying asset and poolConfig -/** - * - * @param reserveAddress - * @param timeRange - * @returns - */ -export function useReserveRatesHistory( - reserveAddress: string, - timeRange: ReserveRateTimeRange, -) { - const [loading, setLoading] = useState(true); - const [error, setError] = useState(false); - const [data, setData] = useState([]); - - const ratesHistoryApiUrl = config.ratesHistoryApiUrl; - - const refetchData = useCallback<() => () => void>(() => { - // reset - setLoading(true); - setError(false); - setData([]); - - if (reserveAddress && ratesHistoryApiUrl) { - const cancelable = makeCancelable( - fetchStats(reserveAddress, timeRange, ratesHistoryApiUrl), - ); - - cancelable.promise - .then((data: APIResponse[]) => { - setData( - data.map(d => ({ - date: new Date( - d.x.year, - d.x.month, - d.x.date, - d.x.hours, - ).getTime(), - liquidityRate: d.liquidityRate_avg, - variableBorrowRate: d.variableBorrowRate_avg, - utilizationRate: d.utilizationRate_avg, - stableBorrowRate: d.stableBorrowRate_avg, - })), - ); - setLoading(false); - }) - .catch(e => { - console.error( - 'useReservesHistory(): Failed to fetch historical reserve data.', - e, - ); - setError(true); - setLoading(false); - }); - - return cancelable.cancel; - } - - setLoading(false); - return () => null; - }, [reserveAddress, timeRange, ratesHistoryApiUrl]); - - useEffect(() => { - const cancel = refetchData(); - return () => cancel(); - }, [refetchData]); - - return { - loading, - data, - error, - refetch: refetchData, - }; -} diff --git a/apps/frontend/src/hooks/aave/utils.ts b/apps/frontend/src/hooks/aave/utils.ts deleted file mode 100644 index 13bbb98f4..000000000 --- a/apps/frontend/src/hooks/aave/utils.ts +++ /dev/null @@ -1,22 +0,0 @@ -export interface CancelablePromise { - promise: Promise; - cancel: () => void; -} - -export const makeCancelable = (promise: Promise) => { - let hasCanceled_ = false; - - const wrappedPromise = new Promise((resolve, reject) => { - promise.then( - val => (hasCanceled_ ? reject({ isCanceled: true }) : resolve(val)), - error => (hasCanceled_ ? reject({ isCanceled: true }) : reject(error)), - ); - }); - - return { - promise: wrappedPromise, - cancel() { - hasCanceled_ = true; - }, - }; -}; diff --git a/apps/frontend/src/hooks/useAddTokenToWallet.tsx b/apps/frontend/src/hooks/useAddTokenToWallet.tsx index 533a347b2..76c6dba32 100644 --- a/apps/frontend/src/hooks/useAddTokenToWallet.tsx +++ b/apps/frontend/src/hooks/useAddTokenToWallet.tsx @@ -14,7 +14,7 @@ export const useAddTokenToWallet = (): { throw new Error('Wallet not available'); } - (window as any)?.ethereum.request({ + (window as any).ethereum.request({ method: 'wallet_watchAsset', params: { type: 'ERC20', diff --git a/apps/frontend/src/hooks/useNotifyError.tsx b/apps/frontend/src/hooks/useNotifyError.tsx index 1e8fed183..e32e34a1f 100644 --- a/apps/frontend/src/hooks/useNotifyError.tsx +++ b/apps/frontend/src/hooks/useNotifyError.tsx @@ -12,10 +12,10 @@ export const useNotifyError = () => { const { addNotification } = useNotificationContext(); const notifyError = useCallback( - (error: Error) => { + (error: Error, title?: string) => { addNotification({ type: NotificationType.error, - title: t(translations.common.somethingWentWrong), + title: title ?? t(translations.common.somethingWentWrong), content: error.message, dismissible: true, id: nanoid(), diff --git a/yarn.lock b/yarn.lock index b6125fd33..d72a13f05 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,14 +7,14 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@aave/contract-helpers@^1.29.1": +"@aave/contract-helpers@1.29.1": version "1.29.1" resolved "https://registry.yarnpkg.com/@aave/contract-helpers/-/contract-helpers-1.29.1.tgz#34beab8afa35bbfc6168c78ced8317d96a493fe0" integrity sha512-34z5CKpNdEx26G+DSezovdR3PyAf0v0Typbf2udaKG4GrOBgqbKqxr4zqS/dpFO5aAQJJ+nuEM19lKRK4sMcpg== dependencies: isomorphic-unfetch "^3.1.0" -"@aave/math-utils@^1.29.1": +"@aave/math-utils@1.29.1": version "1.29.1" resolved "https://registry.yarnpkg.com/@aave/math-utils/-/math-utils-1.29.1.tgz#fcb9499bd472bde7b80429c7dbe63d4358852697" integrity sha512-a+L2+vjza/nH4BxUTzwDcoJH/Qr6UP+g+9YSwG7YKUgoVfdy8gJs5VyXjfDDEVe9lMeZuPJq7CqrgV4P2M6Nkg== @@ -21052,7 +21052,7 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -reflect-metadata@^0.2.2: +reflect-metadata@0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== From 8db5467c5ad7fa35de74a4d1f42473d378d5ff89 Mon Sep 17 00:00:00 2001 From: matzapata Date: Fri, 6 Sep 2024 19:40:24 +0300 Subject: [PATCH 082/116] cleanup --- .../InterestRateModelGraph.tsx | 12 +++- .../components/Chart/Chart.constants.ts | 2 + .../components/Chart/Chart.tsx | 33 ++--------- .../components/Chart/Chart.utils.ts | 28 +++++++++ .../SupplyDetailsGraph/SupplyDetailsGraph.tsx | 57 +++++++------------ .../components/TopPanel/TopPanel.tsx | 30 +--------- .../WalletOverview/WalletOverview.tsx | 10 ++-- .../components/BorrowAction/BorrowAction.tsx | 8 ++- apps/frontend/src/constants/aave.ts | 1 - .../src/hooks/aave/useAaveReservesData.tsx | 3 +- .../src/hooks/useAddTokenToWallet.tsx | 10 ++-- .../aave/AaveBorrowTransactionsFactory.ts | 4 +- .../src/utils/aave/AaveCalculations.ts | 9 --- 13 files changed, 93 insertions(+), 114 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx index 0a1246913..2d91d245e 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx @@ -34,6 +34,10 @@ export const InterestRateModelGraph: FC = ({ }`; }, []); + const collectorContractUrl = useMemo(() => { + return `${getBobExplorerUrl()}/address/${config.TreasuryAddress}`; + }, []); + const currentUsageRatio = useMemo(() => { return Decimal.from(reserve.borrowUsageRatio).mul(100); }, [reserve.borrowUsageRatio]); @@ -94,7 +98,13 @@ export const InterestRateModelGraph: FC = ({ /> } + value={ + + } />
diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts index b5fc9efd5..1616f5f2f 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.constants.ts @@ -2,6 +2,8 @@ import { theme } from '@sovryn/tailwindcss-config'; export const GRID_COLOR = '#484d59'; export const TICK_COLOR = '#b6bac1'; +export const CHART_PERCENTAGES = [0, 0.25, 0.5, 0.75, 1]; + const SM_BREAKPOINT = parseInt(theme.screens.sm, 10) || 576; export const CUSTOM_CANVAS_BACKGROUND_COLOR = { diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx index 8b4895318..a31914864 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.tsx @@ -6,12 +6,16 @@ import 'chartjs-adapter-date-fns'; import { theme } from '@sovryn/tailwindcss-config'; import { + CHART_PERCENTAGES, CUSTOM_CANVAS_BACKGROUND_COLOR, GRID_COLOR, TICK_COLOR, } from './Chart.constants'; import { RatesData } from './Chart.types'; -import { htmlLegendPlugin } from './Chart.utils'; +import { + calculateVariableInterestRateModel, + htmlLegendPlugin, +} from './Chart.utils'; type ChartProps = { meta: { @@ -21,30 +25,6 @@ type ChartProps = { rates: RatesData; }; -const calcInterestRateModel = ( - u: number, - base: number, - optimal: number, - slope1: number, - slope2: number, -) => { - if (u === 0) return 0; - - if (u <= optimal) return base + (u / optimal) * slope1; - - return base + slope1 + ((u - optimal) / (1 - optimal)) * slope2; -}; - -const calcVariableInterestRateModel = (u: number, rates: RatesData) => { - const base = parseFloat(rates.baseVariableBorrowRate); - const optimal = parseFloat(rates.optimalUsageRatio); - const slope1 = parseFloat(rates.variableRateSlope1); - const slope2 = parseFloat(rates.variableRateSlope2); - - return calcInterestRateModel(u, base, optimal, slope1, slope2); -}; -const CHART_PERCENTAGES = [0, 0.25, 0.5, 0.75, 1]; - export const Chart: FC = ({ meta, rates }) => { const canvas = useRef(null); const chartRef = useRef(null); @@ -53,7 +33,7 @@ export const Chart: FC = ({ meta, rates }) => { () => CHART_PERCENTAGES.map(x => ({ x: x * 100, - y: calcVariableInterestRateModel(x, rates) * 100, + y: calculateVariableInterestRateModel(x, rates) * 100, })), [rates], ); @@ -136,7 +116,6 @@ export const Chart: FC = ({ meta, rates }) => { callback: function (value) { return value + '%'; }, - //maxTicksLimit: 5, }, }, y: { diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.utils.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.utils.ts index 40a303ffa..28f4bf540 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.utils.ts +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/components/Chart/Chart.utils.ts @@ -1,3 +1,5 @@ +import { RatesData } from './Chart.types'; + const getOrCreateLegendList = id => { const legendContainer = document.getElementById(id); if (!legendContainer) { @@ -87,3 +89,29 @@ export const htmlLegendPlugin = { }); }, }; + +export const calculateInterestRateModel = ( + u: number, + base: number, + optimal: number, + slope1: number, + slope2: number, +) => { + if (u === 0) return 0; + + if (u <= optimal) return base + (u / optimal) * slope1; + + return base + slope1 + ((u - optimal) / (1 - optimal)) * slope2; +}; + +export const calculateVariableInterestRateModel = ( + u: number, + rates: RatesData, +) => { + const base = parseFloat(rates.baseVariableBorrowRate); + const optimal = parseFloat(rates.optimalUsageRatio); + const slope1 = parseFloat(rates.variableRateSlope1); + const slope2 = parseFloat(rates.variableRateSlope2); + + return calculateInterestRateModel(u, base, optimal, slope1, slope2); +}; diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx index 4678feea7..b77fc7d08 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx @@ -5,7 +5,6 @@ import { t } from 'i18next'; import { theme } from '@sovryn/tailwindcss-config'; import { Accordion, Icon, IconNames, Paragraph } from '@sovryn/ui'; -import { Decimal } from '@sovryn/utils'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; @@ -13,6 +12,7 @@ import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; import { formatAmountWithSuffix } from '../../../../../utils/math'; +import { normalizeSupplyStats } from './SupplyDetailsGraph.utils'; import { Chart } from './components/Chart/Chart'; const pageTranslations = translations.aaveReserveOverviewPage.supplyDetails; @@ -27,33 +27,8 @@ export const SupplyDetailsGraph: FC = ({ const [open, setOpen] = useState(true); const { isMobile } = useIsMobile(); - const totalSupplied = useMemo(() => { - return Decimal.from(reserve.availableLiquidity).add(reserve.totalDebt); - }, [reserve]); - - const totalSuppliedUSD = useMemo(() => { - return Decimal.from(reserve.availableLiquidityUSD).add( - reserve.totalDebtUSD, - ); - }, [reserve]); - - const supplyCap = useMemo(() => { - return Decimal.from(reserve.supplyCap); - }, [reserve]); - - const supplyCapUSD = useMemo(() => { - return Decimal.from(reserve.supplyCapUSD); - }, [reserve]); - - const suppliedPercentage = useMemo(() => { - return Decimal.from(totalSuppliedUSD) - .div(Decimal.from(reserve.supplyCapUSD)) - .mul(100) - .toString(0); - }, [reserve, totalSuppliedUSD]); - - const maxLTV = useMemo(() => { - return Decimal.from(reserve.baseLTVasCollateral).div(100); + const supplyStats = useMemo(() => { + return normalizeSupplyStats(reserve); }, [reserve]); return ( @@ -80,14 +55,14 @@ export const SupplyDetailsGraph: FC = ({
- {supplyCap.gt(0) && ( + {supplyStats.supplyCap.gt(0) && ( <> {t(pageTranslations.of)} )} @@ -97,25 +72,27 @@ export const SupplyDetailsGraph: FC = ({ - {supplyCap.gt(0) && ( + {supplyStats.supplyCap.gt(0) && ( <> {t(pageTranslations.of)} )}
{/* Progress bar */} - {supplyCap.gt(0) && ( + {supplyStats.supplyCap.gt(0) && (
)} @@ -178,7 +155,13 @@ export const SupplyDetailsGraph: FC = ({ } + value={ + + } /> {/* liquidation threshold */} diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx index 82bdf38bf..e78e7d8d9 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/TopPanel.tsx @@ -22,7 +22,7 @@ import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRen import { AssetRenderer } from '../../../../2_molecules/AssetRenderer/AssetRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; -import { useNotifyError } from '../../../../../hooks/useNotifyError'; +import { useAddTokenToWallet } from '../../../../../hooks/useAddTokenToWallet'; import { translations } from '../../../../../locales/i18n'; import { getBobExplorerUrl } from '../../../../../utils/helpers'; import { formatAmountWithSuffix } from '../../../../../utils/math'; @@ -50,37 +50,13 @@ type TopPanelProps = { }; export const TopPanel: FC = ({ reserve, className }) => { - const { notifyError } = useNotifyError(); + const { addTokenToWallet } = useAddTokenToWallet(BOB_CHAIN_ID); const openInExplorer = useCallback((tokenAddress: string) => { const explorer = getBobExplorerUrl(); window.open(`${explorer}/address/${tokenAddress}`, '_blank'); }, []); - const addToWallet = useCallback( - (token: string) => { - try { - if (!(window as any)?.ethereum) { - throw new Error('Wallet not available'); - } - - (window as any)?.ethereum.request({ - method: 'wallet_watchAsset', - params: { - type: 'ERC20', - options: { - chainId: BOB_CHAIN_ID, - address: token, - }, - }, - }); - } catch (error) { - notifyError(error); - } - }, - [notifyError], - ); - const oracleLink = useMemo(() => { return getBobExplorerUrl() + '/address/' + reserve.priceOracle; }, [reserve.priceOracle]); @@ -157,7 +133,7 @@ export const TopPanel: FC = ({ reserve, className }) => { underlyingTokenAddress={reserve.underlyingAsset} variableDebtTokenAddress={reserve.variableDebtTokenAddress} stableDebtTokenAddress={reserve.stableDebtTokenAddress} - onTokenClick={addToWallet} + onTokenClick={addTokenToWallet} /> } > diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/WalletOverview.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/WalletOverview.tsx index ef9b931e6..cc36fcced 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/WalletOverview.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/WalletOverview/WalletOverview.tsx @@ -28,7 +28,7 @@ type WalletOverviewProps = { }; export const WalletOverview: FC = ({ symbol }) => { - const [asset, setAsset] = useState(); + const [asset, setAsset] = useState(); const { account, connectWallet, pending } = useWalletConnect(); const { summary } = useAaveUserReservesData(); @@ -40,6 +40,10 @@ export const WalletOverview: FC = ({ symbol }) => { return summary.reserves.find(r => r.reserve.symbol === symbol); }, [summary, symbol]); + const supplyCapReached = useMemo(() => { + return Decimal.from(reserveSummary?.reserve.supplyUsageRatio ?? 0).gte(1); + }, [reserveSummary?.reserve.supplyUsageRatio]); + return (
@@ -97,9 +101,7 @@ export const WalletOverview: FC = ({ symbol }) => { /> )} - {Decimal.from(reserveSummary?.reserve.supplyUsageRatio ?? 0).gte( - 1, - ) && ( + {supplyCapReached && ( = ({ setOpen(true); }, []); + const isBorrowDisabled = useMemo(() => { + return availableToBorrow.lte(0); + }, [availableToBorrow]); + return (
@@ -64,7 +68,7 @@ export const BorrowAction: FC = ({
); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx index b56df01bd..fb1a84ad4 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx @@ -8,6 +8,7 @@ import { ErrorBadge, ErrorLevel, Link, + SelectOption, SimpleTable, SimpleTableRow, } from '@sovryn/ui'; @@ -58,7 +59,7 @@ export const BorrowForm: FC = ({ asset, onComplete }) => { }); } return acc; - }, [] as { value: string; label: JSX.Element }[]), + }, [] as SelectOption[]), [summary.reserves], ); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx index cca226a60..de59e60d3 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx @@ -14,7 +14,10 @@ import { import { Decimal } from '@sovryn/utils'; import { AmountRenderer } from '../../../../../../../../2_molecules/AmountRenderer/AmountRenderer'; -import { config } from '../../../../../../../../../constants/aave'; +import { + config, + EMODE_DISABLED_ID, +} from '../../../../../../../../../constants/aave'; import { useAaveSetUserEMode } from '../../../../../../../../../hooks/aave/useAaveSetUserEMode'; import { useAaveUserReservesData } from '../../../../../../../../../hooks/aave/useAaveUserReservesData'; import { translations } from '../../../../../../../../../locales/i18n'; @@ -41,7 +44,7 @@ export const DisableEModeForm: FC = ({ const summaryAfterDisabled = useMemo(() => { return normalizeEModeSummary( - 0, // disabled + EMODE_DISABLED_ID, reservesData, userReservesData, timestamp, diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx index f14b941a7..02538672a 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx @@ -12,7 +12,7 @@ import { LendAssetAction } from '../LendAssetAction/LendAssetAction'; type LendAssetDetailsProps = { pool: LendPoolDetails; - onLendClick: () => unknown; + onLendClick: () => void; }; export const LendAssetDetails: FC = ({ diff --git a/apps/frontend/src/constants/aave.ts b/apps/frontend/src/constants/aave.ts index 653a4e14a..feacb47a1 100644 --- a/apps/frontend/src/constants/aave.ts +++ b/apps/frontend/src/constants/aave.ts @@ -12,3 +12,5 @@ export const config = { InterestRateStrategyAddress: '0x847A3364Cc5fE389283bD821cfC8A477288D9e82', RepayAllETHSurplus: '1000000', }; + +export const EMODE_DISABLED_ID = 0; From a6054bd4132cd728d77bd3a822bf46ce0bd57ac3 Mon Sep 17 00:00:00 2001 From: matzapata Date: Mon, 9 Sep 2024 11:42:56 +0400 Subject: [PATCH 091/116] fix borrow power --- .../app/5_pages/AavePage/AavePage.utils.tsx | 32 ++++++++++++------- .../src/utils/aave/AaveUserReservesSummary.ts | 8 ++++- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx b/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx index 5b01426ef..b7ef0d1fe 100644 --- a/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx @@ -82,21 +82,29 @@ export const normalizeBorrowPoolDetails = ( }, [] as BorrowPoolDetails[]); } else { return reserves.reduce((acc, r) => { + // skip borrow pools that are not enabled + if (!r.borrowingEnabled) { + return acc; + } + + // skip borrow pools that are not enabled for the user if ( - r.borrowingEnabled && - (userReservesSummary.eModeCategoryId === r.eModeCategoryId || - userReservesSummary.eModeCategoryId === 0) + userReservesSummary.eModeCategoryId !== r.eModeCategoryId && + userReservesSummary.eModeCategoryId !== 0 ) { - acc.push({ - asset: r.symbol, - apy: Decimal.from(r.variableBorrowAPY).mul(100), - available: - userReservesSummary.reserves.find( - userReserve => r.symbol === userReserve.asset, - )?.availableToBorrow || Decimal.from(0), - availableUSD: userReservesSummary.borrowPower, - }); + return acc; } + + const userSummary = userReservesSummary.reserves.find( + userReserve => r.symbol === userReserve.asset, + ); + acc.push({ + asset: r.symbol, + apy: Decimal.from(r.variableBorrowAPY).mul(100), + available: userSummary?.availableToBorrow, + availableUSD: userSummary?.availableToBorrowUSD, + }); + return acc; }, [] as BorrowPoolDetails[]); } diff --git a/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts b/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts index f28073a47..0dfeaaf7b 100644 --- a/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts +++ b/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts @@ -204,10 +204,16 @@ export class AaveUserReservesSummaryFactory { const asset = await getAssetData(symbol, BOB_CHAIN_ID); const balance = await getBalance(asset, account, provider); const decimalBalance = decimalic(fromWei(balance, asset.decimals)); - const canBorrow = borrowPower.div(r.reserve.priceInUSD); const availableLiquidity = Decimal.from( formatUnits(r.reserve.availableLiquidity, r.reserve.decimals), ); + + // how much the user can borrow if there's no limit of supply + const canBorrow = borrowPower + .sub(borrowBalance) + .div(r.reserve.priceInUSD); + + // available to borrow for user including liquidity limitation const availableToBorrow = availableLiquidity.lt(canBorrow) ? availableLiquidity : canBorrow; From b3b476473ab3c83669dd75230eba0f5f46cfcd17 Mon Sep 17 00:00:00 2001 From: matzapata Date: Mon, 9 Sep 2024 11:49:54 +0400 Subject: [PATCH 092/116] fixes and cleanup --- .../app/5_pages/AavePage/AavePage.utils.tsx | 69 ++++++++++--------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx b/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx index b7ef0d1fe..51026f5fe 100644 --- a/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx @@ -2,6 +2,7 @@ import { t } from 'i18next'; import { Decimal } from '@sovryn/utils'; +import { EMODE_DISABLED_ID } from '../../../constants/aave'; import { Reserve } from '../../../hooks/aave/useAaveReservesData'; import { translations } from '../../../locales/i18n'; import { BorrowRateMode } from '../../../types/aave'; @@ -31,7 +32,7 @@ export const normalizeLendPositions = ( if (r.supplied.gt(0)) { acc.push({ asset: r.reserve.symbol, - apy: Decimal.from(r.reserve.variableBorrowAPY), + apy: Decimal.from(r.reserve.supplyAPY).mul(100), supplied: r.supplied, suppliedUSD: r.suppliedUSD, collateral: r.collateral, @@ -70,6 +71,7 @@ export const normalizeBorrowPoolDetails = ( reserves: Reserve[], userReservesSummary: AaveUserReservesSummary, ): BorrowPoolDetails[] => { + // wallet not connected or summary not available at this point if (userReservesSummary.reserves.length === 0) { return reserves.reduce((acc, r) => { if (r.borrowingEnabled) { @@ -80,40 +82,41 @@ export const normalizeBorrowPoolDetails = ( } return acc; }, [] as BorrowPoolDetails[]); - } else { - return reserves.reduce((acc, r) => { - // skip borrow pools that are not enabled - if (!r.borrowingEnabled) { - return acc; - } - - // skip borrow pools that are not enabled for the user - if ( - userReservesSummary.eModeCategoryId !== r.eModeCategoryId && - userReservesSummary.eModeCategoryId !== 0 - ) { - return acc; - } + } - const userSummary = userReservesSummary.reserves.find( - userReserve => r.symbol === userReserve.asset, - ); - acc.push({ - asset: r.symbol, - apy: Decimal.from(r.variableBorrowAPY).mul(100), - available: userSummary?.availableToBorrow, - availableUSD: userSummary?.availableToBorrowUSD, - }); + return reserves.reduce((acc, r) => { + // skip borrow pools that are not enabled + if (!r.borrowingEnabled) { + return acc; + } + // skip borrow pools that are not enabled for the user + if ( + userReservesSummary.eModeCategoryId !== r.eModeCategoryId && + userReservesSummary.eModeCategoryId !== EMODE_DISABLED_ID + ) { return acc; - }, [] as BorrowPoolDetails[]); - } + } + + const userSummary = userReservesSummary.reserves.find( + userReserve => r.symbol === userReserve.asset, + ); + acc.push({ + asset: r.symbol, + apy: Decimal.from(r.variableBorrowAPY).mul(100), + available: userSummary?.availableToBorrow, + availableUSD: userSummary?.availableToBorrowUSD, + }); + + return acc; + }, [] as BorrowPoolDetails[]); }; export const normalizeLendPoolDetails = ( reserves: Reserve[], userReservesSummary: AaveUserReservesSummary, ): LendPoolDetails[] => { + // wallet not connected or summary not available at this point if (userReservesSummary.reserves.length === 0) { return reserves.map(r => ({ asset: r.symbol, @@ -121,12 +124,12 @@ export const normalizeLendPoolDetails = ( canBeCollateral: r.usageAsCollateralEnabled, walletBalance: Decimal.from(0), })); - } else { - return userReservesSummary.reserves.map(r => ({ - asset: r.reserve.symbol, - apy: Decimal.from(r.reserve.supplyAPY).mul(100), - canBeCollateral: r.reserve.usageAsCollateralEnabled, - walletBalance: r.walletBalance, - })); } + + return userReservesSummary.reserves.map(r => ({ + asset: r.reserve.symbol, + apy: Decimal.from(r.reserve.supplyAPY).mul(100), + canBeCollateral: r.reserve.usageAsCollateralEnabled, + walletBalance: r.walletBalance, + })); }; From b58373a31a891d3f03d2debec53f5d90f4fc2e57 Mon Sep 17 00:00:00 2001 From: Matias Zapata <42716817+matzapata@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:50:32 +0300 Subject: [PATCH 093/116] Fixes and cleanup (#48) * coments * fix borrow power * fixes and cleanup --- .../app/5_pages/AavePage/AavePage.utils.tsx | 65 +++++++++++-------- .../BorrowAssetAction/BorrowAssetAction.tsx | 9 ++- .../components/BorrowModal/BorrowForm.tsx | 3 +- .../DisableEModeForm/DisableEModeForm.tsx | 7 +- .../LendAssetDetails/LendAssetDetails.tsx | 2 +- apps/frontend/src/constants/aave.ts | 2 + .../src/utils/aave/AaveUserReservesSummary.ts | 8 ++- 7 files changed, 62 insertions(+), 34 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx b/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx index 5b01426ef..51026f5fe 100644 --- a/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/AavePage.utils.tsx @@ -2,6 +2,7 @@ import { t } from 'i18next'; import { Decimal } from '@sovryn/utils'; +import { EMODE_DISABLED_ID } from '../../../constants/aave'; import { Reserve } from '../../../hooks/aave/useAaveReservesData'; import { translations } from '../../../locales/i18n'; import { BorrowRateMode } from '../../../types/aave'; @@ -31,7 +32,7 @@ export const normalizeLendPositions = ( if (r.supplied.gt(0)) { acc.push({ asset: r.reserve.symbol, - apy: Decimal.from(r.reserve.variableBorrowAPY), + apy: Decimal.from(r.reserve.supplyAPY).mul(100), supplied: r.supplied, suppliedUSD: r.suppliedUSD, collateral: r.collateral, @@ -70,6 +71,7 @@ export const normalizeBorrowPoolDetails = ( reserves: Reserve[], userReservesSummary: AaveUserReservesSummary, ): BorrowPoolDetails[] => { + // wallet not connected or summary not available at this point if (userReservesSummary.reserves.length === 0) { return reserves.reduce((acc, r) => { if (r.borrowingEnabled) { @@ -80,32 +82,41 @@ export const normalizeBorrowPoolDetails = ( } return acc; }, [] as BorrowPoolDetails[]); - } else { - return reserves.reduce((acc, r) => { - if ( - r.borrowingEnabled && - (userReservesSummary.eModeCategoryId === r.eModeCategoryId || - userReservesSummary.eModeCategoryId === 0) - ) { - acc.push({ - asset: r.symbol, - apy: Decimal.from(r.variableBorrowAPY).mul(100), - available: - userReservesSummary.reserves.find( - userReserve => r.symbol === userReserve.asset, - )?.availableToBorrow || Decimal.from(0), - availableUSD: userReservesSummary.borrowPower, - }); - } - return acc; - }, [] as BorrowPoolDetails[]); } + + return reserves.reduce((acc, r) => { + // skip borrow pools that are not enabled + if (!r.borrowingEnabled) { + return acc; + } + + // skip borrow pools that are not enabled for the user + if ( + userReservesSummary.eModeCategoryId !== r.eModeCategoryId && + userReservesSummary.eModeCategoryId !== EMODE_DISABLED_ID + ) { + return acc; + } + + const userSummary = userReservesSummary.reserves.find( + userReserve => r.symbol === userReserve.asset, + ); + acc.push({ + asset: r.symbol, + apy: Decimal.from(r.variableBorrowAPY).mul(100), + available: userSummary?.availableToBorrow, + availableUSD: userSummary?.availableToBorrowUSD, + }); + + return acc; + }, [] as BorrowPoolDetails[]); }; export const normalizeLendPoolDetails = ( reserves: Reserve[], userReservesSummary: AaveUserReservesSummary, ): LendPoolDetails[] => { + // wallet not connected or summary not available at this point if (userReservesSummary.reserves.length === 0) { return reserves.map(r => ({ asset: r.symbol, @@ -113,12 +124,12 @@ export const normalizeLendPoolDetails = ( canBeCollateral: r.usageAsCollateralEnabled, walletBalance: Decimal.from(0), })); - } else { - return userReservesSummary.reserves.map(r => ({ - asset: r.reserve.symbol, - apy: Decimal.from(r.reserve.supplyAPY).mul(100), - canBeCollateral: r.reserve.usageAsCollateralEnabled, - walletBalance: r.walletBalance, - })); } + + return userReservesSummary.reserves.map(r => ({ + asset: r.reserve.symbol, + apy: Decimal.from(r.reserve.supplyAPY).mul(100), + canBeCollateral: r.reserve.usageAsCollateralEnabled, + walletBalance: r.walletBalance, + })); }; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx index b0c4b40c1..fcaf49d30 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowAssetAction/BorrowAssetAction.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react'; +import React, { FC, useCallback } from 'react'; import { t } from 'i18next'; import { useNavigate } from 'react-router-dom'; @@ -22,6 +22,11 @@ export const BorrowAssetAction: FC = ({ }) => { const navigate = useNavigate(); + const onDetailsClick = useCallback( + () => navigate(`/aave/reserve-overview?asset=${asset}`), + [navigate, asset], + ); + return (
); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx index b56df01bd..fb1a84ad4 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx @@ -8,6 +8,7 @@ import { ErrorBadge, ErrorLevel, Link, + SelectOption, SimpleTable, SimpleTableRow, } from '@sovryn/ui'; @@ -58,7 +59,7 @@ export const BorrowForm: FC = ({ asset, onComplete }) => { }); } return acc; - }, [] as { value: string; label: JSX.Element }[]), + }, [] as SelectOption[]), [summary.reserves], ); diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx index cca226a60..de59e60d3 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx @@ -14,7 +14,10 @@ import { import { Decimal } from '@sovryn/utils'; import { AmountRenderer } from '../../../../../../../../2_molecules/AmountRenderer/AmountRenderer'; -import { config } from '../../../../../../../../../constants/aave'; +import { + config, + EMODE_DISABLED_ID, +} from '../../../../../../../../../constants/aave'; import { useAaveSetUserEMode } from '../../../../../../../../../hooks/aave/useAaveSetUserEMode'; import { useAaveUserReservesData } from '../../../../../../../../../hooks/aave/useAaveUserReservesData'; import { translations } from '../../../../../../../../../locales/i18n'; @@ -41,7 +44,7 @@ export const DisableEModeForm: FC = ({ const summaryAfterDisabled = useMemo(() => { return normalizeEModeSummary( - 0, // disabled + EMODE_DISABLED_ID, reservesData, userReservesData, timestamp, diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx index f14b941a7..02538672a 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendAssetsList/components/LendAssetDetails/LendAssetDetails.tsx @@ -12,7 +12,7 @@ import { LendAssetAction } from '../LendAssetAction/LendAssetAction'; type LendAssetDetailsProps = { pool: LendPoolDetails; - onLendClick: () => unknown; + onLendClick: () => void; }; export const LendAssetDetails: FC = ({ diff --git a/apps/frontend/src/constants/aave.ts b/apps/frontend/src/constants/aave.ts index 653a4e14a..feacb47a1 100644 --- a/apps/frontend/src/constants/aave.ts +++ b/apps/frontend/src/constants/aave.ts @@ -12,3 +12,5 @@ export const config = { InterestRateStrategyAddress: '0x847A3364Cc5fE389283bD821cfC8A477288D9e82', RepayAllETHSurplus: '1000000', }; + +export const EMODE_DISABLED_ID = 0; diff --git a/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts b/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts index f28073a47..0dfeaaf7b 100644 --- a/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts +++ b/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts @@ -204,10 +204,16 @@ export class AaveUserReservesSummaryFactory { const asset = await getAssetData(symbol, BOB_CHAIN_ID); const balance = await getBalance(asset, account, provider); const decimalBalance = decimalic(fromWei(balance, asset.decimals)); - const canBorrow = borrowPower.div(r.reserve.priceInUSD); const availableLiquidity = Decimal.from( formatUnits(r.reserve.availableLiquidity, r.reserve.decimals), ); + + // how much the user can borrow if there's no limit of supply + const canBorrow = borrowPower + .sub(borrowBalance) + .div(r.reserve.priceInUSD); + + // available to borrow for user including liquidity limitation const availableToBorrow = availableLiquidity.lt(canBorrow) ? availableLiquidity : canBorrow; From 60f93c273012c2fc8153bf3bdc9adf4d326a6197 Mon Sep 17 00:00:00 2001 From: matzapata Date: Mon, 9 Sep 2024 13:08:18 +0400 Subject: [PATCH 094/116] cleanup --- .../app/2_molecules/AssetIcon/AssetIcon.tsx | 44 +++++++++ .../SwitchEModeForm/SwitchEModeForm.tsx | 23 +++-- .../RepayForm/RepayForm.constants.ts | 2 +- .../components/RepayForm/RepayForm.tsx | 6 +- .../LendPositionAction/LendPositionAction.tsx | 2 +- .../WithdrawForm/WithdrawForm.constants.ts | 2 +- .../components/WithdrawForm/WithdrawForm.tsx | 4 +- .../WithdrawModal/WithdrawForm.constants.ts | 12 +++ .../components/WithdrawModal/WithdrawForm.tsx | 50 +++++------ .../AaveReserveOverviewPage.constants.tsx | 2 +- .../AaveReserveOverviewPage.tsx | 4 +- .../ReserveTokens/ReserveTokens.tsx | 90 ++++++++----------- .../src/utils/aave/AaveEModeCategories.ts | 14 +-- 13 files changed, 150 insertions(+), 105 deletions(-) create mode 100644 apps/frontend/src/app/2_molecules/AssetIcon/AssetIcon.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawForm.constants.ts diff --git a/apps/frontend/src/app/2_molecules/AssetIcon/AssetIcon.tsx b/apps/frontend/src/app/2_molecules/AssetIcon/AssetIcon.tsx new file mode 100644 index 000000000..725d97ca6 --- /dev/null +++ b/apps/frontend/src/app/2_molecules/AssetIcon/AssetIcon.tsx @@ -0,0 +1,44 @@ +import React, { FC, useEffect } from 'react'; + +import { getAssetData } from '@sovryn/contracts'; +import { ChainId } from '@sovryn/ethers-provider'; + +type AssetIconProps = { + symbol: string; + chainId: ChainId; + className?: string; + size?: number; +}; + +export const AssetIcon: FC = ({ + symbol, + chainId, + className, + size = 24, +}) => { + const [tokenLogo, setTokenLogo] = React.useState(); + + useEffect(() => { + getAssetData(symbol, chainId).then(data => { + setTokenLogo(data.icon); + }); + }, [symbol, chainId]); + + if (!tokenLogo) { + return ( +
+ {symbol.slice(0, 1).toUpperCase()} +
+ ); + } + return ( +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx index 766f8abb6..4c2e76190 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx @@ -10,6 +10,7 @@ import { IconNames, Paragraph, Select, + SelectOption, SimpleTable, SimpleTableRow, } from '@sovryn/ui'; @@ -41,12 +42,19 @@ export const SwitchEModeForm: FC = ({ useAaveUserReservesData(); const categoriesOptions = useMemo(() => { - return categories - .filter(c => c.id !== current.id) - .map(category => ({ - label: category.label, - value: String(category.id), - })); + return categories.reduce((acc, category) => { + if (category.id === current?.id) { + return acc; // skip current category + } + + return [ + ...acc, + { + label: category.label, + value: String(category.id), + }, + ]; + }, [] as SelectOption[]); }, [categories, current?.id]); const selectedCategory = useMemo(() => { @@ -78,8 +86,7 @@ export const SwitchEModeForm: FC = ({ }, [summaryAfterSwitch, positionsInOtherCategories, selectedCategory]); const onConfirm = useCallback(() => { - if (!selectedCategory) return; - handleSetUserEMode(selectedCategory, { onComplete }); + handleSetUserEMode(selectedCategory!, { onComplete }); }, [handleSetUserEMode, selectedCategory, onComplete]); return ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.constants.ts b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.constants.ts index cfee00c96..2ed032d02 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.constants.ts +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.constants.ts @@ -2,7 +2,7 @@ import { t } from 'i18next'; import { translations } from '../../../../../../../locales/i18n'; -export const tabItems = [ +export const TAB_ITEMS = [ { activeClassName: 'text-primary-20', dataAttribute: 'wallet-balance', diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx index fa3be9c91..012bd197e 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx @@ -5,13 +5,13 @@ import { t } from 'i18next'; import { Paragraph, ParagraphSize, Tabs, TabType } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; -import { tabItems } from './RepayForm.constants'; +import { TAB_ITEMS } from './RepayForm.constants'; import { RepayWithCollateralForm } from './RepayWithCollateralForm'; import { RepayWithWalletBalanceForm } from './RepayWithWalletBalanceForm'; type RepayFormProps = { asset: string; - onComplete: () => unknown; + onComplete: () => void; }; enum RepayWith { @@ -36,7 +36,7 @@ export const RepayForm: FC = ({ asset, onComplete }) => { contentClassName="p-4" index={activeTab} onChange={setActiveTab} - items={tabItems} + items={TAB_ITEMS} type={TabType.secondary} /> diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx index 3b1c98047..f7151aaa6 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx @@ -7,7 +7,7 @@ import { Button, ButtonStyle } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; type LendPositionActionProps = { - onWithdrawClick: () => unknown; + onWithdrawClick: () => void; }; export const LendPositionAction: FC = ({ diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.constants.ts b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.constants.ts index 9e096ee95..4af131e11 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.constants.ts +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.constants.ts @@ -2,7 +2,7 @@ import { t } from 'i18next'; import { translations } from '../../../../../../../locales/i18n'; -export const tabItems = [ +export const TAB_ITEMS = [ // For now just withdraw is supported { activeClassName: 'text-primary-20', diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx index 4234eb6ed..08ad11d45 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx @@ -22,7 +22,7 @@ import { useAaveUserReservesData } from '../../../../../../../hooks/aave/useAave import { useAaveWithdraw } from '../../../../../../../hooks/aave/useAaveWithdraw'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; import { translations } from '../../../../../../../locales/i18n'; -import { tabItems } from './WithdrawForm.constants'; +import { TAB_ITEMS } from './WithdrawForm.constants'; const pageTranslations = translations.aavePage; @@ -103,7 +103,7 @@ export const WithdrawForm: FC = ({ asset, onComplete }) => { return (
- +
= ({ asset, onComplete }) => { const withdrawableAssetsOptions = useMemo( () => - summary.reserves - .filter(r => r.supplied.gt(0)) - .map(sa => ({ - value: sa.asset, - label: ( - - ), - })), + summary.reserves.reduce((acc, r) => { + if (r.supplied.lte(0)) { + return acc; + } + + return [ + ...acc, + { + value: r.asset, + label: ( + + ), + }, + ]; + }, [] as SelectOption[]), + [summary], ); @@ -84,18 +94,6 @@ export const WithdrawForm: FC = ({ asset, onComplete }) => { [isValidWithdrawAmount, withdrawSize], ); - const tabItems = useMemo( - () => [ - // For now just withdraw is supported - { - activeClassName: 'text-primary-20', - dataAttribute: 'withdraw', - label: t(translations.common.withdraw), - }, - ], - [], - ); - const onConfirm = useCallback(() => { handleWithdraw( withdrawSize, @@ -114,7 +112,7 @@ export const WithdrawForm: FC = ({ asset, onComplete }) => { return (
- +
{ setActiveOverviewTab(e)} size={TabSize.normal} type={TabType.secondary} diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx index 6523b7382..22040c7af 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx @@ -1,12 +1,11 @@ -import React, { FC, ReactElement, useEffect, useMemo } from 'react'; +import React, { FC } from 'react'; import classNames from 'classnames'; import { t } from 'i18next'; -import { getAssetData } from '@sovryn/contracts'; - import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; +import { AssetIcon } from '../../../../../../2_molecules/AssetIcon/AssetIcon'; import { translations } from '../../../../../../../locales/i18n'; import { TokenButton } from './components/TokenButton/TokenButton'; @@ -28,57 +27,40 @@ export const ReserveTokens: FC = ({ variableDebtTokenAddress, stableDebtTokenAddress, className, -}) => { - const [tokenLogo, setTokenLogo] = React.useState(); - - useEffect(() => { - getAssetData(symbol, BOB_CHAIN_ID).then(data => { - setTokenLogo(data.icon); - }); - }, [symbol]); - - const TokenLogo: ReactElement = useMemo(() => { - if (!tokenLogo) return <>; - else return
; - }, [tokenLogo]); +}) => ( +
+ } + onClick={() => onTokenClick(underlyingTokenAddress)} + /> - return ( -
- onTokenClick(underlyingTokenAddress)} - /> + } + onClick={() => onTokenClick(aTokenAddress)} + /> - onTokenClick(aTokenAddress)} - /> + } + onClick={() => onTokenClick(variableDebtTokenAddress)} + /> - onTokenClick(variableDebtTokenAddress)} - /> - - onTokenClick(stableDebtTokenAddress)} - /> -
- ); -}; + } + onClick={() => onTokenClick(stableDebtTokenAddress)} + /> +
+); diff --git a/apps/frontend/src/utils/aave/AaveEModeCategories.ts b/apps/frontend/src/utils/aave/AaveEModeCategories.ts index 18379b821..3b42cee25 100644 --- a/apps/frontend/src/utils/aave/AaveEModeCategories.ts +++ b/apps/frontend/src/utils/aave/AaveEModeCategories.ts @@ -76,9 +76,10 @@ export class AaveEModeCategories { this.getEModeCategory(categoryId, reserves), ), ).then(res => - res - .filter(r => r.status === 'fulfilled') - .map(r => (r as PromiseFulfilledResult).value), + res.reduce( + (acc, r) => (r.status === 'fulfilled' ? [...acc, r.value] : acc), + [] as EModeCategory[], + ), ); } @@ -89,9 +90,10 @@ export class AaveEModeCategories { const [ltv, liquidationThreshold, liquidationBonus, , label] = await this.Pool.getEModeCategoryData(categoryId); - const assets = reserves - .filter(r => r.eModeCategoryId === categoryId) - .map(r => r.symbol); + const assets = reserves.reduce( + (acc, r) => (r.eModeCategoryId === categoryId ? [...acc, r.symbol] : acc), + [] as string[], + ); return { id: categoryId, From 4064d8bf5d1c785f33ec86c46008d6d4bf9abfa9 Mon Sep 17 00:00:00 2001 From: Matias Zapata <42716817+matzapata@users.noreply.github.com> Date: Mon, 9 Sep 2024 12:11:47 +0300 Subject: [PATCH 095/116] Cleanup (#49) * coments * fix borrow power * fixes and cleanup * cleanup --- .../app/2_molecules/AssetIcon/AssetIcon.tsx | 44 +++++++++ .../SwitchEModeForm/SwitchEModeForm.tsx | 23 +++-- .../RepayForm/RepayForm.constants.ts | 2 +- .../components/RepayForm/RepayForm.tsx | 6 +- .../LendPositionAction/LendPositionAction.tsx | 2 +- .../WithdrawForm/WithdrawForm.constants.ts | 2 +- .../components/WithdrawForm/WithdrawForm.tsx | 4 +- .../WithdrawModal/WithdrawForm.constants.ts | 12 +++ .../components/WithdrawModal/WithdrawForm.tsx | 50 +++++------ .../AaveReserveOverviewPage.constants.tsx | 2 +- .../AaveReserveOverviewPage.tsx | 4 +- .../ReserveTokens/ReserveTokens.tsx | 90 ++++++++----------- .../src/utils/aave/AaveEModeCategories.ts | 14 +-- 13 files changed, 150 insertions(+), 105 deletions(-) create mode 100644 apps/frontend/src/app/2_molecules/AssetIcon/AssetIcon.tsx create mode 100644 apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawModal/WithdrawForm.constants.ts diff --git a/apps/frontend/src/app/2_molecules/AssetIcon/AssetIcon.tsx b/apps/frontend/src/app/2_molecules/AssetIcon/AssetIcon.tsx new file mode 100644 index 000000000..725d97ca6 --- /dev/null +++ b/apps/frontend/src/app/2_molecules/AssetIcon/AssetIcon.tsx @@ -0,0 +1,44 @@ +import React, { FC, useEffect } from 'react'; + +import { getAssetData } from '@sovryn/contracts'; +import { ChainId } from '@sovryn/ethers-provider'; + +type AssetIconProps = { + symbol: string; + chainId: ChainId; + className?: string; + size?: number; +}; + +export const AssetIcon: FC = ({ + symbol, + chainId, + className, + size = 24, +}) => { + const [tokenLogo, setTokenLogo] = React.useState(); + + useEffect(() => { + getAssetData(symbol, chainId).then(data => { + setTokenLogo(data.icon); + }); + }, [symbol, chainId]); + + if (!tokenLogo) { + return ( +
+ {symbol.slice(0, 1).toUpperCase()} +
+ ); + } + return ( +
+ ); +}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx index 766f8abb6..4c2e76190 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx @@ -10,6 +10,7 @@ import { IconNames, Paragraph, Select, + SelectOption, SimpleTable, SimpleTableRow, } from '@sovryn/ui'; @@ -41,12 +42,19 @@ export const SwitchEModeForm: FC = ({ useAaveUserReservesData(); const categoriesOptions = useMemo(() => { - return categories - .filter(c => c.id !== current.id) - .map(category => ({ - label: category.label, - value: String(category.id), - })); + return categories.reduce((acc, category) => { + if (category.id === current?.id) { + return acc; // skip current category + } + + return [ + ...acc, + { + label: category.label, + value: String(category.id), + }, + ]; + }, [] as SelectOption[]); }, [categories, current?.id]); const selectedCategory = useMemo(() => { @@ -78,8 +86,7 @@ export const SwitchEModeForm: FC = ({ }, [summaryAfterSwitch, positionsInOtherCategories, selectedCategory]); const onConfirm = useCallback(() => { - if (!selectedCategory) return; - handleSetUserEMode(selectedCategory, { onComplete }); + handleSetUserEMode(selectedCategory!, { onComplete }); }, [handleSetUserEMode, selectedCategory, onComplete]); return ( diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.constants.ts b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.constants.ts index cfee00c96..2ed032d02 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.constants.ts +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.constants.ts @@ -2,7 +2,7 @@ import { t } from 'i18next'; import { translations } from '../../../../../../../locales/i18n'; -export const tabItems = [ +export const TAB_ITEMS = [ { activeClassName: 'text-primary-20', dataAttribute: 'wallet-balance', diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx index fa3be9c91..012bd197e 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayForm.tsx @@ -5,13 +5,13 @@ import { t } from 'i18next'; import { Paragraph, ParagraphSize, Tabs, TabType } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; -import { tabItems } from './RepayForm.constants'; +import { TAB_ITEMS } from './RepayForm.constants'; import { RepayWithCollateralForm } from './RepayWithCollateralForm'; import { RepayWithWalletBalanceForm } from './RepayWithWalletBalanceForm'; type RepayFormProps = { asset: string; - onComplete: () => unknown; + onComplete: () => void; }; enum RepayWith { @@ -36,7 +36,7 @@ export const RepayForm: FC = ({ asset, onComplete }) => { contentClassName="p-4" index={activeTab} onChange={setActiveTab} - items={tabItems} + items={TAB_ITEMS} type={TabType.secondary} /> diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx index 3b1c98047..f7151aaa6 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/LendPositionAction/LendPositionAction.tsx @@ -7,7 +7,7 @@ import { Button, ButtonStyle } from '@sovryn/ui'; import { translations } from '../../../../../../../locales/i18n'; type LendPositionActionProps = { - onWithdrawClick: () => unknown; + onWithdrawClick: () => void; }; export const LendPositionAction: FC = ({ diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.constants.ts b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.constants.ts index 9e096ee95..4af131e11 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.constants.ts +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.constants.ts @@ -2,7 +2,7 @@ import { t } from 'i18next'; import { translations } from '../../../../../../../locales/i18n'; -export const tabItems = [ +export const TAB_ITEMS = [ // For now just withdraw is supported { activeClassName: 'text-primary-20', diff --git a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx index 4234eb6ed..08ad11d45 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/LendPositionsList/components/WithdrawForm/WithdrawForm.tsx @@ -22,7 +22,7 @@ import { useAaveUserReservesData } from '../../../../../../../hooks/aave/useAave import { useAaveWithdraw } from '../../../../../../../hooks/aave/useAaveWithdraw'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; import { translations } from '../../../../../../../locales/i18n'; -import { tabItems } from './WithdrawForm.constants'; +import { TAB_ITEMS } from './WithdrawForm.constants'; const pageTranslations = translations.aavePage; @@ -103,7 +103,7 @@ export const WithdrawForm: FC = ({ asset, onComplete }) => { return (
- +
= ({ asset, onComplete }) => { const withdrawableAssetsOptions = useMemo( () => - summary.reserves - .filter(r => r.supplied.gt(0)) - .map(sa => ({ - value: sa.asset, - label: ( - - ), - })), + summary.reserves.reduce((acc, r) => { + if (r.supplied.lte(0)) { + return acc; + } + + return [ + ...acc, + { + value: r.asset, + label: ( + + ), + }, + ]; + }, [] as SelectOption[]), + [summary], ); @@ -84,18 +94,6 @@ export const WithdrawForm: FC = ({ asset, onComplete }) => { [isValidWithdrawAmount, withdrawSize], ); - const tabItems = useMemo( - () => [ - // For now just withdraw is supported - { - activeClassName: 'text-primary-20', - dataAttribute: 'withdraw', - label: t(translations.common.withdraw), - }, - ], - [], - ); - const onConfirm = useCallback(() => { handleWithdraw( withdrawSize, @@ -114,7 +112,7 @@ export const WithdrawForm: FC = ({ asset, onComplete }) => { return (
- +
{ setActiveOverviewTab(e)} size={TabSize.normal} type={TabType.secondary} diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx index 6523b7382..22040c7af 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/TopPanel/components/ReserveTokens/ReserveTokens.tsx @@ -1,12 +1,11 @@ -import React, { FC, ReactElement, useEffect, useMemo } from 'react'; +import React, { FC } from 'react'; import classNames from 'classnames'; import { t } from 'i18next'; -import { getAssetData } from '@sovryn/contracts'; - import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; +import { AssetIcon } from '../../../../../../2_molecules/AssetIcon/AssetIcon'; import { translations } from '../../../../../../../locales/i18n'; import { TokenButton } from './components/TokenButton/TokenButton'; @@ -28,57 +27,40 @@ export const ReserveTokens: FC = ({ variableDebtTokenAddress, stableDebtTokenAddress, className, -}) => { - const [tokenLogo, setTokenLogo] = React.useState(); - - useEffect(() => { - getAssetData(symbol, BOB_CHAIN_ID).then(data => { - setTokenLogo(data.icon); - }); - }, [symbol]); - - const TokenLogo: ReactElement = useMemo(() => { - if (!tokenLogo) return <>; - else return
; - }, [tokenLogo]); +}) => ( +
+ } + onClick={() => onTokenClick(underlyingTokenAddress)} + /> - return ( -
- onTokenClick(underlyingTokenAddress)} - /> + } + onClick={() => onTokenClick(aTokenAddress)} + /> - onTokenClick(aTokenAddress)} - /> + } + onClick={() => onTokenClick(variableDebtTokenAddress)} + /> - onTokenClick(variableDebtTokenAddress)} - /> - - onTokenClick(stableDebtTokenAddress)} - /> -
- ); -}; + } + onClick={() => onTokenClick(stableDebtTokenAddress)} + /> +
+); diff --git a/apps/frontend/src/utils/aave/AaveEModeCategories.ts b/apps/frontend/src/utils/aave/AaveEModeCategories.ts index 18379b821..3b42cee25 100644 --- a/apps/frontend/src/utils/aave/AaveEModeCategories.ts +++ b/apps/frontend/src/utils/aave/AaveEModeCategories.ts @@ -76,9 +76,10 @@ export class AaveEModeCategories { this.getEModeCategory(categoryId, reserves), ), ).then(res => - res - .filter(r => r.status === 'fulfilled') - .map(r => (r as PromiseFulfilledResult).value), + res.reduce( + (acc, r) => (r.status === 'fulfilled' ? [...acc, r.value] : acc), + [] as EModeCategory[], + ), ); } @@ -89,9 +90,10 @@ export class AaveEModeCategories { const [ltv, liquidationThreshold, liquidationBonus, , label] = await this.Pool.getEModeCategoryData(categoryId); - const assets = reserves - .filter(r => r.eModeCategoryId === categoryId) - .map(r => r.symbol); + const assets = reserves.reduce( + (acc, r) => (r.eModeCategoryId === categoryId ? [...acc, r.symbol] : acc), + [] as string[], + ); return { id: categoryId, From dd1df1f5729c17f88c9c07a857914ee5005a52ec Mon Sep 17 00:00:00 2001 From: matzapata Date: Mon, 9 Sep 2024 13:30:16 +0400 Subject: [PATCH 096/116] refactor config for same code style and handle mainnet config --- .../components/BorrowModal/BorrowForm.tsx | 4 +- .../DisableEModeForm/DisableEModeForm.tsx | 4 +- .../SwitchEModeForm/SwitchEModeForm.tsx | 4 +- .../RepayForm/RepayWithCollateralForm.tsx | 4 +- .../RepayForm/RepayWithWalletBalanceForm.tsx | 4 +- .../RepayModal/RepayWithCollateralForm.tsx | 4 +- .../RepayModal/RepayWithWalletBalanceForm.tsx | 4 +- .../BorrowDetailsGraph.utils.ts | 4 +- .../InterestRateModelGraph.tsx | 6 +-- apps/frontend/src/config/chains.ts | 3 +- apps/frontend/src/constants/aave.ts | 39 ++++++++++++------- .../frontend/src/hooks/aave/useAaveBorrow.tsx | 8 ++-- .../src/hooks/aave/useAaveEModeCategories.tsx | 4 +- apps/frontend/src/hooks/aave/useAaveRepay.tsx | 6 +-- .../src/hooks/aave/useAaveReservesData.tsx | 7 ++-- .../src/hooks/aave/useAaveSetUserEMode.tsx | 12 ++++-- .../frontend/src/hooks/aave/useAaveSupply.tsx | 6 +-- .../hooks/aave/useAaveUserReservesData.tsx | 10 +++-- .../src/hooks/aave/useAaveWithdraw.tsx | 6 +-- .../aave/AaveBorrowTransactionsFactory.ts | 4 +- .../aave/AaveRepayTransactionsFactory.ts | 9 +++-- .../aave/AaveSupplyTransactionsFactory.ts | 8 ++-- .../src/utils/aave/AaveUserReservesSummary.ts | 4 +- .../aave/AaveWithdrawTransactionsFactory.ts | 4 +- 24 files changed, 98 insertions(+), 70 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx index fb1a84ad4..12c02ccd4 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx @@ -19,7 +19,7 @@ import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; -import { config } from '../../../../../../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../../../../../../constants/aave'; import { useAaveBorrow } from '../../../../../../../hooks/aave/useAaveBorrow'; import { useAaveUserReservesData } from '../../../../../../../hooks/aave/useAaveUserReservesData'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; @@ -147,7 +147,7 @@ export const BorrowForm: FC = ({ asset, onComplete }) => { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx index de59e60d3..d98b970ba 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx @@ -15,8 +15,8 @@ import { Decimal } from '@sovryn/utils'; import { AmountRenderer } from '../../../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { - config, EMODE_DISABLED_ID, + MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE, } from '../../../../../../../../../constants/aave'; import { useAaveSetUserEMode } from '../../../../../../../../../hooks/aave/useAaveSetUserEMode'; import { useAaveUserReservesData } from '../../../../../../../../../hooks/aave/useAaveUserReservesData'; @@ -68,7 +68,7 @@ export const DisableEModeForm: FC = ({ diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx index 4c2e76190..d46856581 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx @@ -17,7 +17,7 @@ import { import { Decimal } from '@sovryn/utils'; import { AmountRenderer } from '../../../../../../../../2_molecules/AmountRenderer/AmountRenderer'; -import { config } from '../../../../../../../../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../../../../../../../../constants/aave'; import { useAaveSetUserEMode } from '../../../../../../../../../hooks/aave/useAaveSetUserEMode'; import { useAaveUserReservesData } from '../../../../../../../../../hooks/aave/useAaveUserReservesData'; import { translations } from '../../../../../../../../../locales/i18n'; @@ -106,7 +106,7 @@ export const SwitchEModeForm: FC = ({ diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx index 6bd3e9754..30a3db373 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx @@ -18,7 +18,7 @@ import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/Amo import { AmountTransition } from '../../../../../../2_molecules/AmountTransition/AmountTransition'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; -import { config } from '../../../../../../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../../../../../../constants/aave'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; import { translations } from '../../../../../../../locales/i18n'; import { CollateralRatioHealthBar } from '../../../CollateralRatioHealthBar/CollateralRatioHealthBar'; @@ -144,7 +144,7 @@ export const RepayWithCollateralForm: FC = () => { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithWalletBalanceForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithWalletBalanceForm.tsx index 6d12296d1..a7688a30d 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithWalletBalanceForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithWalletBalanceForm.tsx @@ -16,7 +16,7 @@ import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; import { AmountTransition } from '../../../../../../2_molecules/AmountTransition/AmountTransition'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; -import { config } from '../../../../../../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../../../../../../constants/aave'; import { useAaveRepay } from '../../../../../../../hooks/aave/useAaveRepay'; import { useAaveUserReservesData } from '../../../../../../../hooks/aave/useAaveUserReservesData'; import { useAccount } from '../../../../../../../hooks/useAccount'; @@ -150,7 +150,7 @@ export const RepayWithWalletBalanceForm: FC< diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx index c38e71287..f182a4eeb 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx @@ -18,7 +18,7 @@ import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/Amo import { AmountTransition } from '../../../../../../2_molecules/AmountTransition/AmountTransition'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; -import { config } from '../../../../../../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../../../../../../constants/aave'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; import { translations } from '../../../../../../../locales/i18n'; import { CollateralRatioHealthBar } from '../../../CollateralRatioHealthBar/CollateralRatioHealthBar'; @@ -144,7 +144,7 @@ export const RepayWithCollateralForm: FC = () => { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx index 61e8d0a2a..af45354bd 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx @@ -16,7 +16,7 @@ import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; import { AmountTransition } from '../../../../../../2_molecules/AmountTransition/AmountTransition'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; -import { config } from '../../../../../../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../../../../../../constants/aave'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; import { translations } from '../../../../../../../locales/i18n'; import { CollateralRatioHealthBar } from '../../../CollateralRatioHealthBar/CollateralRatioHealthBar'; @@ -98,7 +98,7 @@ export const RepayWithWalletBalanceForm: FC< diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts index 66e576501..e69b02b58 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts @@ -1,6 +1,6 @@ import { Decimal } from '@sovryn/utils'; -import { config } from '../../../../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../../../../constants/aave'; import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; import { getBobExplorerUrl } from '../../../../../utils/helpers'; @@ -15,6 +15,6 @@ export const normalizeBorrowStats = (reserve: Reserve) => ({ .mul(100), reserveFactor: Decimal.from(reserve.reserveFactor).mul(100), collectorContractLink: `${getBobExplorerUrl()}/address/${ - config.TreasuryAddress + AAVE_CONTRACT_ADDRESSES.TREASURY }`, }); diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx index 2d91d245e..f74cd192d 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx @@ -8,7 +8,7 @@ import { Decimal } from '@sovryn/utils'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; -import { config } from '../../../../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../../../../constants/aave'; import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; @@ -30,12 +30,12 @@ export const InterestRateModelGraph: FC = ({ const interestRateStrategyUrl = useMemo(() => { return `${getBobExplorerUrl()}/address/${ - config.InterestRateStrategyAddress + AAVE_CONTRACT_ADDRESSES.INTEREST_RATE_STRATEGY }`; }, []); const collectorContractUrl = useMemo(() => { - return `${getBobExplorerUrl()}/address/${config.TreasuryAddress}`; + return `${getBobExplorerUrl()}/address/${AAVE_CONTRACT_ADDRESSES.TREASURY}`; }, []); const currentUsageRatio = useMemo(() => { diff --git a/apps/frontend/src/config/chains.ts b/apps/frontend/src/config/chains.ts index e9fe504d3..ea73a5714 100644 --- a/apps/frontend/src/config/chains.ts +++ b/apps/frontend/src/config/chains.ts @@ -10,7 +10,8 @@ import { RSK } from '../constants/infrastructure/rsk'; import { SEPOLIA } from '../constants/infrastructure/sepolia'; import { Environments } from '../types/global'; -const IS_MAINNET = process.env.REACT_APP_NETWORK === Environments.Mainnet; +export const IS_MAINNET = + process.env.REACT_APP_NETWORK === Environments.Mainnet; export enum Chains { RSK = 'rsk', diff --git a/apps/frontend/src/constants/aave.ts b/apps/frontend/src/constants/aave.ts index feacb47a1..6ff518320 100644 --- a/apps/frontend/src/constants/aave.ts +++ b/apps/frontend/src/constants/aave.ts @@ -1,16 +1,29 @@ -import { decimalic } from '../utils/math'; +import { IS_MAINNET } from '../config/chains'; -export const config = { - MinCollateralRatio: decimalic(1.5), - PoolAddress: '0xFbdc7303cc2046Ff6c340F94F71aB01517b42476', - WETHGatewayAddress: '0x50ff0B7a3e7af6F896CA81aF2E563814Eee7A3c0', - UiPoolDataProviderV3Address: '0x5E7F300702D15d8B661aed2B67dc68f8e48B3b55', - PoolAddressesProviderAddress: '0xc187B9083fF15B3Ae4eA56f8ab82DF52474B4E58', - VariableDebtWETHAddress: '0x8bA8F81a5c2F406f3eD8FE18A94AecE3E27C2831', - WETHAddress: '0x8CEc2719a2e896A11eA3f10406EfDb6Ad87281D9', - TreasuryAddress: '0x2a9d8f5b2f7b8f5b4b5d5e7f3b4b5d5b5d5d5d5d', - InterestRateStrategyAddress: '0x847A3364Cc5fE389283bD821cfC8A477288D9e82', - RepayAllETHSurplus: '1000000', -}; +import { decimalic } from '../utils/math'; export const EMODE_DISABLED_ID = 0; +export const MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE = decimalic(1.5); +export const REPAY_ALL_ETH_SURPLUS_AAVE = '1000000'; + +export const AAVE_CONTRACT_ADDRESSES = IS_MAINNET + ? { + POOL: '', + WETH_GATEWAY: '', + UI_POOL_DATA_PROVIDER: '', + POOL_ADDRESSES_PROVIDER: '', + VARIABLE_DEBT_ETH: '', + WETH: '', + TREASURY: '', + INTEREST_RATE_STRATEGY: '', + } + : { + POOL: '0xFbdc7303cc2046Ff6c340F94F71aB01517b42476', + WETH_GATEWAY: '0x50ff0B7a3e7af6F896CA81aF2E563814Eee7A3c0', + UI_POOL_DATA_PROVIDER: '0x5E7F300702D15d8B661aed2B67dc68f8e48B3b55', + POOL_ADDRESSES_PROVIDER: '0xc187B9083fF15B3Ae4eA56f8ab82DF52474B4E58', + VARIABLE_DEBT_ETH: '0x8bA8F81a5c2F406f3eD8FE18A94AecE3E27C2831', + WETH: '0x8CEc2719a2e896A11eA3f10406EfDb6Ad87281D9', + TREASURY: '0x2a9d8f5b2f7b8f5b4b5d5e7f3b4b5d5b5d5d5d5d', + INTEREST_RATE_STRATEGY: '0x847A3364Cc5fE389283bD821cfC8A477288D9e82', + }; diff --git a/apps/frontend/src/hooks/aave/useAaveBorrow.tsx b/apps/frontend/src/hooks/aave/useAaveBorrow.tsx index cba302881..c9baee288 100644 --- a/apps/frontend/src/hooks/aave/useAaveBorrow.tsx +++ b/apps/frontend/src/hooks/aave/useAaveBorrow.tsx @@ -8,7 +8,7 @@ import { Decimal } from '@sovryn/utils'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { useTransactionContext } from '../../contexts/TransactionContext'; import { translations } from '../../locales/i18n'; import { BorrowRateMode, TransactionFactoryOptions } from '../../types/aave'; @@ -24,9 +24,9 @@ export const useAaveBorrow = () => { const aaveBorrowTransactionsFactory = useMemo(() => { if (!signer) return null; return new AaveBorrowTransactionsFactory( - config.PoolAddress, - config.WETHGatewayAddress, - config.VariableDebtWETHAddress, + AAVE_CONTRACT_ADDRESSES.POOL, + AAVE_CONTRACT_ADDRESSES.WETH_GATEWAY, + AAVE_CONTRACT_ADDRESSES.VARIABLE_DEBT_ETH, signer, ); }, [signer]); diff --git a/apps/frontend/src/hooks/aave/useAaveEModeCategories.tsx b/apps/frontend/src/hooks/aave/useAaveEModeCategories.tsx index 853f846d4..21271520a 100644 --- a/apps/frontend/src/hooks/aave/useAaveEModeCategories.tsx +++ b/apps/frontend/src/hooks/aave/useAaveEModeCategories.tsx @@ -4,13 +4,13 @@ import { getProvider } from '@sovryn/ethers-provider'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { EModeCategory } from '../../types/aave'; import { AaveEModeCategories } from '../../utils/aave/AaveEModeCategories'; import { useAaveReservesData } from './useAaveReservesData'; const eModeCategoriesFetcher = new AaveEModeCategories( - config.PoolAddress, + AAVE_CONTRACT_ADDRESSES.POOL, getProvider(BOB_CHAIN_ID), ); diff --git a/apps/frontend/src/hooks/aave/useAaveRepay.tsx b/apps/frontend/src/hooks/aave/useAaveRepay.tsx index 33500bba3..5d0f8cc05 100644 --- a/apps/frontend/src/hooks/aave/useAaveRepay.tsx +++ b/apps/frontend/src/hooks/aave/useAaveRepay.tsx @@ -8,7 +8,7 @@ import { Decimal } from '@sovryn/utils'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { useTransactionContext } from '../../contexts/TransactionContext'; import { translations } from '../../locales/i18n'; import { BorrowRateMode, TransactionFactoryOptions } from '../../types/aave'; @@ -24,8 +24,8 @@ export const useAaveRepay = () => { const aaveRepayTransactionsFactory = useMemo(() => { if (!signer) return null; return new AaveRepayTransactionsFactory( - config.PoolAddress, - config.WETHGatewayAddress, + AAVE_CONTRACT_ADDRESSES.POOL, + AAVE_CONTRACT_ADDRESSES.WETH_GATEWAY, signer, ); }, [signer]); diff --git a/apps/frontend/src/hooks/aave/useAaveReservesData.tsx b/apps/frontend/src/hooks/aave/useAaveReservesData.tsx index ca7172432..7e493d3c5 100644 --- a/apps/frontend/src/hooks/aave/useAaveReservesData.tsx +++ b/apps/frontend/src/hooks/aave/useAaveReservesData.tsx @@ -12,7 +12,7 @@ import { getProvider } from '@sovryn/ethers-provider'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { ETH, WETH } from '../../constants/currencies'; export type Reserve = ReserveDataHumanized & FormatReserveUSDResponse; @@ -20,7 +20,7 @@ export type ReserveData = { reserves: Reserve[]; loading: boolean }; const uiPoolDataProvider = new UiPoolDataProvider({ provider: getProvider(BOB_CHAIN_ID), - uiPoolDataProviderAddress: config.UiPoolDataProviderV3Address, + uiPoolDataProviderAddress: AAVE_CONTRACT_ADDRESSES.UI_POOL_DATA_PROVIDER, chainId: Number(BOB_CHAIN_ID), }); @@ -31,7 +31,8 @@ export const useAaveReservesData = (): ReserveData => { const fetchReservesData = useCallback(async () => { const currentTimestamp = dayjs().unix(); const reservesData = await uiPoolDataProvider.getReservesHumanized({ - lendingPoolAddressProvider: config.PoolAddressesProviderAddress, + lendingPoolAddressProvider: + AAVE_CONTRACT_ADDRESSES.POOL_ADDRESSES_PROVIDER, }); const formattedReserves = formatReserves({ reserves: reservesData.reservesData, diff --git a/apps/frontend/src/hooks/aave/useAaveSetUserEMode.tsx b/apps/frontend/src/hooks/aave/useAaveSetUserEMode.tsx index 42bc7ce2d..eeecd3a4d 100644 --- a/apps/frontend/src/hooks/aave/useAaveSetUserEMode.tsx +++ b/apps/frontend/src/hooks/aave/useAaveSetUserEMode.tsx @@ -2,7 +2,7 @@ import { useCallback, useMemo } from 'react'; import { t } from 'i18next'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { useTransactionContext } from '../../contexts/TransactionContext'; import { translations } from '../../locales/i18n'; import { EModeCategory, TransactionFactoryOptions } from '../../types/aave'; @@ -16,8 +16,14 @@ export const useAaveSetUserEMode = () => { const { setTransactions, setIsOpen, setTitle } = useTransactionContext(); const aaveEModeTransactionsFactory = useMemo(() => { - if (!signer) return null; - return new AaveEModeTransactionsFactory(config.PoolAddress, signer); + if (!signer) { + return null; + } + + return new AaveEModeTransactionsFactory( + AAVE_CONTRACT_ADDRESSES.POOL, + signer, + ); }, [signer]); const handleSetUserEMode = useCallback( diff --git a/apps/frontend/src/hooks/aave/useAaveSupply.tsx b/apps/frontend/src/hooks/aave/useAaveSupply.tsx index 7a952382e..73aad67e3 100644 --- a/apps/frontend/src/hooks/aave/useAaveSupply.tsx +++ b/apps/frontend/src/hooks/aave/useAaveSupply.tsx @@ -8,7 +8,7 @@ import { Decimal } from '@sovryn/utils'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { useTransactionContext } from '../../contexts/TransactionContext'; import { translations } from '../../locales/i18n'; import { TransactionFactoryOptions } from '../../types/aave'; @@ -24,8 +24,8 @@ export const useAaveSupply = () => { const aaveSupplyTransactionsFactory = useMemo(() => { if (!signer) return null; return new AaveSupplyTransactionsFactory( - config.PoolAddress, - config.WETHGatewayAddress, + AAVE_CONTRACT_ADDRESSES.POOL, + AAVE_CONTRACT_ADDRESSES.WETH_GATEWAY, signer, ); }, [signer]); diff --git a/apps/frontend/src/hooks/aave/useAaveUserReservesData.tsx b/apps/frontend/src/hooks/aave/useAaveUserReservesData.tsx index e1658dd85..b325b4fad 100644 --- a/apps/frontend/src/hooks/aave/useAaveUserReservesData.tsx +++ b/apps/frontend/src/hooks/aave/useAaveUserReservesData.tsx @@ -11,7 +11,7 @@ import { getProvider } from '@sovryn/ethers-provider'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { UserReservesData } from '../../types/aave'; import { AaveUserReservesSummary, @@ -22,7 +22,7 @@ import { useBlockNumber } from '../useBlockNumber'; const uiPoolDataProvider = new UiPoolDataProvider({ provider: getProvider(BOB_CHAIN_ID), - uiPoolDataProviderAddress: config.UiPoolDataProviderV3Address, + uiPoolDataProviderAddress: AAVE_CONTRACT_ADDRESSES.UI_POOL_DATA_PROVIDER, chainId: Number(BOB_CHAIN_ID), }); @@ -52,10 +52,12 @@ export const useAaveUserReservesData = (): { try { const [reservesData, userReservesData] = await Promise.all([ uiPoolDataProvider.getReservesHumanized({ - lendingPoolAddressProvider: config.PoolAddressesProviderAddress, + lendingPoolAddressProvider: + AAVE_CONTRACT_ADDRESSES.POOL_ADDRESSES_PROVIDER, }), uiPoolDataProvider.getUserReservesHumanized({ - lendingPoolAddressProvider: config.PoolAddressesProviderAddress, + lendingPoolAddressProvider: + AAVE_CONTRACT_ADDRESSES.POOL_ADDRESSES_PROVIDER, user: account, }), ]); diff --git a/apps/frontend/src/hooks/aave/useAaveWithdraw.tsx b/apps/frontend/src/hooks/aave/useAaveWithdraw.tsx index f394bc80e..f2f946a77 100644 --- a/apps/frontend/src/hooks/aave/useAaveWithdraw.tsx +++ b/apps/frontend/src/hooks/aave/useAaveWithdraw.tsx @@ -8,7 +8,7 @@ import { Decimal } from '@sovryn/utils'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { useTransactionContext } from '../../contexts/TransactionContext'; import { translations } from '../../locales/i18n'; import { TransactionFactoryOptions } from '../../types/aave'; @@ -24,8 +24,8 @@ export const useAaveWithdraw = () => { const aaveWithdrawTransactionsFactory = useMemo(() => { if (!signer) return null; return new AaveWithdrawTransactionsFactory( - config.PoolAddress, - config.WETHGatewayAddress, + AAVE_CONTRACT_ADDRESSES.POOL, + AAVE_CONTRACT_ADDRESSES.WETH_GATEWAY, signer, ); }, [signer]); diff --git a/apps/frontend/src/utils/aave/AaveBorrowTransactionsFactory.ts b/apps/frontend/src/utils/aave/AaveBorrowTransactionsFactory.ts index b16da8fcf..75d822dcb 100644 --- a/apps/frontend/src/utils/aave/AaveBorrowTransactionsFactory.ts +++ b/apps/frontend/src/utils/aave/AaveBorrowTransactionsFactory.ts @@ -9,7 +9,7 @@ import { Transaction, TransactionType, } from '../../app/3_organisms/TransactionStepDialog/TransactionStepDialog.types'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { translations } from '../../locales/i18n'; import { BorrowRateMode, TransactionFactoryOptions } from '../../types/aave'; @@ -56,7 +56,7 @@ export class AaveBorrowTransactionsFactory { ): Promise { if ( asset.isNative || - asset.address.toLowerCase() === config.WETHAddress.toLowerCase() + asset.address.toLowerCase() === AAVE_CONTRACT_ADDRESSES.WETH.toLowerCase() ) { return this.borrowNative(amount, rateMode, opts); } else return this.borrowToken(asset, amount, rateMode, opts); diff --git a/apps/frontend/src/utils/aave/AaveRepayTransactionsFactory.ts b/apps/frontend/src/utils/aave/AaveRepayTransactionsFactory.ts index 98c94269f..441a12e9f 100644 --- a/apps/frontend/src/utils/aave/AaveRepayTransactionsFactory.ts +++ b/apps/frontend/src/utils/aave/AaveRepayTransactionsFactory.ts @@ -9,7 +9,10 @@ import { Transaction, TransactionType, } from '../../app/3_organisms/TransactionStepDialog/TransactionStepDialog.types'; -import { config } from '../../constants/aave'; +import { + AAVE_CONTRACT_ADDRESSES, + REPAY_ALL_ETH_SURPLUS_AAVE, +} from '../../constants/aave'; import { translations } from '../../locales/i18n'; import { BorrowRateMode, TransactionFactoryOptions } from '../../types/aave'; import { prepareApproveTransaction } from '../transactions'; @@ -49,7 +52,7 @@ export class AaveRepayTransactionsFactory { ): Promise { if ( token.isNative || - token.address.toLowerCase() === config.WETHAddress.toLowerCase() + token.address.toLowerCase() === AAVE_CONTRACT_ADDRESSES.WETH.toLowerCase() ) { return this.repayNative(amount, isEntireDebt, borrowRateMode, opts); } else @@ -108,7 +111,7 @@ export class AaveRepayTransactionsFactory { constants.AddressZero, BOB_CHAIN_ID, ); - amount = isEntireDebt ? amount.add(config.RepayAllETHSurplus) : amount; + amount = isEntireDebt ? amount.add(REPAY_ALL_ETH_SURPLUS_AAVE) : amount; return [ { diff --git a/apps/frontend/src/utils/aave/AaveSupplyTransactionsFactory.ts b/apps/frontend/src/utils/aave/AaveSupplyTransactionsFactory.ts index 09c017444..c5c2bb43b 100644 --- a/apps/frontend/src/utils/aave/AaveSupplyTransactionsFactory.ts +++ b/apps/frontend/src/utils/aave/AaveSupplyTransactionsFactory.ts @@ -9,7 +9,7 @@ import { Transaction, TransactionType, } from '../../app/3_organisms/TransactionStepDialog/TransactionStepDialog.types'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { translations } from '../../locales/i18n'; import { TransactionFactoryOptions } from '../../types/aave'; import { prepareApproveTransaction } from '../transactions'; @@ -48,7 +48,7 @@ export class AaveSupplyTransactionsFactory { ): Promise { if ( token.isNative || - token.address.toLowerCase() === config.WETHAddress.toLowerCase() + token.address.toLowerCase() === AAVE_CONTRACT_ADDRESSES.WETH.toLowerCase() ) { return this.supplyNative(amount, opts); } else return this.supplyToken(token, amount, opts); @@ -59,7 +59,9 @@ export class AaveSupplyTransactionsFactory { useAsCollateral: boolean, opts?: TransactionFactoryOptions, ): Promise { - const tokenAddress = token.isNative ? config.WETHAddress : token.address; + const tokenAddress = token.isNative + ? AAVE_CONTRACT_ADDRESSES.WETH + : token.address; return [ { diff --git a/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts b/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts index 0dfeaaf7b..32fab812f 100644 --- a/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts +++ b/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts @@ -17,7 +17,7 @@ import { Decimal } from '@sovryn/utils'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../constants/aave'; import { Reserve } from '../../hooks/aave/useAaveReservesData'; import { BorrowRateMode, UserReservesData } from '../../types/aave'; import { decimalic, fromWei } from '../math'; @@ -162,7 +162,7 @@ export class AaveUserReservesSummaryFactory { userSummary.currentLiquidationThreshold, ); const borrowPower = AaveCalculations.computeBorrowPower( - config.MinCollateralRatio, + MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE, collateralBalance, ); const borrowPowerUsed = AaveCalculations.computeBorrowPowerUsed( diff --git a/apps/frontend/src/utils/aave/AaveWithdrawTransactionsFactory.ts b/apps/frontend/src/utils/aave/AaveWithdrawTransactionsFactory.ts index dfbab462a..81ea5eb89 100644 --- a/apps/frontend/src/utils/aave/AaveWithdrawTransactionsFactory.ts +++ b/apps/frontend/src/utils/aave/AaveWithdrawTransactionsFactory.ts @@ -9,7 +9,7 @@ import { Transaction, TransactionType, } from '../../app/3_organisms/TransactionStepDialog/TransactionStepDialog.types'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { translations } from '../../locales/i18n'; import { TransactionFactoryOptions } from '../../types/aave'; import { prepareApproveTransaction } from '../transactions'; @@ -44,7 +44,7 @@ export class AaveWithdrawTransactionsFactory { ): Promise { if ( token.isNative || - token.address.toLowerCase() === config.WETHAddress.toLowerCase() + token.address.toLowerCase() === AAVE_CONTRACT_ADDRESSES.WETH.toLowerCase() ) { return this.withdrawNative(amount, isMaxAmount, opts); } else return this.withdrawToken(token, amount, isMaxAmount, opts); From 28b105d32d7f930c5b7c58d9c3a6a91875a69308 Mon Sep 17 00:00:00 2001 From: Matias Zapata <42716817+matzapata@users.noreply.github.com> Date: Mon, 9 Sep 2024 12:36:32 +0300 Subject: [PATCH 097/116] Refactor config for same code style and handle mainnet config (#50) * coments * fix borrow power * fixes and cleanup * cleanup * refactor config for same code style and handle mainnet config --- .../components/BorrowModal/BorrowForm.tsx | 4 +- .../DisableEModeForm/DisableEModeForm.tsx | 4 +- .../SwitchEModeForm/SwitchEModeForm.tsx | 4 +- .../RepayForm/RepayWithCollateralForm.tsx | 4 +- .../RepayForm/RepayWithWalletBalanceForm.tsx | 4 +- .../RepayModal/RepayWithCollateralForm.tsx | 4 +- .../RepayModal/RepayWithWalletBalanceForm.tsx | 4 +- .../BorrowDetailsGraph.utils.ts | 4 +- .../InterestRateModelGraph.tsx | 6 +-- apps/frontend/src/config/chains.ts | 3 +- apps/frontend/src/constants/aave.ts | 38 ++++++++++++------- .../frontend/src/hooks/aave/useAaveBorrow.tsx | 8 ++-- .../src/hooks/aave/useAaveEModeCategories.tsx | 4 +- apps/frontend/src/hooks/aave/useAaveRepay.tsx | 6 +-- .../src/hooks/aave/useAaveReservesData.tsx | 7 ++-- .../src/hooks/aave/useAaveSetUserEMode.tsx | 12 ++++-- .../frontend/src/hooks/aave/useAaveSupply.tsx | 6 +-- .../hooks/aave/useAaveUserReservesData.tsx | 10 +++-- .../src/hooks/aave/useAaveWithdraw.tsx | 6 +-- .../aave/AaveBorrowTransactionsFactory.ts | 4 +- .../aave/AaveRepayTransactionsFactory.ts | 9 +++-- .../aave/AaveSupplyTransactionsFactory.ts | 8 ++-- .../src/utils/aave/AaveUserReservesSummary.ts | 4 +- .../aave/AaveWithdrawTransactionsFactory.ts | 4 +- 24 files changed, 97 insertions(+), 70 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx index fb1a84ad4..12c02ccd4 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowAssetsList/components/BorrowModal/BorrowForm.tsx @@ -19,7 +19,7 @@ import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; -import { config } from '../../../../../../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../../../../../../constants/aave'; import { useAaveBorrow } from '../../../../../../../hooks/aave/useAaveBorrow'; import { useAaveUserReservesData } from '../../../../../../../hooks/aave/useAaveUserReservesData'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; @@ -147,7 +147,7 @@ export const BorrowForm: FC = ({ asset, onComplete }) => { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx index de59e60d3..d98b970ba 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/DisableEModeForm/DisableEModeForm.tsx @@ -15,8 +15,8 @@ import { Decimal } from '@sovryn/utils'; import { AmountRenderer } from '../../../../../../../../2_molecules/AmountRenderer/AmountRenderer'; import { - config, EMODE_DISABLED_ID, + MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE, } from '../../../../../../../../../constants/aave'; import { useAaveSetUserEMode } from '../../../../../../../../../hooks/aave/useAaveSetUserEMode'; import { useAaveUserReservesData } from '../../../../../../../../../hooks/aave/useAaveUserReservesData'; @@ -68,7 +68,7 @@ export const DisableEModeForm: FC = ({ diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx index 4c2e76190..d46856581 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/EfficiencyModeCard/components/SwitchEModeForm/SwitchEModeForm.tsx @@ -17,7 +17,7 @@ import { import { Decimal } from '@sovryn/utils'; import { AmountRenderer } from '../../../../../../../../2_molecules/AmountRenderer/AmountRenderer'; -import { config } from '../../../../../../../../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../../../../../../../../constants/aave'; import { useAaveSetUserEMode } from '../../../../../../../../../hooks/aave/useAaveSetUserEMode'; import { useAaveUserReservesData } from '../../../../../../../../../hooks/aave/useAaveUserReservesData'; import { translations } from '../../../../../../../../../locales/i18n'; @@ -106,7 +106,7 @@ export const SwitchEModeForm: FC = ({ diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx index 6bd3e9754..30a3db373 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithCollateralForm.tsx @@ -18,7 +18,7 @@ import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/Amo import { AmountTransition } from '../../../../../../2_molecules/AmountTransition/AmountTransition'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; -import { config } from '../../../../../../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../../../../../../constants/aave'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; import { translations } from '../../../../../../../locales/i18n'; import { CollateralRatioHealthBar } from '../../../CollateralRatioHealthBar/CollateralRatioHealthBar'; @@ -144,7 +144,7 @@ export const RepayWithCollateralForm: FC = () => { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithWalletBalanceForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithWalletBalanceForm.tsx index 6d12296d1..a7688a30d 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithWalletBalanceForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithWalletBalanceForm.tsx @@ -16,7 +16,7 @@ import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; import { AmountTransition } from '../../../../../../2_molecules/AmountTransition/AmountTransition'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; -import { config } from '../../../../../../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../../../../../../constants/aave'; import { useAaveRepay } from '../../../../../../../hooks/aave/useAaveRepay'; import { useAaveUserReservesData } from '../../../../../../../hooks/aave/useAaveUserReservesData'; import { useAccount } from '../../../../../../../hooks/useAccount'; @@ -150,7 +150,7 @@ export const RepayWithWalletBalanceForm: FC< diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx index c38e71287..f182a4eeb 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx @@ -18,7 +18,7 @@ import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/Amo import { AmountTransition } from '../../../../../../2_molecules/AmountTransition/AmountTransition'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; -import { config } from '../../../../../../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../../../../../../constants/aave'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; import { translations } from '../../../../../../../locales/i18n'; import { CollateralRatioHealthBar } from '../../../CollateralRatioHealthBar/CollateralRatioHealthBar'; @@ -144,7 +144,7 @@ export const RepayWithCollateralForm: FC = () => { diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx index 61e8d0a2a..af45354bd 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx @@ -16,7 +16,7 @@ import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; import { AmountTransition } from '../../../../../../2_molecules/AmountTransition/AmountTransition'; import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; -import { config } from '../../../../../../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../../../../../../constants/aave'; import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; import { translations } from '../../../../../../../locales/i18n'; import { CollateralRatioHealthBar } from '../../../CollateralRatioHealthBar/CollateralRatioHealthBar'; @@ -98,7 +98,7 @@ export const RepayWithWalletBalanceForm: FC< diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts index 66e576501..e69b02b58 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.utils.ts @@ -1,6 +1,6 @@ import { Decimal } from '@sovryn/utils'; -import { config } from '../../../../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../../../../constants/aave'; import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; import { getBobExplorerUrl } from '../../../../../utils/helpers'; @@ -15,6 +15,6 @@ export const normalizeBorrowStats = (reserve: Reserve) => ({ .mul(100), reserveFactor: Decimal.from(reserve.reserveFactor).mul(100), collectorContractLink: `${getBobExplorerUrl()}/address/${ - config.TreasuryAddress + AAVE_CONTRACT_ADDRESSES.TREASURY }`, }); diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx index 2d91d245e..f74cd192d 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx @@ -8,7 +8,7 @@ import { Decimal } from '@sovryn/utils'; import { AmountRenderer } from '../../../../2_molecules/AmountRenderer/AmountRenderer'; import { StatisticsCard } from '../../../../2_molecules/StatisticsCard/StatisticsCard'; -import { config } from '../../../../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../../../../constants/aave'; import { Reserve } from '../../../../../hooks/aave/useAaveReservesData'; import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; @@ -30,12 +30,12 @@ export const InterestRateModelGraph: FC = ({ const interestRateStrategyUrl = useMemo(() => { return `${getBobExplorerUrl()}/address/${ - config.InterestRateStrategyAddress + AAVE_CONTRACT_ADDRESSES.INTEREST_RATE_STRATEGY }`; }, []); const collectorContractUrl = useMemo(() => { - return `${getBobExplorerUrl()}/address/${config.TreasuryAddress}`; + return `${getBobExplorerUrl()}/address/${AAVE_CONTRACT_ADDRESSES.TREASURY}`; }, []); const currentUsageRatio = useMemo(() => { diff --git a/apps/frontend/src/config/chains.ts b/apps/frontend/src/config/chains.ts index e9fe504d3..ea73a5714 100644 --- a/apps/frontend/src/config/chains.ts +++ b/apps/frontend/src/config/chains.ts @@ -10,7 +10,8 @@ import { RSK } from '../constants/infrastructure/rsk'; import { SEPOLIA } from '../constants/infrastructure/sepolia'; import { Environments } from '../types/global'; -const IS_MAINNET = process.env.REACT_APP_NETWORK === Environments.Mainnet; +export const IS_MAINNET = + process.env.REACT_APP_NETWORK === Environments.Mainnet; export enum Chains { RSK = 'rsk', diff --git a/apps/frontend/src/constants/aave.ts b/apps/frontend/src/constants/aave.ts index feacb47a1..9c5fa4dfb 100644 --- a/apps/frontend/src/constants/aave.ts +++ b/apps/frontend/src/constants/aave.ts @@ -1,16 +1,28 @@ +import { IS_MAINNET } from '../config/chains'; import { decimalic } from '../utils/math'; -export const config = { - MinCollateralRatio: decimalic(1.5), - PoolAddress: '0xFbdc7303cc2046Ff6c340F94F71aB01517b42476', - WETHGatewayAddress: '0x50ff0B7a3e7af6F896CA81aF2E563814Eee7A3c0', - UiPoolDataProviderV3Address: '0x5E7F300702D15d8B661aed2B67dc68f8e48B3b55', - PoolAddressesProviderAddress: '0xc187B9083fF15B3Ae4eA56f8ab82DF52474B4E58', - VariableDebtWETHAddress: '0x8bA8F81a5c2F406f3eD8FE18A94AecE3E27C2831', - WETHAddress: '0x8CEc2719a2e896A11eA3f10406EfDb6Ad87281D9', - TreasuryAddress: '0x2a9d8f5b2f7b8f5b4b5d5e7f3b4b5d5b5d5d5d5d', - InterestRateStrategyAddress: '0x847A3364Cc5fE389283bD821cfC8A477288D9e82', - RepayAllETHSurplus: '1000000', -}; - export const EMODE_DISABLED_ID = 0; +export const MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE = decimalic(1.5); +export const REPAY_ALL_ETH_SURPLUS_AAVE = '1000000'; + +export const AAVE_CONTRACT_ADDRESSES = IS_MAINNET + ? { + POOL: '', + WETH_GATEWAY: '', + UI_POOL_DATA_PROVIDER: '', + POOL_ADDRESSES_PROVIDER: '', + VARIABLE_DEBT_ETH: '', + WETH: '', + TREASURY: '', + INTEREST_RATE_STRATEGY: '', + } + : { + POOL: '0xFbdc7303cc2046Ff6c340F94F71aB01517b42476', + WETH_GATEWAY: '0x50ff0B7a3e7af6F896CA81aF2E563814Eee7A3c0', + UI_POOL_DATA_PROVIDER: '0x5E7F300702D15d8B661aed2B67dc68f8e48B3b55', + POOL_ADDRESSES_PROVIDER: '0xc187B9083fF15B3Ae4eA56f8ab82DF52474B4E58', + VARIABLE_DEBT_ETH: '0x8bA8F81a5c2F406f3eD8FE18A94AecE3E27C2831', + WETH: '0x8CEc2719a2e896A11eA3f10406EfDb6Ad87281D9', + TREASURY: '0x2a9d8f5b2f7b8f5b4b5d5e7f3b4b5d5b5d5d5d5d', + INTEREST_RATE_STRATEGY: '0x847A3364Cc5fE389283bD821cfC8A477288D9e82', + }; diff --git a/apps/frontend/src/hooks/aave/useAaveBorrow.tsx b/apps/frontend/src/hooks/aave/useAaveBorrow.tsx index cba302881..c9baee288 100644 --- a/apps/frontend/src/hooks/aave/useAaveBorrow.tsx +++ b/apps/frontend/src/hooks/aave/useAaveBorrow.tsx @@ -8,7 +8,7 @@ import { Decimal } from '@sovryn/utils'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { useTransactionContext } from '../../contexts/TransactionContext'; import { translations } from '../../locales/i18n'; import { BorrowRateMode, TransactionFactoryOptions } from '../../types/aave'; @@ -24,9 +24,9 @@ export const useAaveBorrow = () => { const aaveBorrowTransactionsFactory = useMemo(() => { if (!signer) return null; return new AaveBorrowTransactionsFactory( - config.PoolAddress, - config.WETHGatewayAddress, - config.VariableDebtWETHAddress, + AAVE_CONTRACT_ADDRESSES.POOL, + AAVE_CONTRACT_ADDRESSES.WETH_GATEWAY, + AAVE_CONTRACT_ADDRESSES.VARIABLE_DEBT_ETH, signer, ); }, [signer]); diff --git a/apps/frontend/src/hooks/aave/useAaveEModeCategories.tsx b/apps/frontend/src/hooks/aave/useAaveEModeCategories.tsx index 853f846d4..21271520a 100644 --- a/apps/frontend/src/hooks/aave/useAaveEModeCategories.tsx +++ b/apps/frontend/src/hooks/aave/useAaveEModeCategories.tsx @@ -4,13 +4,13 @@ import { getProvider } from '@sovryn/ethers-provider'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { EModeCategory } from '../../types/aave'; import { AaveEModeCategories } from '../../utils/aave/AaveEModeCategories'; import { useAaveReservesData } from './useAaveReservesData'; const eModeCategoriesFetcher = new AaveEModeCategories( - config.PoolAddress, + AAVE_CONTRACT_ADDRESSES.POOL, getProvider(BOB_CHAIN_ID), ); diff --git a/apps/frontend/src/hooks/aave/useAaveRepay.tsx b/apps/frontend/src/hooks/aave/useAaveRepay.tsx index 33500bba3..5d0f8cc05 100644 --- a/apps/frontend/src/hooks/aave/useAaveRepay.tsx +++ b/apps/frontend/src/hooks/aave/useAaveRepay.tsx @@ -8,7 +8,7 @@ import { Decimal } from '@sovryn/utils'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { useTransactionContext } from '../../contexts/TransactionContext'; import { translations } from '../../locales/i18n'; import { BorrowRateMode, TransactionFactoryOptions } from '../../types/aave'; @@ -24,8 +24,8 @@ export const useAaveRepay = () => { const aaveRepayTransactionsFactory = useMemo(() => { if (!signer) return null; return new AaveRepayTransactionsFactory( - config.PoolAddress, - config.WETHGatewayAddress, + AAVE_CONTRACT_ADDRESSES.POOL, + AAVE_CONTRACT_ADDRESSES.WETH_GATEWAY, signer, ); }, [signer]); diff --git a/apps/frontend/src/hooks/aave/useAaveReservesData.tsx b/apps/frontend/src/hooks/aave/useAaveReservesData.tsx index ca7172432..7e493d3c5 100644 --- a/apps/frontend/src/hooks/aave/useAaveReservesData.tsx +++ b/apps/frontend/src/hooks/aave/useAaveReservesData.tsx @@ -12,7 +12,7 @@ import { getProvider } from '@sovryn/ethers-provider'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { ETH, WETH } from '../../constants/currencies'; export type Reserve = ReserveDataHumanized & FormatReserveUSDResponse; @@ -20,7 +20,7 @@ export type ReserveData = { reserves: Reserve[]; loading: boolean }; const uiPoolDataProvider = new UiPoolDataProvider({ provider: getProvider(BOB_CHAIN_ID), - uiPoolDataProviderAddress: config.UiPoolDataProviderV3Address, + uiPoolDataProviderAddress: AAVE_CONTRACT_ADDRESSES.UI_POOL_DATA_PROVIDER, chainId: Number(BOB_CHAIN_ID), }); @@ -31,7 +31,8 @@ export const useAaveReservesData = (): ReserveData => { const fetchReservesData = useCallback(async () => { const currentTimestamp = dayjs().unix(); const reservesData = await uiPoolDataProvider.getReservesHumanized({ - lendingPoolAddressProvider: config.PoolAddressesProviderAddress, + lendingPoolAddressProvider: + AAVE_CONTRACT_ADDRESSES.POOL_ADDRESSES_PROVIDER, }); const formattedReserves = formatReserves({ reserves: reservesData.reservesData, diff --git a/apps/frontend/src/hooks/aave/useAaveSetUserEMode.tsx b/apps/frontend/src/hooks/aave/useAaveSetUserEMode.tsx index 42bc7ce2d..eeecd3a4d 100644 --- a/apps/frontend/src/hooks/aave/useAaveSetUserEMode.tsx +++ b/apps/frontend/src/hooks/aave/useAaveSetUserEMode.tsx @@ -2,7 +2,7 @@ import { useCallback, useMemo } from 'react'; import { t } from 'i18next'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { useTransactionContext } from '../../contexts/TransactionContext'; import { translations } from '../../locales/i18n'; import { EModeCategory, TransactionFactoryOptions } from '../../types/aave'; @@ -16,8 +16,14 @@ export const useAaveSetUserEMode = () => { const { setTransactions, setIsOpen, setTitle } = useTransactionContext(); const aaveEModeTransactionsFactory = useMemo(() => { - if (!signer) return null; - return new AaveEModeTransactionsFactory(config.PoolAddress, signer); + if (!signer) { + return null; + } + + return new AaveEModeTransactionsFactory( + AAVE_CONTRACT_ADDRESSES.POOL, + signer, + ); }, [signer]); const handleSetUserEMode = useCallback( diff --git a/apps/frontend/src/hooks/aave/useAaveSupply.tsx b/apps/frontend/src/hooks/aave/useAaveSupply.tsx index 7a952382e..73aad67e3 100644 --- a/apps/frontend/src/hooks/aave/useAaveSupply.tsx +++ b/apps/frontend/src/hooks/aave/useAaveSupply.tsx @@ -8,7 +8,7 @@ import { Decimal } from '@sovryn/utils'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { useTransactionContext } from '../../contexts/TransactionContext'; import { translations } from '../../locales/i18n'; import { TransactionFactoryOptions } from '../../types/aave'; @@ -24,8 +24,8 @@ export const useAaveSupply = () => { const aaveSupplyTransactionsFactory = useMemo(() => { if (!signer) return null; return new AaveSupplyTransactionsFactory( - config.PoolAddress, - config.WETHGatewayAddress, + AAVE_CONTRACT_ADDRESSES.POOL, + AAVE_CONTRACT_ADDRESSES.WETH_GATEWAY, signer, ); }, [signer]); diff --git a/apps/frontend/src/hooks/aave/useAaveUserReservesData.tsx b/apps/frontend/src/hooks/aave/useAaveUserReservesData.tsx index e1658dd85..b325b4fad 100644 --- a/apps/frontend/src/hooks/aave/useAaveUserReservesData.tsx +++ b/apps/frontend/src/hooks/aave/useAaveUserReservesData.tsx @@ -11,7 +11,7 @@ import { getProvider } from '@sovryn/ethers-provider'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { UserReservesData } from '../../types/aave'; import { AaveUserReservesSummary, @@ -22,7 +22,7 @@ import { useBlockNumber } from '../useBlockNumber'; const uiPoolDataProvider = new UiPoolDataProvider({ provider: getProvider(BOB_CHAIN_ID), - uiPoolDataProviderAddress: config.UiPoolDataProviderV3Address, + uiPoolDataProviderAddress: AAVE_CONTRACT_ADDRESSES.UI_POOL_DATA_PROVIDER, chainId: Number(BOB_CHAIN_ID), }); @@ -52,10 +52,12 @@ export const useAaveUserReservesData = (): { try { const [reservesData, userReservesData] = await Promise.all([ uiPoolDataProvider.getReservesHumanized({ - lendingPoolAddressProvider: config.PoolAddressesProviderAddress, + lendingPoolAddressProvider: + AAVE_CONTRACT_ADDRESSES.POOL_ADDRESSES_PROVIDER, }), uiPoolDataProvider.getUserReservesHumanized({ - lendingPoolAddressProvider: config.PoolAddressesProviderAddress, + lendingPoolAddressProvider: + AAVE_CONTRACT_ADDRESSES.POOL_ADDRESSES_PROVIDER, user: account, }), ]); diff --git a/apps/frontend/src/hooks/aave/useAaveWithdraw.tsx b/apps/frontend/src/hooks/aave/useAaveWithdraw.tsx index f394bc80e..f2f946a77 100644 --- a/apps/frontend/src/hooks/aave/useAaveWithdraw.tsx +++ b/apps/frontend/src/hooks/aave/useAaveWithdraw.tsx @@ -8,7 +8,7 @@ import { Decimal } from '@sovryn/utils'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { useTransactionContext } from '../../contexts/TransactionContext'; import { translations } from '../../locales/i18n'; import { TransactionFactoryOptions } from '../../types/aave'; @@ -24,8 +24,8 @@ export const useAaveWithdraw = () => { const aaveWithdrawTransactionsFactory = useMemo(() => { if (!signer) return null; return new AaveWithdrawTransactionsFactory( - config.PoolAddress, - config.WETHGatewayAddress, + AAVE_CONTRACT_ADDRESSES.POOL, + AAVE_CONTRACT_ADDRESSES.WETH_GATEWAY, signer, ); }, [signer]); diff --git a/apps/frontend/src/utils/aave/AaveBorrowTransactionsFactory.ts b/apps/frontend/src/utils/aave/AaveBorrowTransactionsFactory.ts index b16da8fcf..75d822dcb 100644 --- a/apps/frontend/src/utils/aave/AaveBorrowTransactionsFactory.ts +++ b/apps/frontend/src/utils/aave/AaveBorrowTransactionsFactory.ts @@ -9,7 +9,7 @@ import { Transaction, TransactionType, } from '../../app/3_organisms/TransactionStepDialog/TransactionStepDialog.types'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { translations } from '../../locales/i18n'; import { BorrowRateMode, TransactionFactoryOptions } from '../../types/aave'; @@ -56,7 +56,7 @@ export class AaveBorrowTransactionsFactory { ): Promise { if ( asset.isNative || - asset.address.toLowerCase() === config.WETHAddress.toLowerCase() + asset.address.toLowerCase() === AAVE_CONTRACT_ADDRESSES.WETH.toLowerCase() ) { return this.borrowNative(amount, rateMode, opts); } else return this.borrowToken(asset, amount, rateMode, opts); diff --git a/apps/frontend/src/utils/aave/AaveRepayTransactionsFactory.ts b/apps/frontend/src/utils/aave/AaveRepayTransactionsFactory.ts index 98c94269f..441a12e9f 100644 --- a/apps/frontend/src/utils/aave/AaveRepayTransactionsFactory.ts +++ b/apps/frontend/src/utils/aave/AaveRepayTransactionsFactory.ts @@ -9,7 +9,10 @@ import { Transaction, TransactionType, } from '../../app/3_organisms/TransactionStepDialog/TransactionStepDialog.types'; -import { config } from '../../constants/aave'; +import { + AAVE_CONTRACT_ADDRESSES, + REPAY_ALL_ETH_SURPLUS_AAVE, +} from '../../constants/aave'; import { translations } from '../../locales/i18n'; import { BorrowRateMode, TransactionFactoryOptions } from '../../types/aave'; import { prepareApproveTransaction } from '../transactions'; @@ -49,7 +52,7 @@ export class AaveRepayTransactionsFactory { ): Promise { if ( token.isNative || - token.address.toLowerCase() === config.WETHAddress.toLowerCase() + token.address.toLowerCase() === AAVE_CONTRACT_ADDRESSES.WETH.toLowerCase() ) { return this.repayNative(amount, isEntireDebt, borrowRateMode, opts); } else @@ -108,7 +111,7 @@ export class AaveRepayTransactionsFactory { constants.AddressZero, BOB_CHAIN_ID, ); - amount = isEntireDebt ? amount.add(config.RepayAllETHSurplus) : amount; + amount = isEntireDebt ? amount.add(REPAY_ALL_ETH_SURPLUS_AAVE) : amount; return [ { diff --git a/apps/frontend/src/utils/aave/AaveSupplyTransactionsFactory.ts b/apps/frontend/src/utils/aave/AaveSupplyTransactionsFactory.ts index 09c017444..c5c2bb43b 100644 --- a/apps/frontend/src/utils/aave/AaveSupplyTransactionsFactory.ts +++ b/apps/frontend/src/utils/aave/AaveSupplyTransactionsFactory.ts @@ -9,7 +9,7 @@ import { Transaction, TransactionType, } from '../../app/3_organisms/TransactionStepDialog/TransactionStepDialog.types'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { translations } from '../../locales/i18n'; import { TransactionFactoryOptions } from '../../types/aave'; import { prepareApproveTransaction } from '../transactions'; @@ -48,7 +48,7 @@ export class AaveSupplyTransactionsFactory { ): Promise { if ( token.isNative || - token.address.toLowerCase() === config.WETHAddress.toLowerCase() + token.address.toLowerCase() === AAVE_CONTRACT_ADDRESSES.WETH.toLowerCase() ) { return this.supplyNative(amount, opts); } else return this.supplyToken(token, amount, opts); @@ -59,7 +59,9 @@ export class AaveSupplyTransactionsFactory { useAsCollateral: boolean, opts?: TransactionFactoryOptions, ): Promise { - const tokenAddress = token.isNative ? config.WETHAddress : token.address; + const tokenAddress = token.isNative + ? AAVE_CONTRACT_ADDRESSES.WETH + : token.address; return [ { diff --git a/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts b/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts index 0dfeaaf7b..32fab812f 100644 --- a/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts +++ b/apps/frontend/src/utils/aave/AaveUserReservesSummary.ts @@ -17,7 +17,7 @@ import { Decimal } from '@sovryn/utils'; import { BOB_CHAIN_ID } from '../../config/chains'; -import { config } from '../../constants/aave'; +import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../constants/aave'; import { Reserve } from '../../hooks/aave/useAaveReservesData'; import { BorrowRateMode, UserReservesData } from '../../types/aave'; import { decimalic, fromWei } from '../math'; @@ -162,7 +162,7 @@ export class AaveUserReservesSummaryFactory { userSummary.currentLiquidationThreshold, ); const borrowPower = AaveCalculations.computeBorrowPower( - config.MinCollateralRatio, + MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE, collateralBalance, ); const borrowPowerUsed = AaveCalculations.computeBorrowPowerUsed( diff --git a/apps/frontend/src/utils/aave/AaveWithdrawTransactionsFactory.ts b/apps/frontend/src/utils/aave/AaveWithdrawTransactionsFactory.ts index dfbab462a..81ea5eb89 100644 --- a/apps/frontend/src/utils/aave/AaveWithdrawTransactionsFactory.ts +++ b/apps/frontend/src/utils/aave/AaveWithdrawTransactionsFactory.ts @@ -9,7 +9,7 @@ import { Transaction, TransactionType, } from '../../app/3_organisms/TransactionStepDialog/TransactionStepDialog.types'; -import { config } from '../../constants/aave'; +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; import { translations } from '../../locales/i18n'; import { TransactionFactoryOptions } from '../../types/aave'; import { prepareApproveTransaction } from '../transactions'; @@ -44,7 +44,7 @@ export class AaveWithdrawTransactionsFactory { ): Promise { if ( token.isNative || - token.address.toLowerCase() === config.WETHAddress.toLowerCase() + token.address.toLowerCase() === AAVE_CONTRACT_ADDRESSES.WETH.toLowerCase() ) { return this.withdrawNative(amount, isMaxAmount, opts); } else return this.withdrawToken(token, amount, isMaxAmount, opts); From 6968768e67687c282dd86ed983e726bc1625e918 Mon Sep 17 00:00:00 2001 From: matzapata Date: Mon, 9 Sep 2024 16:38:40 +0400 Subject: [PATCH 098/116] small fixes --- .../components/BorrowDetailsGraph/BorrowDetailsGraph.tsx | 4 ++-- .../components/SupplyDetailsGraph/SupplyDetailsGraph.tsx | 2 +- apps/frontend/src/utils/math.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx index 747f2f774..16c1bd5f8 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/BorrowDetailsGraph/BorrowDetailsGraph.tsx @@ -102,7 +102,7 @@ export const BorrowDetailsGraph: FC = ({ label={t(pageTranslations.apr)} value={ @@ -144,7 +144,7 @@ export const BorrowDetailsGraph: FC = ({ help={t(pageTranslations.reserveFactorInfo)} value={ diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx index 877aa801f..2693d8687 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/SupplyDetailsGraph/SupplyDetailsGraph.tsx @@ -103,7 +103,7 @@ export const SupplyDetailsGraph: FC = ({ label={t(pageTranslations.apy)} value={ diff --git a/apps/frontend/src/utils/math.ts b/apps/frontend/src/utils/math.ts index 08ca66913..68b86bf6c 100644 --- a/apps/frontend/src/utils/math.ts +++ b/apps/frontend/src/utils/math.ts @@ -173,6 +173,6 @@ export const formatAmountWithSuffix = ( } else if (value.gte(1e3)) { return { value: value.div(1e3).toString(1), suffix: 'K' }; } else { - return { value: value.toString(), suffix: '' }; + return { value: value.toString(1), suffix: '' }; } }; From c9256bee91fa9f75397ce2db16d7945423d522f4 Mon Sep 17 00:00:00 2001 From: matzapata Date: Mon, 9 Sep 2024 17:06:06 +0400 Subject: [PATCH 099/116] remove unused files and fix repay with balance future collateral ratio --- .../RepayForm/RepayWithWalletBalanceForm.tsx | 4 +- .../RepayModal/RepayModalContainer.tsx | 77 ------- .../RepayModal/RepayWithCollateralForm.tsx | 197 ------------------ .../RepayModal/RepayWithWalletBalanceForm.tsx | 140 ------------- 4 files changed, 2 insertions(+), 416 deletions(-) delete mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx delete mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx delete mode 100644 apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithWalletBalanceForm.tsx diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithWalletBalanceForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithWalletBalanceForm.tsx index a7688a30d..a97d535c7 100644 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithWalletBalanceForm.tsx +++ b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayForm/RepayWithWalletBalanceForm.tsx @@ -97,9 +97,9 @@ export const RepayWithWalletBalanceForm: FC< const newCollateralRatio = useMemo(() => { return AaveCalculations.computeCollateralRatio( summary.collateralBalance, - summary.borrowBalance.add(newDebtAmountUSD), + summary.borrowBalance.sub(repayUsdAmount), ); - }, [summary, newDebtAmountUSD]); + }, [summary, repayUsdAmount]); const isValidRepayAmount = useMemo( () => (repaySize.gt(0) ? repaySize.lte(maximumRepayAmount) : true), diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx deleted file mode 100644 index 448de1cf4..000000000 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayModalContainer.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React, { FC, useState } from 'react'; - -import { t } from 'i18next'; - -import { - Dialog, - DialogBody, - DialogHeader, - Paragraph, - ParagraphSize, - Tabs, - TabType, -} from '@sovryn/ui'; - -import { translations } from '../../../../../../../locales/i18n'; -import { RepayWithCollateralForm } from './RepayWithCollateralForm'; -import { RepayWithWalletBalanceForm } from './RepayWithWalletBalanceForm'; - -type RepayModalContainerProps = { - isOpen: boolean; - handleCloseModal: () => void; -}; - -enum RepayWith { - BALANCE = 0, - COLLATERAL, -} - -export const RepayModalContainer: FC = ({ - isOpen, - handleCloseModal, -}) => { - const [activeTab, setActiveTab] = useState(RepayWith.BALANCE); - - return ( - - - - - {t(translations.aavePage.repayModal.repayWith)} - - - - - {activeTab === RepayWith.BALANCE ? ( - - ) : ( - - )} - - - ); -}; diff --git a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx b/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx deleted file mode 100644 index f182a4eeb..000000000 --- a/apps/frontend/src/app/5_pages/AavePage/components/BorrowPositionsList/components/RepayModal/RepayWithCollateralForm.tsx +++ /dev/null @@ -1,197 +0,0 @@ -import React, { FC, useMemo, useState } from 'react'; - -import { t } from 'i18next'; - -import { - Button, - ErrorBadge, - ErrorLevel, - HelperButton, - SimpleTable, - SimpleTableRow, -} from '@sovryn/ui'; -import { Decimal } from '@sovryn/utils'; - -import { BOB_CHAIN_ID } from '../../../../../../../config/chains'; - -import { AmountRenderer } from '../../../../../../2_molecules/AmountRenderer/AmountRenderer'; -import { AmountTransition } from '../../../../../../2_molecules/AmountTransition/AmountTransition'; -import { AssetAmountInput } from '../../../../../../2_molecules/AssetAmountInput/AssetAmountInput'; -import { AssetRenderer } from '../../../../../../2_molecules/AssetRenderer/AssetRenderer'; -import { MINIMUM_COLLATERAL_RATIO_LENDING_POOLS_AAVE } from '../../../../../../../constants/aave'; -import { useDecimalAmountInput } from '../../../../../../../hooks/useDecimalAmountInput'; -import { translations } from '../../../../../../../locales/i18n'; -import { CollateralRatioHealthBar } from '../../../CollateralRatioHealthBar/CollateralRatioHealthBar'; - -const pageTranslations = translations.aavePage; - -type RepayWithCollateralFormProps = { - onSuccess: () => void; -}; - -export const RepayWithCollateralForm: FC = () => { - const assetPrice = 3258.47; // TODO: this is mocked data. Replace with proper hook - const totalBorrowed = Decimal.from(10); // TODO: this is mocked data. Replace with proper hook - const collateralToLoanRate = Decimal.from(10); // TODO: this is mocked data. Replace with proper hook - const collateralSize = Decimal.from(10); // TODO: this is mockd data. Replace with proper hook - const assets = useMemo(() => ['BTC', 'SOV'], []); // TODO: this is mocked data. Replace with proper hook - const [maximumRepayAmount] = useState(Decimal.from(10)); // TODO: this is mocked data. Replace with proper hook - const [maximumRepayWithAmount] = useState(Decimal.from(10)); // TODO: this is mocked data. Replace with proper hook - const [repayAsset, setRepayAsset] = useState(assets[0]); - const [repayAmount, setRepayAmount, repaySize] = useDecimalAmountInput(''); - const [repayWithAsset, setRepayWithAsset] = useState(assets[0]); - const [repayWithAmount, setRepayWithAmount, repayWithSize] = - useDecimalAmountInput(''); - - const repayAssetsOptions = useMemo( - () => - assets.map(token => ({ - value: token, - label: ( - - ), - })), - [assets], - ); - - const remainingDebt = useMemo( - () => maximumRepayAmount.sub(repaySize), - [repaySize, maximumRepayAmount], - ); - - const collateralRatio = useMemo(() => { - if ([collateralSize, totalBorrowed, repaySize].some(v => v.isZero())) { - return Decimal.from(0); - } - - return collateralSize.mul(collateralToLoanRate).div(totalBorrowed).mul(100); - }, [collateralSize, totalBorrowed, repaySize, collateralToLoanRate]); - - // TODO: add more validations - const isValidRepayAmount = useMemo( - () => (repaySize.gt(0) ? repaySize.lte(maximumRepayAmount) : true), - [repaySize, maximumRepayAmount], - ); - - // TODO: add more validations - const isValidRepayWithAmount = useMemo( - () => - repayWithSize.gt(0) ? repayWithSize.lte(maximumRepayWithAmount) : true, - [repayWithSize, maximumRepayWithAmount], - ); - - // TODO: add more validations - const submitButtonDisabled = useMemo( - () => - !isValidRepayAmount || - repaySize.lte(0) || - !isValidRepayWithAmount || - repayWithSize.lte(0), - [isValidRepayAmount, repaySize, isValidRepayWithAmount, repayWithSize], - ); - - return ( - -
- - - {!isValidRepayAmount && ( - - )} -
- -
- - - {!isValidRepayWithAmount && ( - - )} -
- - - - - - } - /> - - - -
- } - /> - - {t(translations.aavePage.repayModal.priceImpact)}{' '} - -
- } - value={} - /> - - -
- } - /> - - -
- } - /> - - {t(translations.aavePage.repayModal.priceImpact)}{' '} - -
- } - value={} - /> - - -
- } - /> - - - + + +
+ ); +}; diff --git a/apps/frontend/src/constants/aave.ts b/apps/frontend/src/constants/aave.ts index 9c5fa4dfb..fe83dccc3 100644 --- a/apps/frontend/src/constants/aave.ts +++ b/apps/frontend/src/constants/aave.ts @@ -1,4 +1,5 @@ import { IS_MAINNET } from '../config/chains'; + import { decimalic } from '../utils/math'; export const EMODE_DISABLED_ID = 0; @@ -25,4 +26,5 @@ export const AAVE_CONTRACT_ADDRESSES = IS_MAINNET WETH: '0x8CEc2719a2e896A11eA3f10406EfDb6Ad87281D9', TREASURY: '0x2a9d8f5b2f7b8f5b4b5d5e7f3b4b5d5b5d5d5d5d', INTEREST_RATE_STRATEGY: '0x847A3364Cc5fE389283bD821cfC8A477288D9e82', + RATES_HISTORY_API_URL: process.env.REACT_APP_RATES_HISTORY_API_URL, }; diff --git a/apps/frontend/src/hooks/aave/useAaveRates.tsx b/apps/frontend/src/hooks/aave/useAaveRates.tsx new file mode 100644 index 000000000..0256f4f99 --- /dev/null +++ b/apps/frontend/src/hooks/aave/useAaveRates.tsx @@ -0,0 +1,68 @@ +import { useMemo, useState } from 'react'; + +import { formatUnits } from 'ethers/lib/utils'; + +import { Decimal } from '@sovryn/utils'; + +import { AaveCalculations } from '../../utils/aave/AaveCalculations'; +import { RAY_DECIMALS } from '../../utils/math'; +import { useAaveReservesData } from './useAaveReservesData'; + +export interface RatesDataResult { + currentUsageRatio: Decimal; + optimalUsageRatio: Decimal; + baseVariableBorrowRate: Decimal; + variableRateSlope1: Decimal; + variableRateSlope2: Decimal; + underlyingAsset: string; + name: string; + symbol: string; + decimals: number; +} + +export const useAaveInterestRatesData = ( + symbol: string, +): { + data: RatesDataResult | null; + error: string | null; +} => { + const [data, setData] = useState(null); + const [error, setError] = useState(null); + const { reserves } = useAaveReservesData(); + useMemo(() => { + const reserve = reserves.find( + r => r.symbol.toLocaleLowerCase() === symbol.toLocaleLowerCase(), + ); + if (!reserve) return; + try { + const utilizationRate = AaveCalculations.calculateUtilizationRate( + reserve.decimals, + reserve.totalDebt, + reserve.availableLiquidity, + ); + setData({ + currentUsageRatio: utilizationRate, + optimalUsageRatio: Decimal.from( + formatUnits(reserve.optimalUsageRatio, RAY_DECIMALS), + ), + baseVariableBorrowRate: Decimal.from( + formatUnits(reserve.baseVariableBorrowRate, RAY_DECIMALS), + ), + variableRateSlope1: Decimal.from( + formatUnits(reserve.variableRateSlope1, RAY_DECIMALS), + ), + variableRateSlope2: Decimal.from( + formatUnits(reserve.variableRateSlope2, RAY_DECIMALS), + ), + underlyingAsset: reserve.underlyingAsset, + name: reserve.name, + symbol: reserve.symbol, + decimals: reserve.decimals, + }); + } catch (error) { + setError(error.message); + } + }, [reserves, symbol]); + + return { data, error }; +}; diff --git a/apps/frontend/src/hooks/aave/useAaveReservesHistory.tsx b/apps/frontend/src/hooks/aave/useAaveReservesHistory.tsx new file mode 100644 index 000000000..790dbb950 --- /dev/null +++ b/apps/frontend/src/hooks/aave/useAaveReservesHistory.tsx @@ -0,0 +1,152 @@ +import { useCallback, useEffect, useState } from 'react'; + +import axios from 'axios'; +import dayjs from 'dayjs'; + +import { AAVE_CONTRACT_ADDRESSES } from '../../constants/aave'; + +export enum ESupportedTimeRanges { + OneMonth = '1m', + ThreeMonths = '3m', + SixMonths = '6m', + OneYear = '1y', + TwoYears = '2y', + FiveYears = '5y', +} + +export const reserveRateTimeRangeOptions = [ + ESupportedTimeRanges.OneMonth, + ESupportedTimeRanges.SixMonths, + ESupportedTimeRanges.OneYear, +]; +export type ReserveRateTimeRange = typeof reserveRateTimeRangeOptions[number]; + +type RatesHistoryParams = { + from: number; + resolutionInHours: number; +}; + +type APIResponse = { + liquidityRate_avg: number; + variableBorrowRate_avg: number; + stableBorrowRate_avg: number; + utilizationRate_avg: number; + x: { year: number; month: number; date: number; hours: number }; +}; + +const requestCache = new Map>(); +const fetchStats = async ( + reserveId: string, + timeRange: ReserveRateTimeRange, + endpointURL: string, +): Promise => { + const { from, resolutionInHours } = resolutionForTimeRange(timeRange); + const qs = `reserveId=${reserveId}&from=${from}&resolutionInHours=${resolutionInHours}`; + const url = `${endpointURL}?${qs}`; + + if (requestCache.has(url)) { + return requestCache.get(url)!; + } + + const requestPromise = axios + .get(url) + .then(response => response.data) + .finally(() => { + requestCache.delete(url); + }); + + requestCache.set(url, requestPromise); + + return requestPromise; +}; + +const resolutionForTimeRange = ( + timeRange: ReserveRateTimeRange, +): RatesHistoryParams => { + switch (timeRange) { + case ESupportedTimeRanges.OneMonth: + return { + from: dayjs().subtract(30, 'day').unix(), + resolutionInHours: 6, + }; + case ESupportedTimeRanges.SixMonths: + return { + from: dayjs().subtract(6, 'month').unix(), + resolutionInHours: 24, + }; + case ESupportedTimeRanges.OneYear: + return { + from: dayjs().subtract(1, 'year').unix(), + resolutionInHours: 24, + }; + default: + return { + // Return today as a fallback + from: dayjs().unix(), + resolutionInHours: 6, + }; + } +}; + +export type FormattedReserveHistoryItem = { + date: number; + liquidityRate: number; + variableBorrowRate: number; +}; + +export function useAaveReservesHistory( + reserveId: string, + timeRange: ReserveRateTimeRange, +) { + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + const [data, setData] = useState([]); + + const refetchData = useCallback(() => { + if (reserveId && AAVE_CONTRACT_ADDRESSES.RATES_HISTORY_API_URL) { + // reset + setLoading(true); + setError(false); + setData([]); + fetchStats( + reserveId, + timeRange, + AAVE_CONTRACT_ADDRESSES.RATES_HISTORY_API_URL, + ) + .then((data: APIResponse[]) => { + setData( + data.map(d => ({ + date: dayjs() + .set('year', d.x.year) + .set('month', d.x.month) + .set('date', d.x.date) + .set('hour', d.x.hours) + .valueOf(), + liquidityRate: d.liquidityRate_avg, + variableBorrowRate: d.variableBorrowRate_avg, + })), + ); + }) + .catch(e => { + console.error( + 'useReservesHistory(): Failed to fetch historical reserve data.', + e, + ); + setError(true); + }) + .finally(() => setLoading(false)); + } + + return () => null; + }, [reserveId, timeRange]); + + useEffect(() => { + refetchData(); + }, [refetchData]); + return { + loading, + data, + error, + refetch: refetchData, + }; +} diff --git a/apps/frontend/src/locales/en/translations.json b/apps/frontend/src/locales/en/translations.json index 3767d3445..cae5b345c 100644 --- a/apps/frontend/src/locales/en/translations.json +++ b/apps/frontend/src/locales/en/translations.json @@ -999,7 +999,7 @@ "of": "of", "apy": "APY", "chart": { - "label1": "Supply APR" + "suppApr": "Supply APR" } }, "borrowDetails": { @@ -1015,7 +1015,7 @@ "collectorContract": "Collector contract", "viewContract": "View contract", "chart": { - "label1": "Borrow APR, variable" + "aprVarLabel": "Borrow APR, variable" } }, "interestRateModel": { @@ -1027,7 +1027,7 @@ "collectorContract": "Collector contract", "viewContract": "View contract", "chart": { - "label1": "Borrow APR, variable" + "aprVarLabel": "Borrow APR, variable" } } }, diff --git a/apps/frontend/src/utils/aave/AaveCalculations.ts b/apps/frontend/src/utils/aave/AaveCalculations.ts index 910b964b5..00246dabd 100644 --- a/apps/frontend/src/utils/aave/AaveCalculations.ts +++ b/apps/frontend/src/utils/aave/AaveCalculations.ts @@ -1,3 +1,5 @@ +import { utils } from 'ethers'; + import { Decimal } from '@sovryn/utils'; import { UserSummary } from './AaveUserReservesSummary'; @@ -128,4 +130,41 @@ export class AaveCalculations { } return borrowSize.mul(currentLiquidationThreshold).div(collateralBalance); } + + static calculateUtilizationRate = ( + decimals: number, + totalDebt: string, + availableLiquidity: string, + ): Decimal => { + const totalBorrowBigInt = BigInt( + utils.parseUnits(totalDebt, decimals).toString(), + ); + const availableLiquidityBigInt = BigInt(availableLiquidity); + + const totalSupplyBigInt = totalBorrowBigInt + availableLiquidityBigInt; + + if (totalSupplyBigInt === BigInt(0)) { + return Decimal.from(0); + } + + const utilizationRateBigInt = + (totalBorrowBigInt * BigInt(10 ** decimals)) / totalSupplyBigInt; + + const utilizationRate = utils.formatUnits( + utilizationRateBigInt.toString(), + decimals, + ); + + const utilizationRateDecimal = Decimal.from(utilizationRate); + + const isBetweenZeroAndOne = + utilizationRateDecimal.gte(Decimal.from(0)) && + utilizationRateDecimal.lte(Decimal.from(1)); + + if (!isBetweenZeroAndOne) { + return Decimal.from(0); + } + + return utilizationRateDecimal; + }; } diff --git a/apps/frontend/src/utils/math.ts b/apps/frontend/src/utils/math.ts index 68b86bf6c..cd4b0d1a3 100644 --- a/apps/frontend/src/utils/math.ts +++ b/apps/frontend/src/utils/math.ts @@ -11,6 +11,8 @@ const DEFAULT_DECIMALS = 6; const DEFAULT_DECIMALS_SEPARATOR = '.'; const DEFAULT_THOUSANDS_SEPARATOR = ','; +export const RAY_DECIMALS = 27; + const unitNames = ['wei', 'kwei', 'mwei', 'gwei', 'szabo', 'finney', 'ether']; // helper function to convert any type of ethers value to wei. From 783e1450aa67ca1be4a275e844958d07cfd765a4 Mon Sep 17 00:00:00 2001 From: juandahl Date: Thu, 12 Sep 2024 17:26:42 -0300 Subject: [PATCH 114/116] fix(ui): fix size of wallet card --- .../5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx index b0109e6fa..eea55516d 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/AaveReserveOverviewPage.tsx @@ -84,7 +84,7 @@ const AaveReserveOverviewPage: FC = () => {
From 549748a01a8ab340fa55b5e3823de7468ace2bba Mon Sep 17 00:00:00 2001 From: Luciano Perez Cerra Date: Fri, 13 Sep 2024 10:14:00 -0300 Subject: [PATCH 115/116] Apply suggestions from code review Co-authored-by: Pietro <74987028+pietro-maximoff@users.noreply.github.com> --- .../InterestRateModelGraph/InterestRateModelGraph.tsx | 7 +++++-- apps/frontend/src/hooks/aave/useAaveRates.tsx | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx index 6c2e7f447..98e9dd2ee 100644 --- a/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx +++ b/apps/frontend/src/app/5_pages/AaveReserveOverviewPage/components/InterestRateModelGraph/InterestRateModelGraph.tsx @@ -16,6 +16,7 @@ import { useIsMobile } from '../../../../../hooks/useIsMobile'; import { translations } from '../../../../../locales/i18n'; import { getBobExplorerUrl } from '../../../../../utils/helpers'; import { Chart } from './components/Chart/Chart'; +import { COMMON_SYMBOLS } from '../../../../../utils/asset'; const pageTranslations = translations.aaveReserveOverviewPage.interestRateModel; @@ -27,7 +28,7 @@ export const InterestRateModelGraph: FC = ({ reserve, }) => { const [searchParams] = useSearchParams(); - const symbol = searchParams.get('asset') || 'ETH'; + const symbol = searchParams.get('asset') || COMMON_SYMBOLS.ETH; const { data: rates } = useAaveInterestRatesData(symbol); const { isMobile } = useIsMobile(); const [open, setOpen] = useState(true); @@ -50,7 +51,9 @@ export const InterestRateModelGraph: FC = ({ [reserve.borrowUsageRatio], ); - if (!rates) return null; + if (!rates) { + return null; + } return ( r.symbol.toLocaleLowerCase() === symbol.toLocaleLowerCase(), ); - if (!reserve) return; + if (!reserve) { + return; + } try { const utilizationRate = AaveCalculations.calculateUtilizationRate( reserve.decimals, From 500fe4c650d9395c5d2ccd4a1397d8541e7736ee Mon Sep 17 00:00:00 2001 From: Luciano Perez Cerra Date: Fri, 13 Sep 2024 10:18:23 -0300 Subject: [PATCH 116/116] @pietro-maximoff suggestion applied: 'it doesn't need to be changed' --- apps/frontend/src/hooks/aave/useAaveBorrow.tsx | 4 +++- apps/frontend/src/hooks/aave/useAaveRepay.tsx | 4 +++- apps/frontend/src/hooks/aave/useAaveSupply.tsx | 4 +++- apps/frontend/src/hooks/aave/useAaveWithdraw.tsx | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/frontend/src/hooks/aave/useAaveBorrow.tsx b/apps/frontend/src/hooks/aave/useAaveBorrow.tsx index c9baee288..2b1587021 100644 --- a/apps/frontend/src/hooks/aave/useAaveBorrow.tsx +++ b/apps/frontend/src/hooks/aave/useAaveBorrow.tsx @@ -22,7 +22,9 @@ export const useAaveBorrow = () => { const { setTransactions, setIsOpen, setTitle } = useTransactionContext(); const aaveBorrowTransactionsFactory = useMemo(() => { - if (!signer) return null; + if (!signer) { + return null; + } return new AaveBorrowTransactionsFactory( AAVE_CONTRACT_ADDRESSES.POOL, AAVE_CONTRACT_ADDRESSES.WETH_GATEWAY, diff --git a/apps/frontend/src/hooks/aave/useAaveRepay.tsx b/apps/frontend/src/hooks/aave/useAaveRepay.tsx index 5d0f8cc05..f76a41695 100644 --- a/apps/frontend/src/hooks/aave/useAaveRepay.tsx +++ b/apps/frontend/src/hooks/aave/useAaveRepay.tsx @@ -22,7 +22,9 @@ export const useAaveRepay = () => { const { setTransactions, setIsOpen, setTitle } = useTransactionContext(); const aaveRepayTransactionsFactory = useMemo(() => { - if (!signer) return null; + if (!signer) { + return null; + } return new AaveRepayTransactionsFactory( AAVE_CONTRACT_ADDRESSES.POOL, AAVE_CONTRACT_ADDRESSES.WETH_GATEWAY, diff --git a/apps/frontend/src/hooks/aave/useAaveSupply.tsx b/apps/frontend/src/hooks/aave/useAaveSupply.tsx index 73aad67e3..ebd6595ec 100644 --- a/apps/frontend/src/hooks/aave/useAaveSupply.tsx +++ b/apps/frontend/src/hooks/aave/useAaveSupply.tsx @@ -22,7 +22,9 @@ export const useAaveSupply = () => { const { setTransactions, setIsOpen, setTitle } = useTransactionContext(); const aaveSupplyTransactionsFactory = useMemo(() => { - if (!signer) return null; + if (!signer) { + return null; + } return new AaveSupplyTransactionsFactory( AAVE_CONTRACT_ADDRESSES.POOL, AAVE_CONTRACT_ADDRESSES.WETH_GATEWAY, diff --git a/apps/frontend/src/hooks/aave/useAaveWithdraw.tsx b/apps/frontend/src/hooks/aave/useAaveWithdraw.tsx index f2f946a77..cfa18870c 100644 --- a/apps/frontend/src/hooks/aave/useAaveWithdraw.tsx +++ b/apps/frontend/src/hooks/aave/useAaveWithdraw.tsx @@ -22,7 +22,9 @@ export const useAaveWithdraw = () => { const { setTransactions, setIsOpen, setTitle } = useTransactionContext(); const aaveWithdrawTransactionsFactory = useMemo(() => { - if (!signer) return null; + if (!signer) { + return null; + } return new AaveWithdrawTransactionsFactory( AAVE_CONTRACT_ADDRESSES.POOL, AAVE_CONTRACT_ADDRESSES.WETH_GATEWAY,