From 47631a1818e2a77f6b125bf1f46754d98f56197d Mon Sep 17 00:00:00 2001 From: Daizze Date: Fri, 12 Jul 2024 23:26:40 +0200 Subject: [PATCH 01/27] Adding Lend page and components --- public/images/tokens/weETH.png | Bin 0 -> 29092 bytes src/bao/lib/config.ts | 90 ++++++++++ src/bao/lib/types.ts | 32 ++++ src/components/Nav/Nav.tsx | 1 + src/hooks/lend/useAccountBalances.ts | 62 +++++++ src/hooks/lend/useSupplyBalance.ts | 57 +++++++ src/pages/lend/[market].tsx | 108 ++++++++++++ src/pages/lend/components/AssetsCard.tsx | 22 +++ src/pages/lend/components/BorrowList.tsx | 80 +++++++++ src/pages/lend/components/BorrowedCard.tsx | 25 +++ .../lend/components/Buttons/SupplyButton.tsx | 77 +++++++++ src/pages/lend/components/MarketButton.tsx | 157 +++++++++++++++++ src/pages/lend/components/MarketList.tsx | 59 +++++++ .../lend/components/Modals/BorrowModal.tsx | 51 ++++++ .../lend/components/Modals/RepayModal.tsx | 160 ++++++++++++++++++ .../lend/components/Modals/SupplyModal.tsx | 68 ++++++++ .../lend/components/Modals/WithdrawModal.tsx | 146 ++++++++++++++++ src/pages/lend/components/SuppliedCard.tsx | 25 +++ src/pages/lend/components/SupplyList.tsx | 98 +++++++++++ src/pages/lend/index.tsx | 32 ++++ 20 files changed, 1350 insertions(+) create mode 100644 public/images/tokens/weETH.png create mode 100644 src/hooks/lend/useAccountBalances.ts create mode 100644 src/hooks/lend/useSupplyBalance.ts create mode 100644 src/pages/lend/[market].tsx create mode 100644 src/pages/lend/components/AssetsCard.tsx create mode 100644 src/pages/lend/components/BorrowList.tsx create mode 100644 src/pages/lend/components/BorrowedCard.tsx create mode 100644 src/pages/lend/components/Buttons/SupplyButton.tsx create mode 100644 src/pages/lend/components/MarketButton.tsx create mode 100644 src/pages/lend/components/MarketList.tsx create mode 100644 src/pages/lend/components/Modals/BorrowModal.tsx create mode 100644 src/pages/lend/components/Modals/RepayModal.tsx create mode 100644 src/pages/lend/components/Modals/SupplyModal.tsx create mode 100644 src/pages/lend/components/Modals/WithdrawModal.tsx create mode 100644 src/pages/lend/components/SuppliedCard.tsx create mode 100644 src/pages/lend/components/SupplyList.tsx create mode 100644 src/pages/lend/index.tsx diff --git a/public/images/tokens/weETH.png b/public/images/tokens/weETH.png new file mode 100644 index 0000000000000000000000000000000000000000..dc03f6c96afd524b008c00b2d2beb363f1d51771 GIT binary patch literal 29092 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 = ({ href, exact }) => { ['1', 'SWAP', '/swap'], ['2', 'EARN', '/earn'], ['3', 'VEBAO', '/vebao'], + ['4', 'LEND', 'lend'], ] return ( diff --git a/src/hooks/lend/useAccountBalances.ts b/src/hooks/lend/useAccountBalances.ts new file mode 100644 index 00000000..228abc8c --- /dev/null +++ b/src/hooks/lend/useAccountBalances.ts @@ -0,0 +1,62 @@ +import { BigNumber } from 'ethers' +import useBao from '@/hooks/base/useBao' +import { useWeb3React } from '@web3-react/core' +import { useQuery } from '@tanstack/react-query' +import { providerKey } from '@/utils/index' +import { Contract } from '@ethersproject/contracts' +import { Erc20__factory } from '@/typechain/factories' +import MultiCall from '@/utils/multicall' +import { useBlockUpdater } from '@/hooks/base/useBlock' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' +import Config from '@/bao/lib/config' + +export type Balance = { + address: string + symbol: string + balance: BigNumber + decimals: number +} + +export const useAccountBalances = (marketName: string): Balance[] => { + const bao = useBao() + const { account, library, chainId } = useWeb3React() + + console.log(marketName) + console.log(Config.lendMarkets[marketName].assets) + const enabled = !!bao && !!account && !!chainId + const { data: balances } = useQuery( + ['@/hooks/lend/useAccountBalance', providerKey(library, account, chainId), { enabled }], + async () => { + const tokens = Config.lendMarkets[marketName].assets.map(asset => asset.underlyingAddress[chainId]) + const contracts: Contract[] = tokens.filter(address => address !== 'ETH').map(address => Erc20__factory.connect(address, library)) + + const res = MultiCall.parseCallResults( + await bao.multicall.call( + MultiCall.createCallContext( + contracts.map( + contract => + contract && { + ref: contract.address, + contract, + calls: [{ method: 'symbol' }, { method: 'decimals' }, { method: 'balanceOf', params: [account] }], + }, + ), + ), + ), + ) + const ethBalance = await library.getBalance(account) + console.log(res) + return tokens.map(address => { + const symbol = res[address] ? res[address][0].values[0] : 'ETH' + const decimals = res[address] ? res[address][1].values[0] : 18 + const balance = res[address] ? res[address][2].values[0] : ethBalance + return { address, symbol, balance, decimals } + }) + }, + { + enabled, + }, + ) + + return balances +} diff --git a/src/hooks/lend/useSupplyBalance.ts b/src/hooks/lend/useSupplyBalance.ts new file mode 100644 index 00000000..fad3acfe --- /dev/null +++ b/src/hooks/lend/useSupplyBalance.ts @@ -0,0 +1,57 @@ +import useBao from '@/hooks/base/useBao' +import { useWeb3React } from '@web3-react/core' +import { useQuery } from '@tanstack/react-query' +import { providerKey } from '@/utils/index' +import { Contract } from '@ethersproject/contracts' +import { Ctoken__factory } from '@/typechain/factories' +import MultiCall from '@/utils/multicall' +import { useBlockUpdater } from '@/hooks/base/useBlock' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' +import { Asset, Balance } from '@/bao/lib/types' +import Config from '@/bao/lib/config' + +export const useSupplyBalance = (): Balance[] => { + const bao = useBao() + const { account, library, chainId } = useWeb3React() + + const enabled = !!bao && !!account && !!chainId + const { data: balances, refetch } = useQuery( + ['@/hooks/lend/useSupplyBalances', providerKey(library, account, chainId), { enabled }], + async () => { + const assets = Config.assets.filter(asset => asset.supply == true) + const contracts: Contract[] = assets.map(asset => Ctoken__factory.connect(asset.underlyingAddress[chainId], library)) + + const res = MultiCall.parseCallResults( + await bao.multicall.call( + MultiCall.createCallContext( + contracts.map(contract => ({ + ref: contract.address, + contract, + calls: [{ method: 'symbol' }, { method: 'balanceOf', params: [account] }], + })), + ), + ), + ) + + return Object.keys(res).map(address => { + return { + address, + symbol: res[address][0].values[0], + balance: res[address][1].values[0], + decimals: 18, + } + }) + }, + { + enabled, + }, + ) + + const _refetch = () => { + if (enabled) refetch() + } + useBlockUpdater(_refetch, 10) + useTxReceiptUpdater(_refetch) + + return balances +} diff --git a/src/pages/lend/[market].tsx b/src/pages/lend/[market].tsx new file mode 100644 index 00000000..b464a84b --- /dev/null +++ b/src/pages/lend/[market].tsx @@ -0,0 +1,108 @@ +import Typography from '@/components/Typography' +import { faArrowLeft } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { NextPage } from 'next' +import { NextSeo } from 'next-seo' +import Image from 'next/future/image' +import Link from 'next/link' +import React, { useCallback, useState } from 'react' +import Config from '@/bao/lib/config' +import SuppliedCard from '@/pages/lend/components/SuppliedCard' +import BorrowedCard from '@/pages/lend/components/BorrowedCard' +import AssetsCard from '@/pages/lend/components/AssetsCard' +import { useAccountBalances } from '@/hooks/lend/useAccountBalances' + +export async function getStaticPaths() { + let paths: { params: { market: string } }[] = [] + Object.keys(Config.lendMarkets).map(marketName => paths.push({ params: { market: marketName } })) + + return { + paths: paths, + fallback: false, // can also be true or 'blocking' + } +} + +export async function getStaticProps({ params }: { params: any }) { + const { market } = params + + return { + props: { + marketName: market, + }, + } +} + +const Market: NextPage<{ + marketName: string +}> = ({ marketName }) => { + const accountBalances = useAccountBalances(marketName) + const [supplyVal, setSupplyVal] = useState('0') + const [borrowVal, setBorrowVal] = useState('0') + + const handleSupplyVal = useCallback( + (updatedState: any) => { + // update the parent component's state with the new value + setSupplyVal(updatedState) + }, + [supplyVal], + ) + + const handleBorrowVal = useCallback( + (updatedState: any) => { + // update the parent component's state with the new value + setBorrowVal(updatedState) + }, + [borrowVal], + ) + + return ( + <> + + <> + { + <> +
+ +
+ +
+ + {/*Desktop*/} +
+
+ {marketName} + + + {marketName} + + +
+
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ + } + + + ) +} + +export default Market diff --git a/src/pages/lend/components/AssetsCard.tsx b/src/pages/lend/components/AssetsCard.tsx new file mode 100644 index 00000000..683b5712 --- /dev/null +++ b/src/pages/lend/components/AssetsCard.tsx @@ -0,0 +1,22 @@ +import Card from '@/components/Card/Card' +import Typography from '@/components/Typography' +import React from 'react' +import SupplyList from '@/pages/lend/components/SupplyList' +import { Balance } from '@/bao/lib/types' + +export const AssetsCard = ({ accountBalances, marketName }: { accountBalances: Balance[]; marketName: string }) => { + return ( + <> + + Assets + + + + + + + + ) +} + +export default AssetsCard diff --git a/src/pages/lend/components/BorrowList.tsx b/src/pages/lend/components/BorrowList.tsx new file mode 100644 index 00000000..ae4d81ca --- /dev/null +++ b/src/pages/lend/components/BorrowList.tsx @@ -0,0 +1,80 @@ +import { ListHeader } from '@/components/List' +import Loader from '@/components/Loader' +import Typography from '@/components/Typography' +import Image from 'next/future/image' +import React, { useEffect, useState } from 'react' +import Config from '@/bao/lib/config' +import { getDisplayBalance } from '@/utils/numberFormat' +import Modal from '@/components/Modal' +import { useSupplyBalances } from '@/hooks/vaults/useBalances' +import { Asset } from '@/bao/lib/types' + +export const BorrowList: React.FC = () => { + const assets = Config.assets + + return ( + <> +
+ + Borrowed + + +
+
+ {assets && assets.filter(asset => asset.borrow === true).map(asset => )} +
+
+
+ + ) +} + +const BorrowListItem: React.FC = ({ asset }) => { + const balance = { assetBalance: 10, assetDecimals: 10 } + const [formattedBalance, setFormattedBalance] = useState(null) + const [showBorrowAsset, setShowBorrowAsset] = useState(false) + + useEffect(() => { + if (balance) setFormattedBalance(getDisplayBalance(balance.assetBalance, balance.assetDecimals)) + }, [balance]) + + const handleClick = () => { + setShowBorrowAsset(!showBorrowAsset) + } + + return ( + <> + + + handleClick()}> + Borrow + + + ) +} + +export default BorrowList + +type BorrowListItemProps = { + asset: Asset +} diff --git a/src/pages/lend/components/BorrowedCard.tsx b/src/pages/lend/components/BorrowedCard.tsx new file mode 100644 index 00000000..0c7142a7 --- /dev/null +++ b/src/pages/lend/components/BorrowedCard.tsx @@ -0,0 +1,25 @@ +import Card from '@/components/Card/Card' +import Typography from '@/components/Typography' +import { useWeb3React } from '@web3-react/core' +import React from 'react' + +export const BorrowedCard = ({ marketName, onUpdate }: { marketName: string; onUpdate: (updatedState: any) => void }) => { + const { account } = useWeb3React() + + return ( + <> + + Borrowed + + +
+ + No assets borrowed. + +
+
+ + ) +} + +export default BorrowedCard diff --git a/src/pages/lend/components/Buttons/SupplyButton.tsx b/src/pages/lend/components/Buttons/SupplyButton.tsx new file mode 100644 index 00000000..91806ef6 --- /dev/null +++ b/src/pages/lend/components/Buttons/SupplyButton.tsx @@ -0,0 +1,77 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { Asset } from '@/bao/lib/types' +import Button from '@/components/Button' +import { PendingTransaction } from '@/components/Loader/Loader' +import useContract from '@/hooks/base/useContract' +import useTransactionHandler from '@/hooks/base/useTransactionHandler' +import { useApprovals } from '@/hooks/vaults/useApprovals' +import type { Erc20 } from '@/typechain/index' +import { getDisplayBalance } from '@/utils/numberFormat' +import { faExternalLink } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { BigNumber, ethers } from 'ethers' +import { useWeb3React } from '@web3-react/core' + +type SupplyButtonProps = { + asset: Asset + val: BigNumber + isDisabled: boolean + onHide?: () => void + marketName: string +} + +const SupplyButton = ({ asset, val, isDisabled, onHide, marketName }: SupplyButtonProps) => { + const { pendingTx, handleTx, txHash } = useTransactionHandler() + const { approvals } = useApprovals(marketName) + const { chainId } = useWeb3React() + + const erc20 = useContract('Erc20', asset.underlyingAddress[chainId]) + + if (pendingTx) { + return ( + + + + ) + } else { + return approvals && (asset.underlyingAddress[chainId] === 'ETH' || approvals[asset.underlyingAddress[chainId]].gt(0)) ? ( + + ) : ( + + ) + } +} + +export default SupplyButton diff --git a/src/pages/lend/components/MarketButton.tsx b/src/pages/lend/components/MarketButton.tsx new file mode 100644 index 00000000..77c476a1 --- /dev/null +++ b/src/pages/lend/components/MarketButton.tsx @@ -0,0 +1,157 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { ActiveSupportedVault } from '@/bao/lib/types' +import Button from '@/components/Button' +import { PendingTransaction } from '@/components/Loader/Loader' +import useContract from '@/hooks/base/useContract' +import useTransactionHandler from '@/hooks/base/useTransactionHandler' +import { useApprovals } from '@/hooks/vaults/useApprovals' +import type { Erc20 } from '@/typechain/index' +import { getDisplayBalance } from '@/utils/numberFormat' +import { faExternalLink } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { BigNumber, ethers } from 'ethers' + +type MarketButtonProps = { + operation: string + asset: ActiveSupportedVault + val: BigNumber + isDisabled: boolean + onHide?: () => void + marketName: string +} + +const MarketButton = ({ operation, asset, val, isDisabled, onHide, marketName }: MarketButtonProps) => { + const { pendingTx, handleTx, txHash } = useTransactionHandler() + const { approvals } = useApprovals(vaultName) + const { vaultContract } = asset + const erc20 = useContract('Erc20', asset.underlyingAddress) + + if (pendingTx) { + return ( + + + + ) + } else { + switch (operation) { + case 'Supply': + return approvals && (asset.underlyingAddress === 'ETH' || approvals[asset.underlyingAddress].gt(0)) ? ( + + ) : ( + + ) + + case 'Withdraw': + return ( + + ) + + case 'Mint': + return ( + + ) + + case 'Repay': + return approvals && (asset.underlyingAddress === 'ETH' || approvals[asset.underlyingAddress].gt(0)) ? ( + + ) : ( + + ) + } + } +} + +export default MarketButton diff --git a/src/pages/lend/components/MarketList.tsx b/src/pages/lend/components/MarketList.tsx new file mode 100644 index 00000000..41031684 --- /dev/null +++ b/src/pages/lend/components/MarketList.tsx @@ -0,0 +1,59 @@ +import { ListHeader } from '@/components/List' +import { PageLoader } from '@/components/Loader' +import Typography from '@/components/Typography' +import { useWeb3React } from '@web3-react/core' +import Image from 'next/future/image' +import Link from 'next/link' +import Config from '@/bao/lib/config' +import React from 'react' + +export const MarketList: React.FC = () => { + const markets = Config.lendMarkets + + return ( + <> + +
+ {markets == null && } + {Object.keys(markets).map((marketName, index) => ( + + ))} +
+ + ) +} + +export const MarketListItem: React.FC = ({ marketName }: MarketListProps) => { + const { account } = useWeb3React() + const market = Config.lendMarkets[marketName] + + return ( + market && ( + + + + ) + ) +} + +export default MarketList + +type MarketListProps = { + marketName: string +} diff --git a/src/pages/lend/components/Modals/BorrowModal.tsx b/src/pages/lend/components/Modals/BorrowModal.tsx new file mode 100644 index 00000000..06b0444a --- /dev/null +++ b/src/pages/lend/components/Modals/BorrowModal.tsx @@ -0,0 +1,51 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { ActiveSupportedVault, Asset } from '@/bao/lib/types' +import Badge from '@/components/Badge' +import Button from '@/components/Button' +import { PendingTransaction } from '@/components/Loader/Loader' +import Modal from '@/components/Modal' +import Typography from '@/components/Typography' +import useTransactionHandler from '@/hooks/base/useTransactionHandler' +import { decimate, getDisplayBalance } from '@/utils/numberFormat' +import { faExternalLink } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { BigNumber } from 'ethers' +import { parseUnits } from 'ethers/lib/utils' +import Image from 'next/future/image' +import { useCallback } from 'react' +import MarketButton from '../MarketButton' + +export type BorrowModalProps = { + asset: Asset + show: boolean + onHide: () => void +} + +const BorrowModal = ({ asset, show, onHide }: BorrowModalProps) => { + const operation = 'Borrow' + + const hideModal = useCallback(() => { + onHide() + }, [onHide]) + + return ( + + +
+ + Borrow + +
+
+ + + {asset.name} + {asset.name} + + + +
+ ) +} + +export default BorrowModal diff --git a/src/pages/lend/components/Modals/RepayModal.tsx b/src/pages/lend/components/Modals/RepayModal.tsx new file mode 100644 index 00000000..68dffbf2 --- /dev/null +++ b/src/pages/lend/components/Modals/RepayModal.tsx @@ -0,0 +1,160 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { ActiveSupportedVault } from '@/bao/lib/types' +import Button from '@/components/Button' +import Input from '@/components/Input' +import { PendingTransaction } from '@/components/Loader/Loader' +import Modal from '@/components/Modal' +import Typography from '@/components/Typography' +import useContract from '@/hooks/base/useContract' +import useTransactionHandler from '@/hooks/base/useTransactionHandler' +import { useAccountLiquidity } from '@/hooks/vaults/useAccountLiquidity' +import { useApprovals } from '@/hooks/vaults/useApprovals' +import { useAccountBalances, useBorrowBalances, useSupplyBalances } from '@/hooks/vaults/useBalances' +import { useExchangeRates } from '@/hooks/vaults/useExchangeRates' +import { Erc20 } from '@/typechain/Erc20' +import { decimate, exponentiate, getDisplayBalance, sqrt } from '@/utils/numberFormat' +import { faExternalLink } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { BigNumber, ethers } from 'ethers' +import { formatUnits, parseUnits } from 'ethers/lib/utils' +import Image from 'next/future/image' +import React, { useCallback, useMemo, useState } from 'react' +import MarketButton from '../MarketButton' + +export type RepayModalProps = { + asset: ActiveSupportedVault + show: boolean + onHide: () => void + vaultName: string +} + +const RepayModal = ({ asset, show, onHide, vaultName }: RepayModalProps) => { + const [val, setVal] = useState('') + const balances = useAccountBalances(vaultName) + const borrowBalances = useBorrowBalances(vaultName) + const supplyBalances = useSupplyBalances(vaultName) + const accountLiquidity = useAccountLiquidity(vaultName) + const { exchangeRates } = useExchangeRates(vaultName) + const { approvals } = useApprovals(vaultName) + const erc20 = useContract('Erc20', asset.underlyingAddress) + + const { pendingTx, txHash, handleTx } = useTransactionHandler() + const { vaultContract } = asset + + const operation = 'Repay' + + const supply = useMemo( + () => + supplyBalances && + supplyBalances.find(balance => balance.address.toLowerCase() === asset.vaultAddress.toLowerCase()) && + exchangeRates && + exchangeRates[asset.vaultAddress] + ? decimate( + supplyBalances + .find(balance => balance.address.toLowerCase() === asset.vaultAddress.toLowerCase()) + .balance.mul(exchangeRates[asset.vaultAddress]), + ) + : BigNumber.from(0), + [supplyBalances, exchangeRates, asset.vaultAddress], + ) + + let _imfFactor = asset.imfFactor + if (accountLiquidity) { + const _sqrt = sqrt(supply) + const num = exponentiate(parseUnits('1.1')) + const denom = decimate(asset.imfFactor.mul(_sqrt).add(parseUnits('1'))) + _imfFactor = num.div(denom) + } + + let withdrawable = BigNumber.from(0) + if (_imfFactor.gt(asset.collateralFactor) && asset.price.gt(0)) { + if (asset.collateralFactor.mul(asset.price).gt(0)) { + withdrawable = accountLiquidity && exponentiate(accountLiquidity.usdBorrowable).div(decimate(asset.collateralFactor.mul(asset.price))) + } else { + withdrawable = accountLiquidity && exponentiate(accountLiquidity.usdBorrowable).div(decimate(_imfFactor).mul(asset.price)) + } + } + + const max = () => { + if (borrowBalances && balances) { + const borrowBalance = borrowBalances.find(_balance => _balance.address.toLowerCase() === asset.vaultAddress.toLowerCase()).balance + const walletBalance = balances.find(_balance => _balance.address.toLowerCase() === asset.underlyingAddress.toLowerCase()).balance + return walletBalance.lt(borrowBalance) ? walletBalance : borrowBalance + } else { + return BigNumber.from(0) + } + } + + const handleChange = useCallback( + (e: React.FormEvent) => { + if (e.currentTarget.value.length < 20) setVal(e.currentTarget.value) + }, + [setVal], + ) + + const hideModal = useCallback(() => { + onHide() + setVal('') + }, [onHide]) + + return ( + <> + + +
+ + Repay + + {asset.underlyingSymbol} +
+
+ <> + +
+
+
+ + Availalble: + + {`${getDisplayBalance(max(), asset.underlyingDecimals)} ${ + asset.underlyingSymbol + }`} +
+
+ setVal(formatUnits(max(), asset.underlyingDecimals))} + label={ +
+
+ {asset.symbol} +
+
+ } + /> +
+
+ + + + +
+ + ) +} + +export default RepayModal diff --git a/src/pages/lend/components/Modals/SupplyModal.tsx b/src/pages/lend/components/Modals/SupplyModal.tsx new file mode 100644 index 00000000..4e1f4366 --- /dev/null +++ b/src/pages/lend/components/Modals/SupplyModal.tsx @@ -0,0 +1,68 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { ActiveSupportedVault, Asset } from '@/bao/lib/types' +import Modal from '@/components/Modal' +import { BigNumber } from 'ethers' +import { parseUnits } from 'ethers/lib/utils' +import { useCallback, useState } from 'react' +import MarketButton from '../MarketButton' +import Input from '@/components/Input' +import Typography from '@/components/Typography' +import VaultButton from '@/pages/vaults/components/VaultButton' +import SupplyButton from '@/pages/lend/components/Buttons/SupplyButton' + +export type SupplyModalProps = { + asset: Asset + show: boolean + onHide: () => void + marketName: string +} + +const SupplyModal = ({ asset, show, onHide, marketName }: SupplyModalProps) => { + const [val, setVal] = useState('0') + const operation = 'Supply' + + const handleChange = useCallback( + (e: React.FormEvent) => { + if (e.currentTarget.value.length < 20) setVal(e.currentTarget.value) + }, + [setVal], + ) + + const hideModal = useCallback(() => { + onHide() + }, [onHide]) + + return ( + {}}> + +
+ + Supply {asset.name} + +
+
+ + + {}} + placeholder={`0`} + className='h-12 min-w-[150px] z-20 w-full bg-baoBlack lg:h-auto' + /> + + + + + +
+ ) +} + +export default SupplyModal diff --git a/src/pages/lend/components/Modals/WithdrawModal.tsx b/src/pages/lend/components/Modals/WithdrawModal.tsx new file mode 100644 index 00000000..f9759ec4 --- /dev/null +++ b/src/pages/lend/components/Modals/WithdrawModal.tsx @@ -0,0 +1,146 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { ActiveSupportedVault } from '@/bao/lib/types' +import Button from '@/components/Button' +import Input from '@/components/Input' +import { PendingTransaction } from '@/components/Loader/Loader' +import Modal from '@/components/Modal' +import Typography from '@/components/Typography' +import useTransactionHandler from '@/hooks/base/useTransactionHandler' +import { useAccountLiquidity } from '@/hooks/vaults/useAccountLiquidity' +import { useSupplyBalances } from '@/hooks/vaults/useBalances' +import { useExchangeRates } from '@/hooks/vaults/useExchangeRates' +import { decimate, exponentiate, getDisplayBalance, sqrt } from '@/utils/numberFormat' +import { faExternalLink } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { BigNumber } from 'ethers' +import { formatUnits, parseUnits } from 'ethers/lib/utils' +import Image from 'next/future/image' +import React, { useCallback, useMemo, useState } from 'react' +import MarketButton from '../MarketButton' + +export type WithdrawModalProps = { + asset: ActiveSupportedVault + show: boolean + onHide: () => void + vaultName: string +} + +const WithdrawModal = ({ asset, show, onHide, vaultName }: WithdrawModalProps) => { + const [val, setVal] = useState('') + const supplyBalances = useSupplyBalances(vaultName) + const accountLiquidity = useAccountLiquidity(vaultName) + const { exchangeRates } = useExchangeRates(vaultName) + const { pendingTx, txHash, handleTx } = useTransactionHandler() + const { vaultContract } = asset + + const operation = 'Withdraw' + + const supply = useMemo( + () => + supplyBalances && + supplyBalances.find(balance => balance.address.toLowerCase() === asset.vaultAddress.toLowerCase()) && + exchangeRates && + exchangeRates[asset.vaultAddress] + ? decimate( + supplyBalances + .find(balance => balance.address.toLowerCase() === asset.vaultAddress.toLowerCase()) + .balance.mul(exchangeRates[asset.vaultAddress]), + ) + : BigNumber.from(0), + [supplyBalances, exchangeRates, asset.vaultAddress], + ) + + let _imfFactor = asset.imfFactor + if (accountLiquidity) { + const _sqrt = sqrt(supply) + const num = exponentiate(parseUnits('1.1')) + const denom = decimate(asset.imfFactor.mul(_sqrt).add(parseUnits('1'))) + _imfFactor = num.div(denom) + } + + let withdrawable = BigNumber.from(0) + if (_imfFactor.gt(asset.collateralFactor) && asset.price.gt(0)) { + if (asset.collateralFactor.mul(asset.price).gt(0)) { + withdrawable = accountLiquidity && exponentiate(accountLiquidity.usdBorrowable).div(decimate(asset.collateralFactor.mul(asset.price))) + } else { + withdrawable = accountLiquidity && exponentiate(accountLiquidity.usdBorrowable).div(decimate(_imfFactor).mul(asset.price)) + } + } + + const max = () => { + return !(accountLiquidity && accountLiquidity.usdBorrowable) || withdrawable.gt(supply) ? supply : withdrawable + } + + const handleChange = useCallback( + (e: React.FormEvent) => { + if (e.currentTarget.value.length < 20) setVal(e.currentTarget.value) + }, + [setVal], + ) + + const hideModal = useCallback(() => { + onHide() + setVal('') + }, [onHide]) + + return ( + <> + + +
+ + Withdraw + + {asset.underlyingSymbol} +
+
+ <> + +
+
+
+ + Withdrawable: + + {`${getDisplayBalance(max(), asset.underlyingDecimals)} ${ + asset.underlyingSymbol + }`} +
+
+ setVal(formatUnits(max(), asset.underlyingDecimals))} + label={ +
+
+ {asset.symbol} +
+
+ } + /> +
+
+ + + + +
+ + ) +} + +export default WithdrawModal diff --git a/src/pages/lend/components/SuppliedCard.tsx b/src/pages/lend/components/SuppliedCard.tsx new file mode 100644 index 00000000..c823465b --- /dev/null +++ b/src/pages/lend/components/SuppliedCard.tsx @@ -0,0 +1,25 @@ +import Card from '@/components/Card/Card' +import Typography from '@/components/Typography' +import { useWeb3React } from '@web3-react/core' +import React from 'react' + +export const SuppliedCard = ({ marketName, onUpdate }: { marketName: string; onUpdate: (updatedState: any) => void }) => { + const { account } = useWeb3React() + + return ( + <> + + Supplied + + +
+ + No assets supplied. + +
+
+ + ) +} + +export default SuppliedCard diff --git a/src/pages/lend/components/SupplyList.tsx b/src/pages/lend/components/SupplyList.tsx new file mode 100644 index 00000000..6d1a9c98 --- /dev/null +++ b/src/pages/lend/components/SupplyList.tsx @@ -0,0 +1,98 @@ +import Loader from '@/components/Loader' +import Typography from '@/components/Typography' +import Image from 'next/future/image' +import React, { useEffect, useState } from 'react' +import Config from '@/bao/lib/config' +import { getDisplayBalance } from '@/utils/numberFormat' +import { Asset, Balance } from '@/bao/lib/types' +import { useWeb3React } from '@web3-react/core' +import Button from '@/components/Button' +import SupplyModal from '@/pages/lend/components/Modals/SupplyModal' +import BorrowModal from '@/pages/lend/components/Modals/BorrowModal' + +export const SupplyList: React.FC = ({ accountBalances, marketName }) => { + const assets = Config.lendMarkets[marketName].assets + + return ( + <> +
+
+ {assets && + assets.map(asset => )} +
+
+ + ) +} + +const SupplyListItem: React.FC = ({ asset, accountBalances, marketName }) => { + const { chainId } = useWeb3React() + const [formattedBalance, setFormattedBalance] = useState(null) + const [showSupplyModal, setShowSupplyModal] = useState(false) + const [showBorrowModal, setShowBorrowModal] = useState(false) + + function fetchBalance(asset: Asset) { + if (accountBalances !== null && accountBalances !== undefined) + return accountBalances.find(({ address }) => address === asset.underlyingAddress[chainId]) + } + + useEffect(() => { + const balance = fetchBalance(asset) + if (balance !== null && balance !== undefined) setFormattedBalance(getDisplayBalance(balance.balance, balance.decimals)) + }, [accountBalances]) + + return ( + <> +
+
+
+
+ {asset.name} + + + {asset.name} + + +
+
+
+ + + + + + +
+
+ + + + +
Account balance
{formattedBalance ? formattedBalance : }
+
+ {asset.supply === true && } + {asset.borrow === true && } +
+ + setShowSupplyModal(!showSupplyModal)} marketName={marketName} /> + setShowBorrowModal(!showBorrowModal)} /> +
+ + ) +} + +export default SupplyList + +type SupplyListItemProps = { + asset: Asset + accountBalances: Balance[] + marketName: string +} + +type SupplyListProps = { + accountBalances: Balance[] + marketName: string +} diff --git a/src/pages/lend/index.tsx b/src/pages/lend/index.tsx new file mode 100644 index 00000000..b73be834 --- /dev/null +++ b/src/pages/lend/index.tsx @@ -0,0 +1,32 @@ +import Button from '@/components/Button' +import { Icon } from '@/components/Icon' +import Typography from '@/components/Typography' +import { NextSeo } from 'next-seo' +import React from 'react' +import MarketList from '@/pages/lend/components/MarketList' + +const Markets: React.FC = () => { + return ( + <> + +
+
+ + Lend + +
+ + + Supply and borrow assets in selected markets. + +
+
+
+ +
+
+ + ) +} + +export default Markets From f6653472d62384247a242ffeaa8d8592a3ca944a Mon Sep 17 00:00:00 2001 From: Daizze Date: Wed, 24 Jul 2024 00:26:27 +0200 Subject: [PATCH 02/27] Adjustments supply --- src/bao/lib/config.ts | 12 +++ src/bao/lib/types.ts | 13 ++++ src/hooks/lend/useAccountBalances.ts | 5 +- src/hooks/lend/useActiveLendMarket.ts | 40 ++++++++++ src/hooks/lend/useLendMarketApprovals.ts | 76 +++++++++++++++++++ .../lend/components/Buttons/SupplyButton.tsx | 22 +++--- .../lend/components/Modals/SupplyModal.tsx | 2 - 7 files changed, 151 insertions(+), 19 deletions(-) create mode 100644 src/hooks/lend/useActiveLendMarket.ts create mode 100644 src/hooks/lend/useLendMarketApprovals.ts diff --git a/src/bao/lib/config.ts b/src/bao/lib/config.ts index 81a613a4..c367c68f 100644 --- a/src/bao/lib/config.ts +++ b/src/bao/lib/config.ts @@ -812,6 +812,12 @@ export default { active: true, comptroller: '0xc2eA0F9856B58CF182Cc72B8FaEc51351479232e', oracle: '0xc7D8b6b170E0FCf4182fa29b47F35F48C402bF0F', + marketAddresses: { + 1: '0x353A07b25c84a522356aF2D9a7c0d7FF481733e9', + }, + underlyingAddresses: { + 1: '0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee', + }, assets: [ { id: 1, @@ -856,6 +862,12 @@ export default { active: true, comptroller: '0xc2eA0F9856B58CF182Cc72B8FaEc51351479232e', oracle: '0xc7D8b6b170E0FCf4182fa29b47F35F48C402bF0F', + marketAddresses: { + 1: '', + }, + underlyingAddresses: { + 1: '', + }, assets: [ { id: 1, diff --git a/src/bao/lib/types.ts b/src/bao/lib/types.ts index 1aae07fe..cb03834d 100644 --- a/src/bao/lib/types.ts +++ b/src/bao/lib/types.ts @@ -235,9 +235,22 @@ export interface LendMarket { active: boolean comptroller: string oracle: string + marketAddresses: { + [network: number]: string + } + underlyingAddresses: { + [network: number]: string + } assets: Asset[] } +export interface ActiveLendMarket { + marketAddress: string + marketContract: Cether | Ctoken + underlyingAddress: string + underlyingContract?: Erc20 +} + export interface Asset { id: number active: boolean diff --git a/src/hooks/lend/useAccountBalances.ts b/src/hooks/lend/useAccountBalances.ts index 228abc8c..1054164e 100644 --- a/src/hooks/lend/useAccountBalances.ts +++ b/src/hooks/lend/useAccountBalances.ts @@ -20,9 +20,6 @@ export type Balance = { export const useAccountBalances = (marketName: string): Balance[] => { const bao = useBao() const { account, library, chainId } = useWeb3React() - - console.log(marketName) - console.log(Config.lendMarkets[marketName].assets) const enabled = !!bao && !!account && !!chainId const { data: balances } = useQuery( ['@/hooks/lend/useAccountBalance', providerKey(library, account, chainId), { enabled }], @@ -45,7 +42,7 @@ export const useAccountBalances = (marketName: string): Balance[] => { ), ) const ethBalance = await library.getBalance(account) - console.log(res) + return tokens.map(address => { const symbol = res[address] ? res[address][0].values[0] : 'ETH' const decimals = res[address] ? res[address][1].values[0] : 18 diff --git a/src/hooks/lend/useActiveLendMarket.ts b/src/hooks/lend/useActiveLendMarket.ts new file mode 100644 index 00000000..8c165916 --- /dev/null +++ b/src/hooks/lend/useActiveLendMarket.ts @@ -0,0 +1,40 @@ +import Config from '@/bao/lib/config' +import useTransactionProvider from '@/hooks/base/useTransactionProvider' +import { Ctoken__factory, Erc20__factory } from '@/typechain/factories' +import { useWeb3React } from '@web3-react/core' +import { ActiveLendMarket } from '@/bao/lib/types' +import { useCallback, useEffect, useState } from 'react' + +export const useActiveLendMarket = (marketName: string): ActiveLendMarket => { + const { library, account, chainId } = useWeb3React() + const { transactions } = useTransactionProvider() + const [lendMarket, setLendMarket] = useState(null) + + const fetchLendMarket = useCallback( + async (marketName: string) => { + const signerOrProvider = account ? library.getSigner() : library + const lendMarket = Config.lendMarkets[marketName] + const marketAddress = lendMarket.marketAddresses[chainId] + const underlyingAddress = lendMarket.underlyingAddresses[chainId] + const marketContract = Ctoken__factory.connect(marketAddress, signerOrProvider) + const underlyingContract = Erc20__factory.connect(underlyingAddress, signerOrProvider) + + const newActiveLendMarket: ActiveLendMarket = { + marketAddress: marketAddress, + marketContract: marketContract, + underlyingAddress: underlyingAddress, + underlyingContract: underlyingContract, + } + + setLendMarket(newActiveLendMarket) + }, + [chainId, library, account], + ) + + useEffect(() => { + if (!library || !chainId) return + fetchLendMarket(marketName) + }, [fetchLendMarket, library, account, chainId, transactions]) + + return lendMarket +} diff --git a/src/hooks/lend/useLendMarketApprovals.ts b/src/hooks/lend/useLendMarketApprovals.ts new file mode 100644 index 00000000..3f05f4d7 --- /dev/null +++ b/src/hooks/lend/useLendMarketApprovals.ts @@ -0,0 +1,76 @@ +import { BigNumber } from 'ethers' +import MultiCall from '@/utils/multicall' +import useBao from '@/hooks/base/useBao' +import { useWeb3React } from '@web3-react/core' +import { providerKey } from '@/utils/index' +import { useQuery } from '@tanstack/react-query' +import { useBlockUpdater } from '@/hooks/base/useBlock' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' +import { useEffect } from 'react' +import { ActiveLendMarket, LendMarket } from '@/bao/lib/types' +import { Contract } from '@ethersproject/contracts' +import { Erc20__factory } from '@/typechain/factories' + +type LendMarketApprovals = { + approvals: { [key: string]: BigNumber } +} + +export const useLendMarketApprovals = (lendMarket: ActiveLendMarket): LendMarketApprovals => { + const bao = useBao() + const { library, account, chainId } = useWeb3React() + + const enabled = !!bao && !!lendMarket && !!account + const { data: approvals, refetch } = useQuery( + ['@/hooks/lend/useLendMarketApprovals', providerKey(library, account, chainId), { enabled }], + async () => { + const contracts: Contract[] = [lendMarket.underlyingContract] + + const res = MultiCall.parseCallResults( + await bao.multicall.call( + MultiCall.createCallContext( + contracts.map( + contract => + contract && { + ref: lendMarket.underlyingAddress, + contract: lendMarket.underlyingContract, + calls: [ + { + method: 'allowance', + ref: lendMarket.marketAddress, + params: [account, lendMarket.marketAddress], + }, + ], + }, + ), + ), + ), + ) + + return Object.keys(res).reduce( + (approvals: { [key: string]: BigNumber }, address: string) => ({ + ...approvals, + [res[address][0].ref]: BigNumber.from(res[address][0].values[0]), + }), + {}, + ) + }, + { + enabled, + }, + ) + + const _refetch = () => { + if (enabled) refetch() + } + + useEffect(() => { + _refetch() + }, [lendMarket]) + + useBlockUpdater(_refetch, 10) + useTxReceiptUpdater(_refetch) + + return { + approvals, + } +} diff --git a/src/pages/lend/components/Buttons/SupplyButton.tsx b/src/pages/lend/components/Buttons/SupplyButton.tsx index 91806ef6..4582eb84 100644 --- a/src/pages/lend/components/Buttons/SupplyButton.tsx +++ b/src/pages/lend/components/Buttons/SupplyButton.tsx @@ -11,6 +11,8 @@ import { faExternalLink } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { BigNumber, ethers } from 'ethers' import { useWeb3React } from '@web3-react/core' +import { useActiveLendMarket } from '@/hooks/lend/useActiveLendMarket' +import { useLendMarketApprovals } from '@/hooks/lend/useLendMarketApprovals' type SupplyButtonProps = { asset: Asset @@ -21,8 +23,10 @@ type SupplyButtonProps = { } const SupplyButton = ({ asset, val, isDisabled, onHide, marketName }: SupplyButtonProps) => { + const activeLendMarket = useActiveLendMarket(marketName) + const { pendingTx, handleTx, txHash } = useTransactionHandler() - const { approvals } = useApprovals(marketName) + const { approvals } = useLendMarketApprovals(activeLendMarket) const { chainId } = useWeb3React() const erc20 = useContract('Erc20', asset.underlyingAddress[chainId]) @@ -37,22 +41,14 @@ const SupplyButton = ({ asset, val, isDisabled, onHide, marketName }: SupplyButt ) } else { - return approvals && (asset.underlyingAddress[chainId] === 'ETH' || approvals[asset.underlyingAddress[chainId]].gt(0)) ? ( + return typeof approvals != 'undefined' && + (asset.underlyingAddress[chainId] === 'ETH' || approvals[asset.underlyingAddress[chainId]].gt(0)) ? ( } + {asset.borrow === true && } - setShowSupplyModal(!showSupplyModal)} marketName={marketName} /> + setShowSupplyModal(!showSupplyModal)} + marketName={marketName} + fullBalance={balance} + /> setShowBorrowModal(!showBorrowModal)} /> From bfcda7842b3cc949ee668482464a433e91308201 Mon Sep 17 00:00:00 2001 From: Daizze Date: Fri, 6 Sep 2024 00:03:38 +0200 Subject: [PATCH 04/27] Adding Modals and Debt Card for Lend --- src/pages/lend/[market].tsx | 56 +++++-- src/pages/lend/components/DebtCard.tsx | 132 +++++++++++++++ src/pages/lend/components/MarketButton.tsx | 157 ------------------ .../lend/components/Modals/BorrowModal.tsx | 12 +- .../lend/components/Modals/RepayModal.tsx | 128 ++------------ .../lend/components/Modals/WithdrawModal.tsx | 112 ++----------- src/pages/lend/components/SuppliedCard.tsx | 2 +- src/pages/lend/components/SupplyList.tsx | 39 ++++- 8 files changed, 235 insertions(+), 403 deletions(-) create mode 100644 src/pages/lend/components/DebtCard.tsx delete mode 100644 src/pages/lend/components/MarketButton.tsx diff --git a/src/pages/lend/[market].tsx b/src/pages/lend/[market].tsx index b464a84b..fbb8390e 100644 --- a/src/pages/lend/[market].tsx +++ b/src/pages/lend/[market].tsx @@ -7,13 +7,12 @@ import Image from 'next/future/image' import Link from 'next/link' import React, { useCallback, useState } from 'react' import Config from '@/bao/lib/config' -import SuppliedCard from '@/pages/lend/components/SuppliedCard' -import BorrowedCard from '@/pages/lend/components/BorrowedCard' import AssetsCard from '@/pages/lend/components/AssetsCard' import { useAccountBalances } from '@/hooks/lend/useAccountBalances' +import DebtCard from '@/pages/lend/components/DebtCard' export async function getStaticPaths() { - let paths: { params: { market: string } }[] = [] + const paths: { params: { market: string } }[] = [] Object.keys(Config.lendMarkets).map(marketName => paths.push({ params: { market: marketName } })) return { @@ -83,16 +82,53 @@ const Market: NextPage<{ + +
+ +
+
+
+ + Oracle Price + + + $123.00 {/*getDisplayBalance(synth.price)*/} + +
+
+ + Total Collateral + + + $1000.00 {/*getDisplayBalance(decimate(totalCollateral), synth.underlyingDecimals)*/} + +
+
+ + Utilization + + + {/*getDisplayBalance(totalDebt.div(decimate(totalCollateral)).mul(100))*/} 12.3% + +
+
+ + Borrow Rate + + + {/*getDisplayBalance(synth.borrowApy, 18, 2)*/}50% + + vAPY +
+
+
-
-
- -
-
- -
+ +
+
+
diff --git a/src/pages/lend/components/DebtCard.tsx b/src/pages/lend/components/DebtCard.tsx new file mode 100644 index 00000000..ee593827 --- /dev/null +++ b/src/pages/lend/components/DebtCard.tsx @@ -0,0 +1,132 @@ +import { ActiveLendMarket, ActiveSupportedVault } from '@/bao/lib/types' +import Typography from '@/components/Typography' +import useBao from '@/hooks/base/useBao' +import { useAccountLiquidity } from '@/hooks/vaults/useAccountLiquidity' +import { useBorrowBalances } from '@/hooks/vaults/useBalances' +import useHealthFactor from '@/hooks/vaults/useHealthFactor' +import { decimate, exponentiate, getDisplayBalance } from '@/utils/numberFormat' +import { useWeb3React } from '@web3-react/core' +import { BigNumber } from 'ethers' +import { formatUnits, parseUnits } from 'ethers/lib/utils' +import Image from 'next/future/image' +import React, { useMemo } from 'react' +import { faDashboard } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { useActiveLendMarket } from '@/hooks/lend/useActiveLendMarket' + +type DashboardCardProps = { + marketName: string + mintVal: string + depositVal: string +} + +const DashboardCard: React.FC = ({ marketName, mintVal, depositVal }: DashboardCardProps) => { + const bao = useBao() + const { account } = useWeb3React() + const borrowBalances = useBorrowBalances(marketName) + const asset = useActiveLendMarket(marketName) + + const borrowed = useMemo( + () => asset && borrowBalances && borrowBalances.find(balance => balance.address === asset.marketAddress).balance, + [borrowBalances, asset], + ) + + const change = mintVal && depositVal ? BigNumber.from(mintVal).sub(BigNumber.from(depositVal)) : BigNumber.from(0) + const borrow = BigNumber.from(0) + const newBorrow = borrow ? borrow.sub(change.gt(0) ? change : 0) : BigNumber.from(0) + const borrowable = BigNumber.from(0) + const newBorrowable = asset && decimate(borrowable).sub(BigNumber.from(parseUnits(formatUnits(change, 36 - 18)))) + + const borrowChange = borrow.add(exponentiate(change)) + const healthFactor = BigNumber.from('10') + + const healthFactorColor = (healthFactor: BigNumber) => { + const c = healthFactor.eq(0) + ? `${(props: any) => props.theme.color.text[100]}` + : healthFactor.lte(parseUnits('1.25')) + ? '#e32222' + : healthFactor.lt(parseUnits('1.55')) + ? '#ffdf19' + : '#45be31' + return c + } + + return ( + <> +
+
+
+
+ + Debt Health:{' '} + {healthFactor && + (healthFactor.lte(BigNumber.from(0)) ? '-' : healthFactor.gt(parseUnits('10000')) ? '∞' : getDisplayBalance(healthFactor))} + +
+
+
+ +
+
+ +
+ {/*Desktop*/} +
+
+ +
+
+
+ + Your Collateral + + + ${`${bao && account ? getDisplayBalance(decimate(BigNumber.from(BigNumber.from(0).toString())), 18, 2) : 0}`} + +
+
+ + Your Debt + + + $ 0 + +
+
+ + Debt Limit Used + + + {newBorrowable && getDisplayBalance(!newBorrowable.eq(0) ? newBorrow.div(newBorrowable).mul(100) : 0, 18, 2)}% + +
+
+ + Debt Limit Remaining + + + $ 0 + +
+
+
+
+
+ + ) +} + +export default DashboardCard diff --git a/src/pages/lend/components/MarketButton.tsx b/src/pages/lend/components/MarketButton.tsx deleted file mode 100644 index 77c476a1..00000000 --- a/src/pages/lend/components/MarketButton.tsx +++ /dev/null @@ -1,157 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -import { ActiveSupportedVault } from '@/bao/lib/types' -import Button from '@/components/Button' -import { PendingTransaction } from '@/components/Loader/Loader' -import useContract from '@/hooks/base/useContract' -import useTransactionHandler from '@/hooks/base/useTransactionHandler' -import { useApprovals } from '@/hooks/vaults/useApprovals' -import type { Erc20 } from '@/typechain/index' -import { getDisplayBalance } from '@/utils/numberFormat' -import { faExternalLink } from '@fortawesome/free-solid-svg-icons' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { BigNumber, ethers } from 'ethers' - -type MarketButtonProps = { - operation: string - asset: ActiveSupportedVault - val: BigNumber - isDisabled: boolean - onHide?: () => void - marketName: string -} - -const MarketButton = ({ operation, asset, val, isDisabled, onHide, marketName }: MarketButtonProps) => { - const { pendingTx, handleTx, txHash } = useTransactionHandler() - const { approvals } = useApprovals(vaultName) - const { vaultContract } = asset - const erc20 = useContract('Erc20', asset.underlyingAddress) - - if (pendingTx) { - return ( - - - - ) - } else { - switch (operation) { - case 'Supply': - return approvals && (asset.underlyingAddress === 'ETH' || approvals[asset.underlyingAddress].gt(0)) ? ( - - ) : ( - - ) - - case 'Withdraw': - return ( - - ) - - case 'Mint': - return ( - - ) - - case 'Repay': - return approvals && (asset.underlyingAddress === 'ETH' || approvals[asset.underlyingAddress].gt(0)) ? ( - - ) : ( - - ) - } - } -} - -export default MarketButton diff --git a/src/pages/lend/components/Modals/BorrowModal.tsx b/src/pages/lend/components/Modals/BorrowModal.tsx index 06b0444a..b96f5d51 100644 --- a/src/pages/lend/components/Modals/BorrowModal.tsx +++ b/src/pages/lend/components/Modals/BorrowModal.tsx @@ -1,19 +1,9 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import { ActiveSupportedVault, Asset } from '@/bao/lib/types' -import Badge from '@/components/Badge' -import Button from '@/components/Button' -import { PendingTransaction } from '@/components/Loader/Loader' +import { Asset } from '@/bao/lib/types' import Modal from '@/components/Modal' import Typography from '@/components/Typography' -import useTransactionHandler from '@/hooks/base/useTransactionHandler' -import { decimate, getDisplayBalance } from '@/utils/numberFormat' -import { faExternalLink } from '@fortawesome/free-solid-svg-icons' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { BigNumber } from 'ethers' -import { parseUnits } from 'ethers/lib/utils' import Image from 'next/future/image' import { useCallback } from 'react' -import MarketButton from '../MarketButton' export type BorrowModalProps = { asset: Asset diff --git a/src/pages/lend/components/Modals/RepayModal.tsx b/src/pages/lend/components/Modals/RepayModal.tsx index 68dffbf2..4c28814d 100644 --- a/src/pages/lend/components/Modals/RepayModal.tsx +++ b/src/pages/lend/components/Modals/RepayModal.tsx @@ -1,100 +1,22 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import { ActiveSupportedVault } from '@/bao/lib/types' -import Button from '@/components/Button' -import Input from '@/components/Input' -import { PendingTransaction } from '@/components/Loader/Loader' +import { Asset } from '@/bao/lib/types' import Modal from '@/components/Modal' import Typography from '@/components/Typography' -import useContract from '@/hooks/base/useContract' -import useTransactionHandler from '@/hooks/base/useTransactionHandler' -import { useAccountLiquidity } from '@/hooks/vaults/useAccountLiquidity' -import { useApprovals } from '@/hooks/vaults/useApprovals' -import { useAccountBalances, useBorrowBalances, useSupplyBalances } from '@/hooks/vaults/useBalances' -import { useExchangeRates } from '@/hooks/vaults/useExchangeRates' -import { Erc20 } from '@/typechain/Erc20' -import { decimate, exponentiate, getDisplayBalance, sqrt } from '@/utils/numberFormat' -import { faExternalLink } from '@fortawesome/free-solid-svg-icons' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { BigNumber, ethers } from 'ethers' -import { formatUnits, parseUnits } from 'ethers/lib/utils' import Image from 'next/future/image' -import React, { useCallback, useMemo, useState } from 'react' -import MarketButton from '../MarketButton' +import React, { useCallback } from 'react' export type RepayModalProps = { - asset: ActiveSupportedVault + asset: Asset show: boolean onHide: () => void - vaultName: string + marketName: string } -const RepayModal = ({ asset, show, onHide, vaultName }: RepayModalProps) => { - const [val, setVal] = useState('') - const balances = useAccountBalances(vaultName) - const borrowBalances = useBorrowBalances(vaultName) - const supplyBalances = useSupplyBalances(vaultName) - const accountLiquidity = useAccountLiquidity(vaultName) - const { exchangeRates } = useExchangeRates(vaultName) - const { approvals } = useApprovals(vaultName) - const erc20 = useContract('Erc20', asset.underlyingAddress) - - const { pendingTx, txHash, handleTx } = useTransactionHandler() - const { vaultContract } = asset - +const RepayModal = ({ asset, show, onHide, marketName }: RepayModalProps) => { const operation = 'Repay' - const supply = useMemo( - () => - supplyBalances && - supplyBalances.find(balance => balance.address.toLowerCase() === asset.vaultAddress.toLowerCase()) && - exchangeRates && - exchangeRates[asset.vaultAddress] - ? decimate( - supplyBalances - .find(balance => balance.address.toLowerCase() === asset.vaultAddress.toLowerCase()) - .balance.mul(exchangeRates[asset.vaultAddress]), - ) - : BigNumber.from(0), - [supplyBalances, exchangeRates, asset.vaultAddress], - ) - - let _imfFactor = asset.imfFactor - if (accountLiquidity) { - const _sqrt = sqrt(supply) - const num = exponentiate(parseUnits('1.1')) - const denom = decimate(asset.imfFactor.mul(_sqrt).add(parseUnits('1'))) - _imfFactor = num.div(denom) - } - - let withdrawable = BigNumber.from(0) - if (_imfFactor.gt(asset.collateralFactor) && asset.price.gt(0)) { - if (asset.collateralFactor.mul(asset.price).gt(0)) { - withdrawable = accountLiquidity && exponentiate(accountLiquidity.usdBorrowable).div(decimate(asset.collateralFactor.mul(asset.price))) - } else { - withdrawable = accountLiquidity && exponentiate(accountLiquidity.usdBorrowable).div(decimate(_imfFactor).mul(asset.price)) - } - } - - const max = () => { - if (borrowBalances && balances) { - const borrowBalance = borrowBalances.find(_balance => _balance.address.toLowerCase() === asset.vaultAddress.toLowerCase()).balance - const walletBalance = balances.find(_balance => _balance.address.toLowerCase() === asset.underlyingAddress.toLowerCase()).balance - return walletBalance.lt(borrowBalance) ? walletBalance : borrowBalance - } else { - return BigNumber.from(0) - } - } - - const handleChange = useCallback( - (e: React.FormEvent) => { - if (e.currentTarget.value.length < 20) setVal(e.currentTarget.value) - }, - [setVal], - ) - const hideModal = useCallback(() => { onHide() - setVal('') }, [onHide]) return ( @@ -105,7 +27,7 @@ const RepayModal = ({ asset, show, onHide, vaultName }: RepayModalProps) => { Repay - {asset.underlyingSymbol} + {asset.name}
<> @@ -114,43 +36,17 @@ const RepayModal = ({ asset, show, onHide, vaultName }: RepayModalProps) => {
- Availalble: + Available: + + + {asset.name} + {asset.name} - {`${getDisplayBalance(max(), asset.underlyingDecimals)} ${ - asset.underlyingSymbol - }`}
- setVal(formatUnits(max(), asset.underlyingDecimals))} - label={ -
-
- {asset.symbol} -
-
- } - />
- - - + diff --git a/src/pages/lend/components/Modals/WithdrawModal.tsx b/src/pages/lend/components/Modals/WithdrawModal.tsx index f9759ec4..3e173281 100644 --- a/src/pages/lend/components/Modals/WithdrawModal.tsx +++ b/src/pages/lend/components/Modals/WithdrawModal.tsx @@ -1,86 +1,22 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import { ActiveSupportedVault } from '@/bao/lib/types' -import Button from '@/components/Button' -import Input from '@/components/Input' -import { PendingTransaction } from '@/components/Loader/Loader' +import { Asset } from '@/bao/lib/types' import Modal from '@/components/Modal' import Typography from '@/components/Typography' -import useTransactionHandler from '@/hooks/base/useTransactionHandler' -import { useAccountLiquidity } from '@/hooks/vaults/useAccountLiquidity' -import { useSupplyBalances } from '@/hooks/vaults/useBalances' -import { useExchangeRates } from '@/hooks/vaults/useExchangeRates' -import { decimate, exponentiate, getDisplayBalance, sqrt } from '@/utils/numberFormat' -import { faExternalLink } from '@fortawesome/free-solid-svg-icons' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { BigNumber } from 'ethers' -import { formatUnits, parseUnits } from 'ethers/lib/utils' import Image from 'next/future/image' -import React, { useCallback, useMemo, useState } from 'react' -import MarketButton from '../MarketButton' +import React, { useCallback } from 'react' export type WithdrawModalProps = { - asset: ActiveSupportedVault + asset: Asset show: boolean onHide: () => void - vaultName: string + marketName: string } -const WithdrawModal = ({ asset, show, onHide, vaultName }: WithdrawModalProps) => { - const [val, setVal] = useState('') - const supplyBalances = useSupplyBalances(vaultName) - const accountLiquidity = useAccountLiquidity(vaultName) - const { exchangeRates } = useExchangeRates(vaultName) - const { pendingTx, txHash, handleTx } = useTransactionHandler() - const { vaultContract } = asset - +const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) => { const operation = 'Withdraw' - const supply = useMemo( - () => - supplyBalances && - supplyBalances.find(balance => balance.address.toLowerCase() === asset.vaultAddress.toLowerCase()) && - exchangeRates && - exchangeRates[asset.vaultAddress] - ? decimate( - supplyBalances - .find(balance => balance.address.toLowerCase() === asset.vaultAddress.toLowerCase()) - .balance.mul(exchangeRates[asset.vaultAddress]), - ) - : BigNumber.from(0), - [supplyBalances, exchangeRates, asset.vaultAddress], - ) - - let _imfFactor = asset.imfFactor - if (accountLiquidity) { - const _sqrt = sqrt(supply) - const num = exponentiate(parseUnits('1.1')) - const denom = decimate(asset.imfFactor.mul(_sqrt).add(parseUnits('1'))) - _imfFactor = num.div(denom) - } - - let withdrawable = BigNumber.from(0) - if (_imfFactor.gt(asset.collateralFactor) && asset.price.gt(0)) { - if (asset.collateralFactor.mul(asset.price).gt(0)) { - withdrawable = accountLiquidity && exponentiate(accountLiquidity.usdBorrowable).div(decimate(asset.collateralFactor.mul(asset.price))) - } else { - withdrawable = accountLiquidity && exponentiate(accountLiquidity.usdBorrowable).div(decimate(_imfFactor).mul(asset.price)) - } - } - - const max = () => { - return !(accountLiquidity && accountLiquidity.usdBorrowable) || withdrawable.gt(supply) ? supply : withdrawable - } - - const handleChange = useCallback( - (e: React.FormEvent) => { - if (e.currentTarget.value.length < 20) setVal(e.currentTarget.value) - }, - [setVal], - ) - const hideModal = useCallback(() => { onHide() - setVal('') }, [onHide]) return ( @@ -91,7 +27,7 @@ const WithdrawModal = ({ asset, show, onHide, vaultName }: WithdrawModalProps) = Withdraw - {asset.underlyingSymbol} + {asset.name}
<> @@ -102,41 +38,15 @@ const WithdrawModal = ({ asset, show, onHide, vaultName }: WithdrawModalProps) = Withdrawable: - {`${getDisplayBalance(max(), asset.underlyingDecimals)} ${ - asset.underlyingSymbol - }`} + + {asset.name} + {asset.name} +
- setVal(formatUnits(max(), asset.underlyingDecimals))} - label={ -
-
- {asset.symbol} -
-
- } - /> - - - + diff --git a/src/pages/lend/components/SuppliedCard.tsx b/src/pages/lend/components/SuppliedCard.tsx index 1af8aaf2..02c779aa 100644 --- a/src/pages/lend/components/SuppliedCard.tsx +++ b/src/pages/lend/components/SuppliedCard.tsx @@ -18,7 +18,7 @@ export const SuppliedCard = ({ marketName, onUpdate }: { marketName: string; onU useEffect(() => { if (suppliedBalances) { - let suppliedAddresses = suppliedBalances.filter(balance => balance.balance.gt(BigNumber.from(0))).map(balance => balance.address) + const suppliedAddresses = suppliedBalances.filter(balance => balance.balance.gt(BigNumber.from(0))).map(balance => balance.address) setSupplied(assets.filter(asset => asset.supply === true && suppliedAddresses.includes(asset.underlyingAddress[chainId]))) } }, [suppliedBalances]) diff --git a/src/pages/lend/components/SupplyList.tsx b/src/pages/lend/components/SupplyList.tsx index 7caabbef..c74debe4 100644 --- a/src/pages/lend/components/SupplyList.tsx +++ b/src/pages/lend/components/SupplyList.tsx @@ -9,6 +9,9 @@ import { useWeb3React } from '@web3-react/core' import Button from '@/components/Button' import SupplyModal from '@/pages/lend/components/Modals/SupplyModal' import BorrowModal from '@/pages/lend/components/Modals/BorrowModal' +import WithdrawModal from '@/pages/lend/components/Modals/WithdrawModal' +import RepayModal from '@/pages/lend/components/Modals/RepayModal' +import { BigNumber } from 'ethers' export const SupplyList: React.FC = ({ accountBalances, marketName }) => { const assets = Config.lendMarkets[marketName].assets @@ -27,11 +30,15 @@ export const SupplyList: React.FC = ({ accountBalances, marketN const SupplyListItem: React.FC = ({ asset, accountBalances, marketName }) => { const { chainId } = useWeb3React() - const [formattedBalance, setFormattedBalance] = useState(null) + const [totalMarketSupply, setTotalMarketSupply] = useState(getDisplayBalance(BigNumber.from(0), 18)) + const [yourPosition, setYourPosition] = useState(getDisplayBalance(BigNumber.from(0), 18)) const [balance, setBalance] = useState(null) const [showSupplyModal, setShowSupplyModal] = useState(false) + const [showWithdrawModal, setShowWithdrawModal] = useState(false) const [showBorrowModal, setShowBorrowModal] = useState(false) + const [showRepayModal, setShowRepayModal] = useState(false) + /* function fetchBalance(asset: Asset) { if (accountBalances !== null && accountBalances !== undefined) return accountBalances.find(({ address }) => address === asset.underlyingAddress[chainId]) @@ -44,7 +51,7 @@ const SupplyListItem: React.FC = ({ asset, accountBalances, setFormattedBalance(getDisplayBalance(balance.balance, balance.decimals)) } }, [accountBalances]) - + */ return ( <>
@@ -66,20 +73,31 @@ const SupplyListItem: React.FC = ({ asset, accountBalances, - + +
- + +
Account balanceTotal market supplyYour position
{formattedBalance ? formattedBalance : }{totalMarketSupply ? totalMarketSupply : }{yourPosition ? yourPosition : }
- {asset.supply === true && } - - {asset.borrow === true && } + {asset.supply === true && ( + <> + + + + )} + {asset.borrow === true && ( + <> + + + + )}
= ({ asset, accountBalances, marketName={marketName} fullBalance={balance} /> + setShowWithdrawModal(!showWithdrawModal)} + marketName={marketName} + /> setShowBorrowModal(!showBorrowModal)} /> + setShowRepayModal(!showRepayModal)} marketName={marketName} />
) From 67fe452aa10827e1ca913765d6d0285961aef8d3 Mon Sep 17 00:00:00 2001 From: Daizze Date: Fri, 6 Sep 2024 00:15:42 +0200 Subject: [PATCH 05/27] Layout adjustments Supply List --- src/pages/lend/components/SupplyList.tsx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/pages/lend/components/SupplyList.tsx b/src/pages/lend/components/SupplyList.tsx index c74debe4..d11bc9bd 100644 --- a/src/pages/lend/components/SupplyList.tsx +++ b/src/pages/lend/components/SupplyList.tsx @@ -57,7 +57,7 @@ const SupplyListItem: React.FC = ({ asset, accountBalances,
@@ -70,7 +70,7 @@ const SupplyListItem: React.FC = ({ asset, accountBalances,
- +
@@ -88,14 +88,22 @@ const SupplyListItem: React.FC = ({ asset, accountBalances,
{asset.supply === true && ( <> - - + + )} {asset.borrow === true && ( <> - - + + )}
From 893891c1260de5f3e590be3940d74a5eb080df11 Mon Sep 17 00:00:00 2001 From: Daizze Date: Sat, 14 Sep 2024 23:36:00 +0200 Subject: [PATCH 06/27] Adjustments showing asset supply and position for assets --- src/bao/lib/config.ts | 13 +++++- src/bao/lib/types.ts | 10 +++++ src/hooks/lend/useAccountBalances.ts | 11 +---- src/hooks/lend/useBorrowBalances.ts | 54 ++++++++++++++++++++++++ src/hooks/lend/useTotalSupplies.ts | 52 +++++++++++++++++++++++ src/pages/lend/[market].tsx | 11 ++++- src/pages/lend/components/AssetsCard.tsx | 21 +++++++-- src/pages/lend/components/DebtCard.tsx | 8 ++-- src/pages/lend/components/SupplyList.tsx | 45 +++++++++++++++++--- 9 files changed, 199 insertions(+), 26 deletions(-) create mode 100644 src/hooks/lend/useBorrowBalances.ts create mode 100644 src/hooks/lend/useTotalSupplies.ts diff --git a/src/bao/lib/config.ts b/src/bao/lib/config.ts index e7caeb4a..bbe782e1 100644 --- a/src/bao/lib/config.ts +++ b/src/bao/lib/config.ts @@ -822,6 +822,9 @@ export default { { id: 1, active: true, + marketAddress: { + 1: '0x353A07b25c84a522356aF2D9a7c0d7FF481733e9', + }, underlyingAddress: { 1: '0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee', }, @@ -834,9 +837,12 @@ export default { { id: 2, active: true, - underlyingAddress: { + marketAddress: { 1: '0x085c35278fDC840b9Ca74AAB226bA8E2f95C446F', }, + underlyingAddress: { + 1: '0x7945b0A6674b175695e5d1D08aE1e6F13744Abb0', + }, underlyingDecimals: 18, name: 'BaoUSD', icon: '/images/tokens/baoUSD.png', @@ -846,9 +852,12 @@ export default { { id: 3, active: true, - underlyingAddress: { + marketAddress: { 1: '0xdC39e6365AA75D762729513004c956D1475bED20', }, + underlyingAddress: { + 1: '0xf4edfad26EE0D23B69CA93112eccE52704E0006f', + }, underlyingDecimals: 18, name: 'BaoETH', icon: '/images/tokens/baoETH.png', diff --git a/src/bao/lib/types.ts b/src/bao/lib/types.ts index cb03834d..5f978ada 100644 --- a/src/bao/lib/types.ts +++ b/src/bao/lib/types.ts @@ -254,6 +254,9 @@ export interface ActiveLendMarket { export interface Asset { id: number active: boolean + marketAddress: { + [network: number]: string + } underlyingAddress: { [network: number]: string } @@ -270,3 +273,10 @@ export type Balance = { balance: BigNumber decimals: number } + +export type TotalSupply = { + address: string + symbol: string + totalSupply: BigNumber + decimals: number +} diff --git a/src/hooks/lend/useAccountBalances.ts b/src/hooks/lend/useAccountBalances.ts index 1054164e..cbc18397 100644 --- a/src/hooks/lend/useAccountBalances.ts +++ b/src/hooks/lend/useAccountBalances.ts @@ -1,4 +1,3 @@ -import { BigNumber } from 'ethers' import useBao from '@/hooks/base/useBao' import { useWeb3React } from '@web3-react/core' import { useQuery } from '@tanstack/react-query' @@ -6,16 +5,8 @@ import { providerKey } from '@/utils/index' import { Contract } from '@ethersproject/contracts' import { Erc20__factory } from '@/typechain/factories' import MultiCall from '@/utils/multicall' -import { useBlockUpdater } from '@/hooks/base/useBlock' -import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' import Config from '@/bao/lib/config' - -export type Balance = { - address: string - symbol: string - balance: BigNumber - decimals: number -} +import { Balance } from '@/bao/lib/types' export const useAccountBalances = (marketName: string): Balance[] => { const bao = useBao() diff --git a/src/hooks/lend/useBorrowBalances.ts b/src/hooks/lend/useBorrowBalances.ts new file mode 100644 index 00000000..d440546c --- /dev/null +++ b/src/hooks/lend/useBorrowBalances.ts @@ -0,0 +1,54 @@ +import useBao from '@/hooks/base/useBao' +import { useWeb3React } from '@web3-react/core' +import { useQuery } from '@tanstack/react-query' +import { providerKey } from '@/utils/index' +import { Contract } from '@ethersproject/contracts' +import { Ctoken__factory } from '@/typechain/factories' +import MultiCall from '@/utils/multicall' +import Config from '@/bao/lib/config' +import { Balance } from '@/bao/lib/types' +import { BigNumber } from 'ethers' + +export const useBorrowBalances = (marketName: string): Balance[] => { + const bao = useBao() + const { account, library, chainId } = useWeb3React() + + const enabled = !!bao && !!account && !!chainId + const { data: balances } = useQuery( + ['@/hooks/lend/useBorrowBalances', providerKey(library, account, chainId), { enabled }], + async () => { + const tokens = Config.lendMarkets[marketName].assets.map(asset => asset.underlyingAddress[chainId]) + const contracts: Contract[] = tokens.map(address => Ctoken__factory.connect(address, library)) + + const res = MultiCall.parseCallResults( + await bao.multicall.call( + MultiCall.createCallContext( + contracts.map(contract => ({ + ref: contract.address, + contract, + calls: [{ method: 'symbol' }, { method: 'borrowBalanceStored', params: [account] }], + })), + ), + ), + ) + + return Object.keys(res).map(address => { + const balance = typeof res[address][1].values[0] !== 'undefined' ? res[address][1].values[0] : BigNumber.from(0) + const decimals = Config.lendMarkets[marketName].assets.find( + asset => asset.underlyingAddress[chainId] === address, + ).underlyingDecimals + return { + address, + symbol: res[address][0].values[0], + balance: balance, + decimals, + } + }) + }, + { + enabled, + }, + ) + + return balances +} diff --git a/src/hooks/lend/useTotalSupplies.ts b/src/hooks/lend/useTotalSupplies.ts new file mode 100644 index 00000000..649b3a86 --- /dev/null +++ b/src/hooks/lend/useTotalSupplies.ts @@ -0,0 +1,52 @@ +import useBao from '@/hooks/base/useBao' +import { useWeb3React } from '@web3-react/core' +import { useQuery } from '@tanstack/react-query' +import { providerKey } from '@/utils/index' +import { Contract } from '@ethersproject/contracts' +import { Ctoken__factory } from '@/typechain/factories' +import MultiCall from '@/utils/multicall' +import Config from '@/bao/lib/config' +import { TotalSupply } from '@/bao/lib/types' + +export const useTotalSupplies = (marketName: string): TotalSupply[] => { + const bao = useBao() + const { account, library, chainId } = useWeb3React() + + const enabled = !!bao && !!account && !!chainId + const { data: totalSupplies } = useQuery( + ['@/hooks/lend/useTotalSupplies', providerKey(library, account, chainId), { enabled }], + async () => { + const tokens = Config.lendMarkets[marketName].assets.map(asset => asset.underlyingAddress[chainId]) + const contracts: Contract[] = tokens.map(address => Ctoken__factory.connect(address, library)) + + const res = MultiCall.parseCallResults( + await bao.multicall.call( + MultiCall.createCallContext( + contracts.map(contract => ({ + ref: contract.address, + contract, + calls: [{ method: 'symbol' }, { method: 'totalSupply' }], + })), + ), + ), + ) + + return Object.keys(res).map(address => { + const decimals = Config.lendMarkets[marketName].assets.find( + asset => asset.underlyingAddress[chainId] === address, + ).underlyingDecimals + return { + address, + symbol: res[address][0].values[0], + totalSupply: res[address][1].values[0], + decimals, + } + }) + }, + { + enabled, + }, + ) + + return totalSupplies +} diff --git a/src/pages/lend/[market].tsx b/src/pages/lend/[market].tsx index fbb8390e..f89b6f71 100644 --- a/src/pages/lend/[market].tsx +++ b/src/pages/lend/[market].tsx @@ -10,6 +10,8 @@ import Config from '@/bao/lib/config' import AssetsCard from '@/pages/lend/components/AssetsCard' import { useAccountBalances } from '@/hooks/lend/useAccountBalances' import DebtCard from '@/pages/lend/components/DebtCard' +import { useBorrowBalances } from '@/hooks/lend/useBorrowBalances' +import { useTotalSupplies } from '@/hooks/lend/useTotalSupplies' export async function getStaticPaths() { const paths: { params: { market: string } }[] = [] @@ -35,6 +37,8 @@ const Market: NextPage<{ marketName: string }> = ({ marketName }) => { const accountBalances = useAccountBalances(marketName) + const borrowBalances = useBorrowBalances(marketName) + const totalSupplies = useTotalSupplies(marketName) const [supplyVal, setSupplyVal] = useState('0') const [borrowVal, setBorrowVal] = useState('0') @@ -131,7 +135,12 @@ const Market: NextPage<{
- +
diff --git a/src/pages/lend/components/AssetsCard.tsx b/src/pages/lend/components/AssetsCard.tsx index 683b5712..a690ee3d 100644 --- a/src/pages/lend/components/AssetsCard.tsx +++ b/src/pages/lend/components/AssetsCard.tsx @@ -2,9 +2,19 @@ import Card from '@/components/Card/Card' import Typography from '@/components/Typography' import React from 'react' import SupplyList from '@/pages/lend/components/SupplyList' -import { Balance } from '@/bao/lib/types' +import { Balance, TotalSupply } from '@/bao/lib/types' -export const AssetsCard = ({ accountBalances, marketName }: { accountBalances: Balance[]; marketName: string }) => { +export const AssetsCard = ({ + accountBalances, + marketName, + borrowBalances, + totalSupplies, +}: { + accountBalances: Balance[] + marketName: string + borrowBalances: Balance[] + totalSupplies: TotalSupply[] +}) => { return ( <> @@ -12,7 +22,12 @@ export const AssetsCard = ({ accountBalances, marketName }: { accountBalances: B - + diff --git a/src/pages/lend/components/DebtCard.tsx b/src/pages/lend/components/DebtCard.tsx index ee593827..79a23861 100644 --- a/src/pages/lend/components/DebtCard.tsx +++ b/src/pages/lend/components/DebtCard.tsx @@ -2,7 +2,7 @@ import { ActiveLendMarket, ActiveSupportedVault } from '@/bao/lib/types' import Typography from '@/components/Typography' import useBao from '@/hooks/base/useBao' import { useAccountLiquidity } from '@/hooks/vaults/useAccountLiquidity' -import { useBorrowBalances } from '@/hooks/vaults/useBalances' +import { useBorrowBalances } from '@/hooks/lend/useBorrowBalances' import useHealthFactor from '@/hooks/vaults/useHealthFactor' import { decimate, exponentiate, getDisplayBalance } from '@/utils/numberFormat' import { useWeb3React } from '@web3-react/core' @@ -22,12 +22,14 @@ type DashboardCardProps = { const DashboardCard: React.FC = ({ marketName, mintVal, depositVal }: DashboardCardProps) => { const bao = useBao() - const { account } = useWeb3React() + const { account, chainId } = useWeb3React() const borrowBalances = useBorrowBalances(marketName) const asset = useActiveLendMarket(marketName) const borrowed = useMemo( - () => asset && borrowBalances && borrowBalances.find(balance => balance.address === asset.marketAddress).balance, + () => + //asset && borrowBalances && borrowBalances.find(balance => balance.address === asset.underlyingAddress[chainId]).balance, + BigNumber.from(0), [borrowBalances, asset], ) diff --git a/src/pages/lend/components/SupplyList.tsx b/src/pages/lend/components/SupplyList.tsx index d11bc9bd..c45b8e1b 100644 --- a/src/pages/lend/components/SupplyList.tsx +++ b/src/pages/lend/components/SupplyList.tsx @@ -1,10 +1,10 @@ import Loader from '@/components/Loader' import Typography from '@/components/Typography' import Image from 'next/future/image' -import React, { useEffect, useState } from 'react' +import React, { useEffect, useMemo, useState } from 'react' import Config from '@/bao/lib/config' import { getDisplayBalance } from '@/utils/numberFormat' -import { Asset, Balance } from '@/bao/lib/types' +import { Asset, Balance, TotalSupply } from '@/bao/lib/types' import { useWeb3React } from '@web3-react/core' import Button from '@/components/Button' import SupplyModal from '@/pages/lend/components/Modals/SupplyModal' @@ -13,7 +13,7 @@ import WithdrawModal from '@/pages/lend/components/Modals/WithdrawModal' import RepayModal from '@/pages/lend/components/Modals/RepayModal' import { BigNumber } from 'ethers' -export const SupplyList: React.FC = ({ accountBalances, marketName }) => { +export const SupplyList: React.FC = ({ accountBalances, marketName, borrowBalances, totalSupplies }) => { const assets = Config.lendMarkets[marketName].assets return ( @@ -21,23 +21,50 @@ export const SupplyList: React.FC = ({ accountBalances, marketN
{assets && - assets.map(asset => )} + assets.map(asset => ( + + ))}
) } -const SupplyListItem: React.FC = ({ asset, accountBalances, marketName }) => { +const SupplyListItem: React.FC = ({ asset, accountBalances, marketName, borrowBalances, totalSupplies }) => { const { chainId } = useWeb3React() - const [totalMarketSupply, setTotalMarketSupply] = useState(getDisplayBalance(BigNumber.from(0), 18)) - const [yourPosition, setYourPosition] = useState(getDisplayBalance(BigNumber.from(0), 18)) const [balance, setBalance] = useState(null) const [showSupplyModal, setShowSupplyModal] = useState(false) const [showWithdrawModal, setShowWithdrawModal] = useState(false) const [showBorrowModal, setShowBorrowModal] = useState(false) const [showRepayModal, setShowRepayModal] = useState(false) + const yourPosition = useMemo( + () => + borrowBalances && + getDisplayBalance( + borrowBalances.find(balance => balance.address === asset.underlyingAddress[chainId]).balance, + asset.underlyingDecimals, + ), + [borrowBalances, asset], + ) + + const totalMarketSupply = useMemo( + () => + totalSupplies && + getDisplayBalance( + totalSupplies.find(totalSupply => totalSupply.address === asset.underlyingAddress[chainId]).totalSupply, + asset.underlyingDecimals, + ), + [totalSupplies, asset], + ) + /* function fetchBalance(asset: Asset) { if (accountBalances !== null && accountBalances !== undefined) @@ -134,9 +161,13 @@ type SupplyListItemProps = { asset: Asset accountBalances: Balance[] marketName: string + borrowBalances: Balance[] + totalSupplies: TotalSupply[] } type SupplyListProps = { accountBalances: Balance[] marketName: string + borrowBalances: Balance[] + totalSupplies: TotalSupply[] } From 576d4f8c4ccf4ebc134b901e6b70b5e66a1943c1 Mon Sep 17 00:00:00 2001 From: Daizze Date: Sun, 15 Sep 2024 19:46:59 +0200 Subject: [PATCH 07/27] Adjustments showing correct total supply and position --- src/components/Button/Button.tsx | 9 +++++++-- src/hooks/lend/useBorrowBalances.ts | 8 +++----- src/hooks/lend/useTotalSupplies.ts | 11 ++++++++++- src/pages/lend/components/SupplyList.tsx | 13 +++++-------- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 04c346f3..7b804b70 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -19,6 +19,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributes( - ({ children, className = '', size = 'md', fullWidth = false, pendingTx, txHash, inline, text, href, disabled, ...rest }, ref) => { + ( + { children, className = '', size = 'md', fullWidth = false, width = false, pendingTx, txHash, inline, text, href, disabled, ...rest }, + ref, + ) => { const ButtonChild = useMemo(() => { if (href) { return ( @@ -55,8 +59,9 @@ const Button = React.forwardRef( Size[size], inline ? 'inline-block' : 'flex', fullWidth ? 'w-full' : '', + width ? 'w-[' + width + 'px]' : 'w-fit', disabled ? 'cursor-not-allowed opacity-50' : '', - `relative flex w-fit items-center justify-center overflow-hidden glassmorphic-card border border-baoWhite border-opacity-20 + `relative flex items-center justify-center overflow-hidden glassmorphic-card border border-baoWhite border-opacity-20 bg-baoWhite bg-opacity-5 px-4 py-2 font-bakbak text-lg text-baoWhite duration-300 hover:border-baoRed hover:bg-baoRed hover:bg-opacity-20`, className, )} diff --git a/src/hooks/lend/useBorrowBalances.ts b/src/hooks/lend/useBorrowBalances.ts index d440546c..eb7eb816 100644 --- a/src/hooks/lend/useBorrowBalances.ts +++ b/src/hooks/lend/useBorrowBalances.ts @@ -17,7 +17,7 @@ export const useBorrowBalances = (marketName: string): Balance[] => { const { data: balances } = useQuery( ['@/hooks/lend/useBorrowBalances', providerKey(library, account, chainId), { enabled }], async () => { - const tokens = Config.lendMarkets[marketName].assets.map(asset => asset.underlyingAddress[chainId]) + const tokens = Config.lendMarkets[marketName].assets.map(asset => asset.marketAddress[chainId]) const contracts: Contract[] = tokens.map(address => Ctoken__factory.connect(address, library)) const res = MultiCall.parseCallResults( @@ -26,7 +26,7 @@ export const useBorrowBalances = (marketName: string): Balance[] => { contracts.map(contract => ({ ref: contract.address, contract, - calls: [{ method: 'symbol' }, { method: 'borrowBalanceStored', params: [account] }], + calls: [{ method: 'symbol' }, { method: 'balanceOfUnderlying', params: [account] }], })), ), ), @@ -34,9 +34,7 @@ export const useBorrowBalances = (marketName: string): Balance[] => { return Object.keys(res).map(address => { const balance = typeof res[address][1].values[0] !== 'undefined' ? res[address][1].values[0] : BigNumber.from(0) - const decimals = Config.lendMarkets[marketName].assets.find( - asset => asset.underlyingAddress[chainId] === address, - ).underlyingDecimals + const decimals = Config.lendMarkets[marketName].assets.find(asset => asset.marketAddress[chainId] === address).underlyingDecimals return { address, symbol: res[address][0].values[0], diff --git a/src/hooks/lend/useTotalSupplies.ts b/src/hooks/lend/useTotalSupplies.ts index 649b3a86..7f7847a3 100644 --- a/src/hooks/lend/useTotalSupplies.ts +++ b/src/hooks/lend/useTotalSupplies.ts @@ -25,7 +25,16 @@ export const useTotalSupplies = (marketName: string): TotalSupply[] => { contracts.map(contract => ({ ref: contract.address, contract, - calls: [{ method: 'symbol' }, { method: 'totalSupply' }], + calls: [ + { method: 'symbol' }, + { + method: 'balanceOf', + params: [ + Config.lendMarkets[marketName].assets.find(asset => asset.underlyingAddress[chainId] === contract.address) + .marketAddress[chainId], + ], + }, + ], })), ), ), diff --git a/src/pages/lend/components/SupplyList.tsx b/src/pages/lend/components/SupplyList.tsx index c45b8e1b..95a64a44 100644 --- a/src/pages/lend/components/SupplyList.tsx +++ b/src/pages/lend/components/SupplyList.tsx @@ -48,10 +48,7 @@ const SupplyListItem: React.FC = ({ asset, accountBalances, const yourPosition = useMemo( () => borrowBalances && - getDisplayBalance( - borrowBalances.find(balance => balance.address === asset.underlyingAddress[chainId]).balance, - asset.underlyingDecimals, - ), + getDisplayBalance(borrowBalances.find(balance => balance.address === asset.marketAddress[chainId]).balance, asset.underlyingDecimals), [borrowBalances, asset], ) @@ -115,20 +112,20 @@ const SupplyListItem: React.FC = ({ asset, accountBalances,
{asset.supply === true && ( <> - - )} {asset.borrow === true && ( <> - - From 341907fd0a38922e5e427cc3996ff288521b118f Mon Sep 17 00:00:00 2001 From: Daizze Date: Mon, 16 Sep 2024 20:30:26 +0200 Subject: [PATCH 08/27] Adding market header information --- src/hooks/lend/useBorrowApy.ts | 56 ++++++++++++++++++++++++++++ src/hooks/lend/useOraclePrice.ts | 29 ++++++++++++++ src/hooks/lend/useTotalCollateral.ts | 47 +++++++++++++++++++++++ src/hooks/lend/useTotalDebt.ts | 46 +++++++++++++++++++++++ src/pages/lend/[market].tsx | 38 ++++++++++++++++--- 5 files changed, 211 insertions(+), 5 deletions(-) create mode 100644 src/hooks/lend/useBorrowApy.ts create mode 100644 src/hooks/lend/useOraclePrice.ts create mode 100644 src/hooks/lend/useTotalCollateral.ts create mode 100644 src/hooks/lend/useTotalDebt.ts diff --git a/src/hooks/lend/useBorrowApy.ts b/src/hooks/lend/useBorrowApy.ts new file mode 100644 index 00000000..36a0455e --- /dev/null +++ b/src/hooks/lend/useBorrowApy.ts @@ -0,0 +1,56 @@ +import useBao from '@/hooks/base/useBao' +import { useWeb3React } from '@web3-react/core' +import { useQuery } from '@tanstack/react-query' +import { providerKey } from '@/utils/index' +import { Contract } from '@ethersproject/contracts' +import { Ctoken__factory } from '@/typechain/factories' +import MultiCall from '@/utils/multicall' +import Config from '@/bao/lib/config' +import { BigNumber } from 'ethers' +import { parseUnits } from 'ethers/lib/utils' + +export const SECONDS_PER_BLOCK = 12 +export const SECONDS_PER_DAY = 24 * 60 * 60 +export const BLOCKS_PER_SECOND = 1 / SECONDS_PER_BLOCK +export const BLOCKS_PER_DAY = BLOCKS_PER_SECOND * SECONDS_PER_DAY +export const DAYS_PER_YEAR = 365 + +const toApy = (rate: BigNumber) => ((Math.pow((rate.toNumber() / 1e18) * BLOCKS_PER_DAY + 1, DAYS_PER_YEAR) - 1) * 100).toFixed(18) + +export const useBorrowApy = (marketName: string): BigNumber => { + const bao = useBao() + const { account, library, chainId } = useWeb3React() + + const enabled = !!bao && !!account && !!chainId + const { data: borrowApy } = useQuery( + ['@/hooks/lend/useBorrowApy', providerKey(library, account, chainId), { enabled }], + async () => { + const address = Config.lendMarkets[marketName].marketAddresses[chainId] + const contracts: Contract[] = [Ctoken__factory.connect(address, library)] + + const res = MultiCall.parseCallResults( + await bao.multicall.call( + MultiCall.createCallContext( + contracts.map(contract => ({ + ref: contract.address, + contract, + calls: [ + { method: 'symbol' }, + { + method: 'borrowRatePerBlock', + }, + ], + })), + ), + ), + ) + + return parseUnits(toApy(res[address][1].values[0]).toString()) + }, + { + enabled, + }, + ) + + return borrowApy +} diff --git a/src/hooks/lend/useOraclePrice.ts b/src/hooks/lend/useOraclePrice.ts new file mode 100644 index 00000000..c67a454b --- /dev/null +++ b/src/hooks/lend/useOraclePrice.ts @@ -0,0 +1,29 @@ +import useBao from '@/hooks/base/useBao' +import { useWeb3React } from '@web3-react/core' +import { useQuery } from '@tanstack/react-query' +import { providerKey } from '@/utils/index' +import { Ctoken__factory, VaultOracle__factory } from '@/typechain/factories' +import Config from '@/bao/lib/config' +import { BigNumber } from 'ethers' + +export const useOraclePrice = (marketName: string): BigNumber => { + const bao = useBao() + const { account, library, chainId } = useWeb3React() + const signerOrProvider = account ? library.getSigner() : library + + const enabled = !!bao && !!account && !!chainId + const { data: price } = useQuery( + ['@/hooks/lend/useOraclePrice', providerKey(library, account, chainId), { enabled }], + async () => { + const oracle = VaultOracle__factory.connect(Config.lendMarkets[marketName].oracle, signerOrProvider) + const address = Config.lendMarkets[marketName].marketAddresses[chainId] + + return await oracle.callStatic.getUnderlyingPrice(address) + }, + { + enabled, + }, + ) + + return price +} diff --git a/src/hooks/lend/useTotalCollateral.ts b/src/hooks/lend/useTotalCollateral.ts new file mode 100644 index 00000000..0942b08a --- /dev/null +++ b/src/hooks/lend/useTotalCollateral.ts @@ -0,0 +1,47 @@ +import useBao from '@/hooks/base/useBao' +import { useWeb3React } from '@web3-react/core' +import { useQuery } from '@tanstack/react-query' +import { providerKey } from '@/utils/index' +import { Contract } from '@ethersproject/contracts' +import { Ctoken__factory } from '@/typechain/factories' +import MultiCall from '@/utils/multicall' +import Config from '@/bao/lib/config' +import { BigNumber } from 'ethers' + +export const useTotalCollateral = (marketName: string): BigNumber => { + const bao = useBao() + const { account, library, chainId } = useWeb3React() + + const enabled = !!bao && !!account && !!chainId + const { data: totalCollateral } = useQuery( + ['@/hooks/lend/useTotalCollateral', providerKey(library, account, chainId), { enabled }], + async () => { + const address = Config.lendMarkets[marketName].marketAddresses[chainId] + const contracts: Contract[] = [Ctoken__factory.connect(address, library)] + + const res = MultiCall.parseCallResults( + await bao.multicall.call( + MultiCall.createCallContext( + contracts.map(contract => ({ + ref: contract.address, + contract, + calls: [ + { method: 'symbol' }, + { + method: 'getCash', + }, + ], + })), + ), + ), + ) + + return res[address][1].values[0] + }, + { + enabled, + }, + ) + + return totalCollateral +} diff --git a/src/hooks/lend/useTotalDebt.ts b/src/hooks/lend/useTotalDebt.ts new file mode 100644 index 00000000..71a7b98e --- /dev/null +++ b/src/hooks/lend/useTotalDebt.ts @@ -0,0 +1,46 @@ +import useBao from '@/hooks/base/useBao' +import { useWeb3React } from '@web3-react/core' +import { useQuery } from '@tanstack/react-query' +import { providerKey } from '@/utils/index' +import { Contract } from '@ethersproject/contracts' +import { Ctoken__factory } from '@/typechain/factories' +import MultiCall from '@/utils/multicall' +import Config from '@/bao/lib/config' +import { BigNumber } from 'ethers' + +export const useTotalDebt = (marketName: string): BigNumber => { + const bao = useBao() + const { account, library, chainId } = useWeb3React() + + const enabled = !!bao && !!account && !!chainId + const { data: totalDebt } = useQuery( + ['@/hooks/lend/useTotalDebts', providerKey(library, account, chainId), { enabled }], + async () => { + const address = Config.lendMarkets[marketName].marketAddresses[chainId] + const contracts: Contract[] = [Ctoken__factory.connect(address, library)] + + const res = MultiCall.parseCallResults( + await bao.multicall.call( + MultiCall.createCallContext( + contracts.map(contract => ({ + ref: contract.address, + contract, + calls: [ + { method: 'symbol' }, + { + method: 'totalBorrows', + }, + ], + })), + ), + ), + ) + return res[address][1].values[0] + }, + { + enabled, + }, + ) + + return totalDebt +} diff --git a/src/pages/lend/[market].tsx b/src/pages/lend/[market].tsx index f89b6f71..967a30dc 100644 --- a/src/pages/lend/[market].tsx +++ b/src/pages/lend/[market].tsx @@ -5,13 +5,20 @@ import { NextPage } from 'next' import { NextSeo } from 'next-seo' import Image from 'next/future/image' import Link from 'next/link' -import React, { useCallback, useState } from 'react' +import React, { useCallback, useMemo, useState } from 'react' import Config from '@/bao/lib/config' import AssetsCard from '@/pages/lend/components/AssetsCard' import { useAccountBalances } from '@/hooks/lend/useAccountBalances' import DebtCard from '@/pages/lend/components/DebtCard' import { useBorrowBalances } from '@/hooks/lend/useBorrowBalances' import { useTotalSupplies } from '@/hooks/lend/useTotalSupplies' +import { useOraclePrice } from '@/hooks/lend/useOraclePrice' +import { decimate, getDisplayBalance } from '@/utils/numberFormat' +import Loader from '@/components/Loader' +import { useTotalCollateral } from '@/hooks/lend/useTotalCollateral' +import { BigNumber } from 'ethers' +import { useTotalDebt } from '@/hooks/lend/useTotalDebt' +import { useBorrowApy } from '@/hooks/lend/useBorrowApy' export async function getStaticPaths() { const paths: { params: { market: string } }[] = [] @@ -39,9 +46,30 @@ const Market: NextPage<{ const accountBalances = useAccountBalances(marketName) const borrowBalances = useBorrowBalances(marketName) const totalSupplies = useTotalSupplies(marketName) + const oraclePrice = useOraclePrice(marketName) + const totalCollateral = useTotalCollateral(marketName) + const borrowApy = useBorrowApy(marketName) + const totalDebt = useTotalDebt(marketName) const [supplyVal, setSupplyVal] = useState('0') const [borrowVal, setBorrowVal] = useState('0') + const formattedOraclePrice = useMemo(() => oraclePrice && '$' + getDisplayBalance(oraclePrice, 18), [oraclePrice]) + const formattedTotalCollateral = useMemo( + () => totalCollateral && oraclePrice && '$' + getDisplayBalance(totalCollateral.mul(decimate(oraclePrice)), 18), + [totalCollateral, oraclePrice], + ) + const utilization = useMemo(() => getDisplayBalance(getUtilization()) + '%', [totalDebt, totalCollateral]) + + function getUtilization() { + if (totalDebt && totalCollateral && decimate(totalCollateral) > BigNumber.from(0)) { + return totalDebt.div(decimate(totalCollateral)).mul(100) + } + + return BigNumber.from(0) + } + + const formattedBorrowApy = useMemo(() => borrowApy && getDisplayBalance(borrowApy, 18, 2) + '%', [borrowApy]) + const handleSupplyVal = useCallback( (updatedState: any) => { // update the parent component's state with the new value @@ -96,7 +124,7 @@ const Market: NextPage<{ Oracle Price - $123.00 {/*getDisplayBalance(synth.price)*/} + {formattedOraclePrice ? formattedOraclePrice : }
@@ -104,7 +132,7 @@ const Market: NextPage<{ Total Collateral - $1000.00 {/*getDisplayBalance(decimate(totalCollateral), synth.underlyingDecimals)*/} + {formattedTotalCollateral ? formattedTotalCollateral : }
@@ -112,7 +140,7 @@ const Market: NextPage<{ Utilization - {/*getDisplayBalance(totalDebt.div(decimate(totalCollateral)).mul(100))*/} 12.3% + {utilization ? utilization : }
@@ -120,7 +148,7 @@ const Market: NextPage<{ Borrow Rate - {/*getDisplayBalance(synth.borrowApy, 18, 2)*/}50% + {formattedBorrowApy ? formattedBorrowApy : } vAPY
From ee0b5ddc591d8a644c5e64f07a4c22c2ade94c93 Mon Sep 17 00:00:00 2001 From: Daizze Date: Tue, 1 Oct 2024 01:09:14 +0200 Subject: [PATCH 09/27] Adding changes DebtCard --- src/hooks/lend/useAccountLiquidity.ts | 94 ++++++++++++++++++++++++ src/hooks/lend/useBorrowRate.ts | 39 ++++++++++ src/hooks/lend/useExchangeRate.ts | 55 ++++++++++++++ src/hooks/lend/useHealthFactor.ts | 52 +++++++++++++ src/hooks/lend/useSupplyRate.ts | 39 ++++++++++ src/pages/lend/[market].tsx | 10 ++- src/pages/lend/components/DebtCard.tsx | 63 ++++++++-------- src/pages/lend/components/SupplyList.tsx | 8 +- 8 files changed, 326 insertions(+), 34 deletions(-) create mode 100644 src/hooks/lend/useAccountLiquidity.ts create mode 100644 src/hooks/lend/useBorrowRate.ts create mode 100644 src/hooks/lend/useExchangeRate.ts create mode 100644 src/hooks/lend/useHealthFactor.ts create mode 100644 src/hooks/lend/useSupplyRate.ts diff --git a/src/hooks/lend/useAccountLiquidity.ts b/src/hooks/lend/useAccountLiquidity.ts new file mode 100644 index 00000000..78d84f99 --- /dev/null +++ b/src/hooks/lend/useAccountLiquidity.ts @@ -0,0 +1,94 @@ +import Config from '@/bao/lib/config' +import { Balance } from '@/bao/lib/types' +import useContract from '@/hooks/base/useContract' +import type { Comptroller } from '@/typechain/index' +import { decimate } from '@/utils/numberFormat' +import { useWeb3React } from '@web3-react/core' +import { BigNumber } from 'ethers' +import { providerKey } from '@/utils/index' +import { useQuery } from '@tanstack/react-query' +import { useExchangeRates } from './useExchangeRate' +import { useSupplyRate } from '@/hooks/lend/useSupplyRate' +import { useBorrowRate } from '@/hooks/lend/useBorrowRate' +import { useOraclePrice } from '@/hooks/lend/useOraclePrice' + +export type AccountLiquidity = { + netApy: BigNumber + usdSupply: BigNumber + usdBorrow: BigNumber + usdBorrowable: BigNumber +} + +export const useAccountLiquidity = (marketName: string, supplyBalances: Balance[], borrowBalances: Balance[]): AccountLiquidity => { + const { library, account, chainId } = useWeb3React() + const market = Config.lendMarkets[marketName] + const { exchangeRates } = useExchangeRates(marketName) + const price = useOraclePrice(marketName) + const supplyRate = useSupplyRate(marketName) + const borrowRate = useBorrowRate(marketName) + const comptroller = useContract('Comptroller', market.comptroller) + + const enabled = !!comptroller && !!account && !!market && !!supplyBalances && !!borrowBalances && !!exchangeRates && !!price + const { data: accountLiquidity } = useQuery( + [ + '@/hooks/vaults/useAccountLiquidity', + providerKey(library, account, chainId), + { + enabled, + supplyBalances, + borrowBalances, + exchangeRates, + price, + marketName, + }, + ], + async () => { + const compAccountLiqudity = await comptroller.getAccountLiquidity(account) + + const usdSupply = Object.keys(exchangeRates).reduce((prev: BigNumber, addr: string) => { + const supply = supplyBalances.find(balance => balance.address === addr) + return prev.add(decimate(supply.balance.mul(exchangeRates[addr]).mul(price))) + }, BigNumber.from(0)) + + const usdBorrow = Object.entries(borrowBalances).reduce((prev: BigNumber, [, { address, balance }]) => { + return prev.add(balance.mul(price)) + }, BigNumber.from(0)) + + const supplyApy = supplyBalances + .find(balance => balance.address === market.marketAddresses[chainId]) + .balance.mul(exchangeRates[market.marketAddresses[chainId]]) + .mul(price) + .mul(supplyRate) + + const borrowApy = borrowBalances + .find(balance => balance.address === market.marketAddresses[chainId]) + .balance.mul(price) + .mul(supplyRate) + + const netApy = + supplyApy.gt(borrowApy) && !usdSupply.eq(0) + ? supplyApy.sub(borrowApy).div(usdSupply) + : borrowApy.gt(supplyApy) && !usdBorrow.eq(0) + ? supplyApy.sub(borrowApy).div(usdBorrow) + : BigNumber.from(0) + + return { + netApy, + usdSupply, + usdBorrow, + usdBorrowable: compAccountLiqudity[1], + } + }, + { + enabled, + placeholderData: { + netApy: BigNumber.from(0), + usdSupply: BigNumber.from(0), + usdBorrow: BigNumber.from(0), + usdBorrowable: BigNumber.from(0), + }, + }, + ) + + return accountLiquidity +} diff --git a/src/hooks/lend/useBorrowRate.ts b/src/hooks/lend/useBorrowRate.ts new file mode 100644 index 00000000..40c2610b --- /dev/null +++ b/src/hooks/lend/useBorrowRate.ts @@ -0,0 +1,39 @@ +import { BigNumber } from 'ethers' +import MultiCall from '@/utils/multicall' +import useBao from '../base/useBao' +import { useWeb3React } from '@web3-react/core' +import { providerKey } from '@/utils/index' +import { useQuery } from '@tanstack/react-query' +import Config from '@/bao/lib/config' +import { Contract } from '@ethersproject/contracts' +import { Ctoken__factory } from '@/typechain/factories' + +export const useBorrowRate = (marketName: string): BigNumber => { + const bao = useBao() + const { library, account, chainId } = useWeb3React() + const market = Config.lendMarkets[marketName] + + const enabled = !!market && !!bao && !!account + const { data: borrowRate } = useQuery( + ['@/hooks/lend/borrowRate', providerKey(library, account, chainId), { enabled, marketName }], + async () => { + const address = market.marketAddresses[chainId] + const contracts: Contract[] = [Ctoken__factory.connect(address, library)] + const multiCallContext = MultiCall.createCallContext( + contracts.map(contract => ({ + ref: contract.address, + contract: contract, + calls: [{ method: 'borrowRatePerBlock' }], + })), + ) + const res = MultiCall.parseCallResults(await bao.multicall.call(multiCallContext)) + + return res[address][1].values[0] + }, + { + enabled, + }, + ) + + return borrowRate +} diff --git a/src/hooks/lend/useExchangeRate.ts b/src/hooks/lend/useExchangeRate.ts new file mode 100644 index 00000000..7dd2f4de --- /dev/null +++ b/src/hooks/lend/useExchangeRate.ts @@ -0,0 +1,55 @@ +import { BigNumber } from 'ethers' +import { ActiveSupportedVault } from '@/bao/lib/types' +import MultiCall from '@/utils/multicall' +import useBao from '../base/useBao' +import { useVaults } from '@/hooks/vaults/useVaults' +import { useWeb3React } from '@web3-react/core' +import { providerKey } from '@/utils/index' +import { useQuery } from '@tanstack/react-query' +import { useBlockUpdater } from '@/hooks/base/useBlock' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' +import { useEffect } from 'react' +import Config from '@/bao/lib/config' +import { Contract } from '@ethersproject/contracts' +import { Ctoken__factory, Erc20__factory } from '@/typechain/factories' + +type ExchangeRates = { + exchangeRates: { [key: string]: BigNumber } +} + +export const useExchangeRates = (marketName: string): ExchangeRates => { + const bao = useBao() + const { library, account, chainId } = useWeb3React() + const market = Config.lendMarkets[marketName] + + const enabled = !!market && !!bao && !!account + const { data: exchangeRates } = useQuery( + ['@/hooks/lend/useExchangeRates', providerKey(library, account, chainId), { enabled, marketName }], + async () => { + const contracts: Contract[] = [Ctoken__factory.connect(market.marketAddresses[chainId], library)] + const multiCallContext = MultiCall.createCallContext( + contracts.map(contract => ({ + ref: contract.address, + contract: contract, + calls: [{ method: 'exchangeRateStored' }], + })), + ) + const data = MultiCall.parseCallResults(await bao.multicall.call(multiCallContext)) + + return Object.keys(data).reduce( + (exchangeRate: { [key: string]: BigNumber }, address: string) => ({ + ...exchangeRate, + [address]: data[address][0].values[0], + }), + {}, + ) + }, + { + enabled, + }, + ) + + return { + exchangeRates, + } +} diff --git a/src/hooks/lend/useHealthFactor.ts b/src/hooks/lend/useHealthFactor.ts new file mode 100644 index 00000000..c7a93dbf --- /dev/null +++ b/src/hooks/lend/useHealthFactor.ts @@ -0,0 +1,52 @@ +import Multicall from '@/utils/multicall' +import { useWeb3React } from '@web3-react/core' +import { BigNumber } from 'ethers' +import { parseUnits } from 'ethers/lib/utils' +import { useCallback, useEffect, useState } from 'react' +import useBao from '../base/useBao' +import { useAccountLiquidity } from './useAccountLiquidity' +import { exponentiate } from '@/utils/numberFormat' +import Config from '@/bao/lib/config' +import { Balance } from '@/bao/lib/types' +import { useOraclePrice } from '@/hooks/lend/useOraclePrice' +import useContract from '@/hooks/base/useContract' +import type { Comptroller } from '@/typechain/Comptroller' + +const useHealthFactor = (marketName: string, borrowBalances: Balance[], supplyBalances: Balance[], borrowChange: BigNumber) => { + const [healthFactor, setHealthFactor] = useState() + const bao = useBao() + const { account, chainId, library } = useWeb3React() + const market = Config.lendMarkets[marketName] + const accountLiquidity = useAccountLiquidity(marketName, borrowBalances, supplyBalances) + const price = useOraclePrice(marketName) + const comptroller = useContract('Comptroller', market.comptroller) + + const fetchHealthFactor = useCallback(async () => { + const collateralFactor = await comptroller.markets(market.marketAddresses[chainId]) + + if (Object.keys(borrowBalances).length === 0) return setHealthFactor(BigNumber.from(0)) + + const collateralSummation = BigNumber.from(price) + .div(BigNumber.from(10).pow(36 - 18)) + .mul(parseUnits(borrowBalances.find(balance => balance.address === market.marketAddresses[chainId]).balance.toString())) + .div(BigNumber.from(10).pow(18)) + .mul(parseUnits(collateralFactor[1].toString())) + + try { + const _healthFactor = collateralSummation.div(exponentiate(borrowChange).toString()) + setHealthFactor(_healthFactor) + } catch { + setHealthFactor(BigNumber.from(0)) + } + }, [market, bao, account, price, borrowChange]) + + useEffect(() => { + if (!(market && accountLiquidity && bao && account && price)) return + + fetchHealthFactor() + }, [market, accountLiquidity, bao, account, price, fetchHealthFactor, borrowChange]) + + return healthFactor +} + +export default useHealthFactor diff --git a/src/hooks/lend/useSupplyRate.ts b/src/hooks/lend/useSupplyRate.ts new file mode 100644 index 00000000..ea82112b --- /dev/null +++ b/src/hooks/lend/useSupplyRate.ts @@ -0,0 +1,39 @@ +import { BigNumber } from 'ethers' +import MultiCall from '@/utils/multicall' +import useBao from '../base/useBao' +import { useWeb3React } from '@web3-react/core' +import { providerKey } from '@/utils/index' +import { useQuery } from '@tanstack/react-query' +import Config from '@/bao/lib/config' +import { Contract } from '@ethersproject/contracts' +import { Ctoken__factory } from '@/typechain/factories' + +export const useSupplyRate = (marketName: string): BigNumber => { + const bao = useBao() + const { library, account, chainId } = useWeb3React() + const market = Config.lendMarkets[marketName] + + const enabled = !!market && !!bao && !!account + const { data: supplyRate } = useQuery( + ['@/hooks/lend/supplyRate', providerKey(library, account, chainId), { enabled, marketName }], + async () => { + const address = market.marketAddresses[chainId] + const contracts: Contract[] = [Ctoken__factory.connect(address, library)] + const multiCallContext = MultiCall.createCallContext( + contracts.map(contract => ({ + ref: contract.address, + contract: contract, + calls: [{ method: 'supplyRatePerBlock' }], + })), + ) + const res = MultiCall.parseCallResults(await bao.multicall.call(multiCallContext)) + + return res[address][1].values[0] + }, + { + enabled, + }, + ) + + return supplyRate +} diff --git a/src/pages/lend/[market].tsx b/src/pages/lend/[market].tsx index 967a30dc..5def1cbb 100644 --- a/src/pages/lend/[market].tsx +++ b/src/pages/lend/[market].tsx @@ -19,6 +19,7 @@ import { useTotalCollateral } from '@/hooks/lend/useTotalCollateral' import { BigNumber } from 'ethers' import { useTotalDebt } from '@/hooks/lend/useTotalDebt' import { useBorrowApy } from '@/hooks/lend/useBorrowApy' +import { useSupplyBalances } from '@/hooks/lend/useSupplyBalances' export async function getStaticPaths() { const paths: { params: { market: string } }[] = [] @@ -45,6 +46,7 @@ const Market: NextPage<{ }> = ({ marketName }) => { const accountBalances = useAccountBalances(marketName) const borrowBalances = useBorrowBalances(marketName) + const supplyBalances = useSupplyBalances(marketName) const totalSupplies = useTotalSupplies(marketName) const oraclePrice = useOraclePrice(marketName) const totalCollateral = useTotalCollateral(marketName) @@ -158,7 +160,13 @@ const Market: NextPage<{
- +
diff --git a/src/pages/lend/components/DebtCard.tsx b/src/pages/lend/components/DebtCard.tsx index 79a23861..08a39fb1 100644 --- a/src/pages/lend/components/DebtCard.tsx +++ b/src/pages/lend/components/DebtCard.tsx @@ -1,47 +1,55 @@ -import { ActiveLendMarket, ActiveSupportedVault } from '@/bao/lib/types' +import { Balance } from '@/bao/lib/types' import Typography from '@/components/Typography' import useBao from '@/hooks/base/useBao' -import { useAccountLiquidity } from '@/hooks/vaults/useAccountLiquidity' -import { useBorrowBalances } from '@/hooks/lend/useBorrowBalances' -import useHealthFactor from '@/hooks/vaults/useHealthFactor' +import { useAccountLiquidity } from '@/hooks/lend/useAccountLiquidity' import { decimate, exponentiate, getDisplayBalance } from '@/utils/numberFormat' import { useWeb3React } from '@web3-react/core' import { BigNumber } from 'ethers' import { formatUnits, parseUnits } from 'ethers/lib/utils' -import Image from 'next/future/image' import React, { useMemo } from 'react' import { faDashboard } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { useActiveLendMarket } from '@/hooks/lend/useActiveLendMarket' +import useHealthFactor from '@/hooks/lend/useHealthFactor' type DashboardCardProps = { marketName: string + borrowBalances: Balance[] + supplyBalances: Balance[] mintVal: string depositVal: string } -const DashboardCard: React.FC = ({ marketName, mintVal, depositVal }: DashboardCardProps) => { +const DashboardCard: React.FC = ({ + marketName, + mintVal, + borrowBalances, + supplyBalances, + depositVal, +}: DashboardCardProps) => { const bao = useBao() const { account, chainId } = useWeb3React() - const borrowBalances = useBorrowBalances(marketName) const asset = useActiveLendMarket(marketName) - - const borrowed = useMemo( - () => - //asset && borrowBalances && borrowBalances.find(balance => balance.address === asset.underlyingAddress[chainId]).balance, - BigNumber.from(0), - [borrowBalances, asset], - ) + const accountLiquidity = useAccountLiquidity(marketName, borrowBalances, supplyBalances) const change = mintVal && depositVal ? BigNumber.from(mintVal).sub(BigNumber.from(depositVal)) : BigNumber.from(0) - const borrow = BigNumber.from(0) + const borrow = accountLiquidity ? accountLiquidity.usdBorrow : BigNumber.from(0) const newBorrow = borrow ? borrow.sub(change.gt(0) ? change : 0) : BigNumber.from(0) - const borrowable = BigNumber.from(0) + const borrowable = accountLiquidity ? accountLiquidity.usdBorrow.add(exponentiate(accountLiquidity.usdBorrowable)) : BigNumber.from(0) const newBorrowable = asset && decimate(borrowable).sub(BigNumber.from(parseUnits(formatUnits(change, 36 - 18)))) const borrowChange = borrow.add(exponentiate(change)) - const healthFactor = BigNumber.from('10') + const healthFactor = useHealthFactor(marketName, borrowBalances, supplyBalances, borrowChange) + const barPercentage = parseFloat( + getDisplayBalance( + accountLiquidity && newBorrowable && !newBorrowable.eq(0) + ? (parseFloat(accountLiquidity.usdBorrow.toString()) / parseFloat(newBorrowable.toString())) * 100 + : 0, + 18, + 2, + ), + ) const healthFactorColor = (healthFactor: BigNumber) => { const c = healthFactor.eq(0) ? `${(props: any) => props.theme.color.text[100]}` @@ -60,15 +68,7 @@ const DashboardCard: React.FC = ({ marketName, mintVal, depo
@@ -96,7 +96,12 @@ const DashboardCard: React.FC = ({ marketName, mintVal, depo Your Collateral - ${`${bao && account ? getDisplayBalance(decimate(BigNumber.from(BigNumber.from(0).toString())), 18, 2) : 0}`} + $ + {`${ + bao && account && accountLiquidity + ? getDisplayBalance(decimate(BigNumber.from(accountLiquidity.usdSupply.toString())), 18, 2) + : 0 + }`}{' '}
@@ -104,7 +109,7 @@ const DashboardCard: React.FC = ({ marketName, mintVal, depo Your Debt - $ 0 + ${accountLiquidity ? getDisplayBalance(decimate(accountLiquidity.usdBorrow), 18, 2) : 0}
@@ -120,7 +125,7 @@ const DashboardCard: React.FC = ({ marketName, mintVal, depo Debt Limit Remaining - $ 0 + ${getDisplayBalance(accountLiquidity ? accountLiquidity.usdBorrowable.sub(change) : BigNumber.from(0))}
diff --git a/src/pages/lend/components/SupplyList.tsx b/src/pages/lend/components/SupplyList.tsx index 95a64a44..4606f1e1 100644 --- a/src/pages/lend/components/SupplyList.tsx +++ b/src/pages/lend/components/SupplyList.tsx @@ -112,20 +112,20 @@ const SupplyListItem: React.FC = ({ asset, accountBalances,
{asset.supply === true && ( <> - - )} {asset.borrow === true && ( <> - - From c3eca70de2799f0a46823183c08b6c850b35eaca Mon Sep 17 00:00:00 2001 From: Daizze Date: Tue, 8 Oct 2024 00:23:51 +0200 Subject: [PATCH 10/27] Bug fixes DebtCard --- src/hooks/lend/useAccountLiquidity.ts | 41 ++++++++++++++++---------- src/hooks/lend/useBorrowRate.ts | 2 +- src/hooks/lend/useHealthFactor.ts | 31 +++++++++++-------- src/hooks/lend/useSupplyRate.ts | 2 +- src/pages/lend/[market].tsx | 19 +++++++----- src/pages/lend/components/DebtCard.tsx | 8 +++-- 6 files changed, 61 insertions(+), 42 deletions(-) diff --git a/src/hooks/lend/useAccountLiquidity.ts b/src/hooks/lend/useAccountLiquidity.ts index 78d84f99..72cd8b9d 100644 --- a/src/hooks/lend/useAccountLiquidity.ts +++ b/src/hooks/lend/useAccountLiquidity.ts @@ -9,7 +9,6 @@ import { providerKey } from '@/utils/index' import { useQuery } from '@tanstack/react-query' import { useExchangeRates } from './useExchangeRate' import { useSupplyRate } from '@/hooks/lend/useSupplyRate' -import { useBorrowRate } from '@/hooks/lend/useBorrowRate' import { useOraclePrice } from '@/hooks/lend/useOraclePrice' export type AccountLiquidity = { @@ -25,10 +24,10 @@ export const useAccountLiquidity = (marketName: string, supplyBalances: Balance[ const { exchangeRates } = useExchangeRates(marketName) const price = useOraclePrice(marketName) const supplyRate = useSupplyRate(marketName) - const borrowRate = useBorrowRate(marketName) const comptroller = useContract('Comptroller', market.comptroller) - const enabled = !!comptroller && !!account && !!market && !!supplyBalances && !!borrowBalances && !!exchangeRates && !!price + const enabled = + !!comptroller && !!account && !!market && !!supplyBalances && !!borrowBalances && !!exchangeRates && !!price && !!supplyRate const { data: accountLiquidity } = useQuery( [ '@/hooks/vaults/useAccountLiquidity', @@ -45,25 +44,35 @@ export const useAccountLiquidity = (marketName: string, supplyBalances: Balance[ async () => { const compAccountLiqudity = await comptroller.getAccountLiquidity(account) - const usdSupply = Object.keys(exchangeRates).reduce((prev: BigNumber, addr: string) => { - const supply = supplyBalances.find(balance => balance.address === addr) - return prev.add(decimate(supply.balance.mul(exchangeRates[addr]).mul(price))) - }, BigNumber.from(0)) + const usdSupply = + supplyBalances && supplyBalances.length > 0 + ? Object.keys(exchangeRates).reduce((prev: BigNumber, addr: string) => { + const supply = supplyBalances.find(balance => balance.address === addr) + if (!supply) return + return prev.add(decimate(supply.balance.mul(exchangeRates[addr]).mul(price))) + }, BigNumber.from(0)) + : BigNumber.from(0) const usdBorrow = Object.entries(borrowBalances).reduce((prev: BigNumber, [, { address, balance }]) => { return prev.add(balance.mul(price)) }, BigNumber.from(0)) - const supplyApy = supplyBalances - .find(balance => balance.address === market.marketAddresses[chainId]) - .balance.mul(exchangeRates[market.marketAddresses[chainId]]) - .mul(price) - .mul(supplyRate) + const supplyApy = + supplyBalances && supplyBalances.length > 0 + ? supplyBalances + .find(balance => balance.address === market.underlyingAddresses[chainId]) + .balance.mul(exchangeRates[market.marketAddresses[chainId]]) + .mul(price) + .mul(supplyRate) + : BigNumber.from(0) - const borrowApy = borrowBalances - .find(balance => balance.address === market.marketAddresses[chainId]) - .balance.mul(price) - .mul(supplyRate) + const borrowApy = + borrowBalances && borrowBalances.length > 0 + ? borrowBalances + .find(balance => balance.address === market.marketAddresses[chainId]) + .balance.mul(price) + .mul(supplyRate) + : BigNumber.from(0) const netApy = supplyApy.gt(borrowApy) && !usdSupply.eq(0) diff --git a/src/hooks/lend/useBorrowRate.ts b/src/hooks/lend/useBorrowRate.ts index 40c2610b..aa00be29 100644 --- a/src/hooks/lend/useBorrowRate.ts +++ b/src/hooks/lend/useBorrowRate.ts @@ -28,7 +28,7 @@ export const useBorrowRate = (marketName: string): BigNumber => { ) const res = MultiCall.parseCallResults(await bao.multicall.call(multiCallContext)) - return res[address][1].values[0] + return res[address][0].values[0] }, { enabled, diff --git a/src/hooks/lend/useHealthFactor.ts b/src/hooks/lend/useHealthFactor.ts index c7a93dbf..f0497b90 100644 --- a/src/hooks/lend/useHealthFactor.ts +++ b/src/hooks/lend/useHealthFactor.ts @@ -1,29 +1,25 @@ -import Multicall from '@/utils/multicall' import { useWeb3React } from '@web3-react/core' import { BigNumber } from 'ethers' import { parseUnits } from 'ethers/lib/utils' import { useCallback, useEffect, useState } from 'react' import useBao from '../base/useBao' -import { useAccountLiquidity } from './useAccountLiquidity' import { exponentiate } from '@/utils/numberFormat' import Config from '@/bao/lib/config' import { Balance } from '@/bao/lib/types' import { useOraclePrice } from '@/hooks/lend/useOraclePrice' -import useContract from '@/hooks/base/useContract' -import type { Comptroller } from '@/typechain/Comptroller' +import { Comptroller__factory } from '@/typechain/factories' -const useHealthFactor = (marketName: string, borrowBalances: Balance[], supplyBalances: Balance[], borrowChange: BigNumber) => { +const useHealthFactor = (marketName: string, borrowBalances: Balance[], borrowChange: BigNumber) => { const [healthFactor, setHealthFactor] = useState() const bao = useBao() const { account, chainId, library } = useWeb3React() const market = Config.lendMarkets[marketName] - const accountLiquidity = useAccountLiquidity(marketName, borrowBalances, supplyBalances) const price = useOraclePrice(marketName) - const comptroller = useContract('Comptroller', market.comptroller) + const signerOrProvider = account ? library.getSigner() : library + const comptroller = Comptroller__factory.connect(market.comptroller, signerOrProvider) + const [collateralFactor, setCollateralFactor] = useState(null) const fetchHealthFactor = useCallback(async () => { - const collateralFactor = await comptroller.markets(market.marketAddresses[chainId]) - if (Object.keys(borrowBalances).length === 0) return setHealthFactor(BigNumber.from(0)) const collateralSummation = BigNumber.from(price) @@ -38,13 +34,22 @@ const useHealthFactor = (marketName: string, borrowBalances: Balance[], supplyBa } catch { setHealthFactor(BigNumber.from(0)) } - }, [market, bao, account, price, borrowChange]) + }, [market, bao, account, price, borrowChange, comptroller]) + + const fetchCollateralFactor = useCallback(async () => { + try { + const _collateralFactor = await comptroller.callStatic.markets(market.marketAddresses[chainId]) + setCollateralFactor(_collateralFactor) + } catch {} + }, [comptroller]) useEffect(() => { - if (!(market && accountLiquidity && bao && account && price)) return + if (!!comptroller && !collateralFactor) fetchCollateralFactor() + }, [comptroller]) - fetchHealthFactor() - }, [market, accountLiquidity, bao, account, price, fetchHealthFactor, borrowChange]) + useEffect(() => { + if (!!market && !!collateralFactor && !!borrowBalances && !!bao && !!account && !!price && !healthFactor) fetchHealthFactor() + }, [collateralFactor, borrowBalances, price, market, bao, account]) return healthFactor } diff --git a/src/hooks/lend/useSupplyRate.ts b/src/hooks/lend/useSupplyRate.ts index ea82112b..e84d43b4 100644 --- a/src/hooks/lend/useSupplyRate.ts +++ b/src/hooks/lend/useSupplyRate.ts @@ -28,7 +28,7 @@ export const useSupplyRate = (marketName: string): BigNumber => { ) const res = MultiCall.parseCallResults(await bao.multicall.call(multiCallContext)) - return res[address][1].values[0] + return res[address][0].values[0] }, { enabled, diff --git a/src/pages/lend/[market].tsx b/src/pages/lend/[market].tsx index 5def1cbb..276278bf 100644 --- a/src/pages/lend/[market].tsx +++ b/src/pages/lend/[market].tsx @@ -54,7 +54,6 @@ const Market: NextPage<{ const totalDebt = useTotalDebt(marketName) const [supplyVal, setSupplyVal] = useState('0') const [borrowVal, setBorrowVal] = useState('0') - const formattedOraclePrice = useMemo(() => oraclePrice && '$' + getDisplayBalance(oraclePrice, 18), [oraclePrice]) const formattedTotalCollateral = useMemo( () => totalCollateral && oraclePrice && '$' + getDisplayBalance(totalCollateral.mul(decimate(oraclePrice)), 18), @@ -160,13 +159,17 @@ const Market: NextPage<{
- + {borrowBalances && borrowBalances.length > 0 ? ( + + ) : ( + 'test' + )}
diff --git a/src/pages/lend/components/DebtCard.tsx b/src/pages/lend/components/DebtCard.tsx index 08a39fb1..82d85bd6 100644 --- a/src/pages/lend/components/DebtCard.tsx +++ b/src/pages/lend/components/DebtCard.tsx @@ -30,7 +30,7 @@ const DashboardCard: React.FC = ({ const bao = useBao() const { account, chainId } = useWeb3React() const asset = useActiveLendMarket(marketName) - const accountLiquidity = useAccountLiquidity(marketName, borrowBalances, supplyBalances) + const accountLiquidity = useAccountLiquidity(marketName, supplyBalances, borrowBalances) const change = mintVal && depositVal ? BigNumber.from(mintVal).sub(BigNumber.from(depositVal)) : BigNumber.from(0) const borrow = accountLiquidity ? accountLiquidity.usdBorrow : BigNumber.from(0) @@ -39,7 +39,7 @@ const DashboardCard: React.FC = ({ const newBorrowable = asset && decimate(borrowable).sub(BigNumber.from(parseUnits(formatUnits(change, 36 - 18)))) const borrowChange = borrow.add(exponentiate(change)) - const healthFactor = useHealthFactor(marketName, borrowBalances, supplyBalances, borrowChange) + const healthFactor = useHealthFactor(marketName, borrowBalances, borrowChange) const barPercentage = parseFloat( getDisplayBalance( @@ -61,7 +61,7 @@ const DashboardCard: React.FC = ({ return c } - return ( + return borrow.gt(BigNumber.from(0)) ? ( <>
@@ -133,6 +133,8 @@ const DashboardCard: React.FC = ({
+ ) : ( + <> ) } From 8e2ef6d4897d46440e171fe1ea7dad2e1277ab9e Mon Sep 17 00:00:00 2001 From: Haruxe Date: Tue, 8 Oct 2024 17:57:46 -0700 Subject: [PATCH 11/27] feat: styles, config --- public/images/tokens/USDe.png | Bin 1650 -> 6702 bytes src/bao/lib/config.ts | 4 ++++ src/bao/lib/types.ts | 2 ++ src/components/Header/Header.tsx | 1 + src/components/Nav/Nav.tsx | 2 +- src/pages/lend/components/MarketList.tsx | 14 +++++------ src/pages/lend/index.tsx | 28 +++++++++++++++------- src/pages/vaults/components/VaultList.tsx | 5 +--- 8 files changed, 36 insertions(+), 20 deletions(-) diff --git a/public/images/tokens/USDe.png b/public/images/tokens/USDe.png index e270a394266a3e0b28d5b9454327cdb4e3e82fe1..5d7175f661c513ddb5f68c35dc679ef6ba0d9a2e 100644 GIT binary patch literal 6702 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 diff --git a/src/bao/lib/config.ts b/src/bao/lib/config.ts index bbe782e1..73b3f1e0 100644 --- a/src/bao/lib/config.ts +++ b/src/bao/lib/config.ts @@ -810,6 +810,8 @@ export default { weETH: { id: 1, active: true, + name: 'weETH', + desc: 'Wrapped eETH', comptroller: '0xc2eA0F9856B58CF182Cc72B8FaEc51351479232e', oracle: '0xc7D8b6b170E0FCf4182fa29b47F35F48C402bF0F', marketAddresses: { @@ -869,6 +871,8 @@ export default { USDe: { id: 1, active: true, + name: 'USDe', + desc: 'Ethena USDe', comptroller: '0xc2eA0F9856B58CF182Cc72B8FaEc51351479232e', oracle: '0xc7D8b6b170E0FCf4182fa29b47F35F48C402bF0F', marketAddresses: { diff --git a/src/bao/lib/types.ts b/src/bao/lib/types.ts index 5f978ada..5217991c 100644 --- a/src/bao/lib/types.ts +++ b/src/bao/lib/types.ts @@ -235,6 +235,8 @@ export interface LendMarket { active: boolean comptroller: string oracle: string + name: string + desc: string marketAddresses: { [network: number]: string } diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 6a0a4eb2..7e05036b 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -107,6 +107,7 @@ const Header: FC = () => { SWAP EARN VEBAO + LEND
diff --git a/src/components/Nav/Nav.tsx b/src/components/Nav/Nav.tsx index 406f6f53..283d9eae 100644 --- a/src/components/Nav/Nav.tsx +++ b/src/components/Nav/Nav.tsx @@ -23,7 +23,7 @@ const Nav: FC = ({ href, exact }) => { ['1', 'SWAP', '/swap'], ['2', 'EARN', '/earn'], ['3', 'VEBAO', '/vebao'], - ['4', 'LEND', 'lend'], + ['4', 'LEND', '/lend'], ] return ( diff --git a/src/pages/lend/components/MarketList.tsx b/src/pages/lend/components/MarketList.tsx index 41031684..0fe79b11 100644 --- a/src/pages/lend/components/MarketList.tsx +++ b/src/pages/lend/components/MarketList.tsx @@ -29,18 +29,18 @@ export const MarketListItem: React.FC = ({ marketName }: Market return ( market && ( - - + +
- + ) } diff --git a/src/pages/vaults/components/VaultList.tsx b/src/pages/vaults/components/VaultList.tsx index c5dd4309..6b0cd239 100644 --- a/src/pages/vaults/components/VaultList.tsx +++ b/src/pages/vaults/components/VaultList.tsx @@ -60,10 +60,7 @@ export const VaultListItem: React.FC = ({ vaultName }: VaultList return ( synth && ( -
Total market supply
+ +
- - - + + + -
- - + +
Total market supplyYour position
Total market supplyYour Position
{totalMarketSupply ? totalMarketSupply : }{yourPosition ? yourPosition : }{totalMarketSupply ? totalMarketSupply : }{yourPosition ? yourPosition : }
+ {/* */} {asset.supply === true && ( <> - - + + )} {asset.borrow === true && ( <> - - + + )}
diff --git a/src/pages/lend/index.tsx b/src/pages/lend/index.tsx index ce7c9113..1b42a8b5 100644 --- a/src/pages/lend/index.tsx +++ b/src/pages/lend/index.tsx @@ -23,12 +23,7 @@ const Markets: React.FC = () => {
diff --git a/src/pages/vaults/components/DepositCard.tsx b/src/pages/vaults/components/DepositCard.tsx index 8ad017ca..49267dd4 100644 --- a/src/pages/vaults/components/DepositCard.tsx +++ b/src/pages/vaults/components/DepositCard.tsx @@ -91,7 +91,7 @@ export const DepositCard = ({ > From 95afb55e42e1601545c8604e562ed596788adf58 Mon Sep 17 00:00:00 2001 From: Daizze Date: Mon, 14 Oct 2024 01:22:33 +0200 Subject: [PATCH 14/27] Adding last modal pages + buttons --- src/bao/lib/config.ts | 2 +- src/components/Button/Button.tsx | 4 +- src/hooks/lend/useActiveLendMarket.ts | 2 + src/hooks/lend/useOraclePrices.ts | 56 +++++++++++++++ src/hooks/lend/useTotalCollateral.ts | 16 +++-- src/pages/lend/[market].tsx | 10 +-- src/pages/lend/components/AssetsCard.tsx | 6 +- .../lend/components/Buttons/BorrowButton.tsx | 63 ++++++++++++++++ .../lend/components/Buttons/RepayButton.tsx | 71 ++++++++++++++++++ .../lend/components/Buttons/SupplyButton.tsx | 8 ++- .../components/Buttons/WithdrawButton.tsx | 61 ++++++++++++++++ .../lend/components/Modals/BorrowModal.tsx | 63 +++++++++++++--- .../lend/components/Modals/RepayModal.tsx | 44 ++++++++++-- .../lend/components/Modals/SupplyModal.tsx | 12 +++- .../lend/components/Modals/WithdrawModal.tsx | 69 ++++++++++++++++-- src/pages/lend/components/SupplyList.tsx | 72 ++++++------------- 16 files changed, 468 insertions(+), 91 deletions(-) create mode 100644 src/hooks/lend/useOraclePrices.ts create mode 100644 src/pages/lend/components/Buttons/BorrowButton.tsx create mode 100644 src/pages/lend/components/Buttons/RepayButton.tsx create mode 100644 src/pages/lend/components/Buttons/WithdrawButton.tsx diff --git a/src/bao/lib/config.ts b/src/bao/lib/config.ts index 73b3f1e0..3c868494 100644 --- a/src/bao/lib/config.ts +++ b/src/bao/lib/config.ts @@ -848,7 +848,7 @@ export default { underlyingDecimals: 18, name: 'BaoUSD', icon: '/images/tokens/baoUSD.png', - supply: false, + supply: true, borrow: true, }, { diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 7b804b70..e89c5562 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -19,7 +19,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributes( Size[size], inline ? 'inline-block' : 'flex', fullWidth ? 'w-full' : '', - width ? 'w-[' + width + 'px]' : 'w-fit', + width ? width : 'w-fit', disabled ? 'cursor-not-allowed opacity-50' : '', `relative flex items-center justify-center overflow-hidden glassmorphic-card border border-baoWhite border-opacity-20 bg-baoWhite bg-opacity-5 px-4 py-2 font-bakbak text-lg text-baoWhite duration-300 hover:border-baoRed hover:bg-baoRed hover:bg-opacity-20`, diff --git a/src/hooks/lend/useActiveLendMarket.ts b/src/hooks/lend/useActiveLendMarket.ts index 8c165916..5fff9a34 100644 --- a/src/hooks/lend/useActiveLendMarket.ts +++ b/src/hooks/lend/useActiveLendMarket.ts @@ -14,6 +14,8 @@ export const useActiveLendMarket = (marketName: string): ActiveLendMarket => { async (marketName: string) => { const signerOrProvider = account ? library.getSigner() : library const lendMarket = Config.lendMarkets[marketName] + + if (!lendMarket) return false const marketAddress = lendMarket.marketAddresses[chainId] const underlyingAddress = lendMarket.underlyingAddresses[chainId] const marketContract = Ctoken__factory.connect(marketAddress, signerOrProvider) diff --git a/src/hooks/lend/useOraclePrices.ts b/src/hooks/lend/useOraclePrices.ts new file mode 100644 index 00000000..9829738e --- /dev/null +++ b/src/hooks/lend/useOraclePrices.ts @@ -0,0 +1,56 @@ +import useBao from '@/hooks/base/useBao' +import { useWeb3React } from '@web3-react/core' +import { useQuery } from '@tanstack/react-query' +import { providerKey } from '@/utils/index' +import { VaultOracle__factory } from '@/typechain/factories' +import Config from '@/bao/lib/config' +import { BigNumber } from 'ethers' +import { ContractCallContext, ContractCallResults, Multicall } from 'ethereum-multicall' +import { forEach } from 'lodash' + +export const useOraclePrices = (marketName: string): { [p: string]: BigNumber } => { + const bao = useBao() + const { account, library, chainId } = useWeb3React() + const signerOrProvider = account ? library.getSigner() : library + const enabled = !!bao && !!account && !!chainId + const { data: prices } = useQuery( + ['@/hooks/lend/useOraclePrices', providerKey(library, account, chainId), { enabled }], + async () => { + const multicall = new Multicall({ ethersProvider: library, tryAggregate: true }) + const multicallContext: ContractCallContext[] = [] + const oracle = VaultOracle__factory.connect(Config.lendMarkets[marketName].oracle, signerOrProvider) + + forEach(Config.lendMarkets[marketName].assets, asset => { + multicallContext.push({ + reference: asset.marketAddress[chainId], + contractAddress: oracle.address, + abi: VaultOracle__factory.abi, + calls: [ + { + reference: 'getUnderlyingPrice', + methodName: 'getUnderlyingPrice', + methodParameters: [asset.marketAddress[chainId]], + }, + ], + }) + }) + + const multicallResults: ContractCallResults = await multicall.call(multicallContext) + + return Object.keys(multicallResults.results).reduce( + (price: { [key: string]: BigNumber }, address: string) => ({ + ...price, + [address]: multicallResults.results[address].callsReturnContext.find(call => call.reference === 'getUnderlyingPrice') + ?.returnValues[0], + }), + {}, + ) + }, + + { + enabled, + }, + ) + + return prices +} diff --git a/src/hooks/lend/useTotalCollateral.ts b/src/hooks/lend/useTotalCollateral.ts index 0942b08a..6fd3f557 100644 --- a/src/hooks/lend/useTotalCollateral.ts +++ b/src/hooks/lend/useTotalCollateral.ts @@ -7,17 +7,20 @@ import { Ctoken__factory } from '@/typechain/factories' import MultiCall from '@/utils/multicall' import Config from '@/bao/lib/config' import { BigNumber } from 'ethers' +import { useOraclePrices } from './useOraclePrices' +import { decimate, getDisplayBalance } from '../../utils/numberFormat' export const useTotalCollateral = (marketName: string): BigNumber => { const bao = useBao() const { account, library, chainId } = useWeb3React() + const prices = useOraclePrices(marketName) - const enabled = !!bao && !!account && !!chainId + const enabled = !!bao && !!account && !!chainId && !!prices const { data: totalCollateral } = useQuery( ['@/hooks/lend/useTotalCollateral', providerKey(library, account, chainId), { enabled }], async () => { - const address = Config.lendMarkets[marketName].marketAddresses[chainId] - const contracts: Contract[] = [Ctoken__factory.connect(address, library)] + const addresses = Config.lendMarkets[marketName].assets.map(asset => asset.marketAddress[chainId]) + const contracts: Contract[] = addresses.map(address => Ctoken__factory.connect(address, library)) const res = MultiCall.parseCallResults( await bao.multicall.call( @@ -36,7 +39,12 @@ export const useTotalCollateral = (marketName: string): BigNumber => { ), ) - return res[address][1].values[0] + let totalCollateral = BigNumber.from(0) + Object.keys(res).map( + address => (totalCollateral = totalCollateral.add(decimate(res[address][1].values[0].mul(BigNumber.from(prices[address]))))), + ) + + return totalCollateral }, { enabled, diff --git a/src/pages/lend/[market].tsx b/src/pages/lend/[market].tsx index 276278bf..30b7818f 100644 --- a/src/pages/lend/[market].tsx +++ b/src/pages/lend/[market].tsx @@ -44,7 +44,6 @@ export async function getStaticProps({ params }: { params: any }) { const Market: NextPage<{ marketName: string }> = ({ marketName }) => { - const accountBalances = useAccountBalances(marketName) const borrowBalances = useBorrowBalances(marketName) const supplyBalances = useSupplyBalances(marketName) const totalSupplies = useTotalSupplies(marketName) @@ -56,7 +55,7 @@ const Market: NextPage<{ const [borrowVal, setBorrowVal] = useState('0') const formattedOraclePrice = useMemo(() => oraclePrice && '$' + getDisplayBalance(oraclePrice, 18), [oraclePrice]) const formattedTotalCollateral = useMemo( - () => totalCollateral && oraclePrice && '$' + getDisplayBalance(totalCollateral.mul(decimate(oraclePrice)), 18), + () => totalCollateral && oraclePrice && '$' + getDisplayBalance(totalCollateral), [totalCollateral, oraclePrice], ) const utilization = useMemo(() => getDisplayBalance(getUtilization()) + '%', [totalDebt, totalCollateral]) @@ -174,12 +173,7 @@ const Market: NextPage<{
- +
diff --git a/src/pages/lend/components/AssetsCard.tsx b/src/pages/lend/components/AssetsCard.tsx index 360dc5e1..f126abef 100644 --- a/src/pages/lend/components/AssetsCard.tsx +++ b/src/pages/lend/components/AssetsCard.tsx @@ -1,23 +1,19 @@ -import Card from '@/components/Card/Card' -import Typography from '@/components/Typography' import React from 'react' import SupplyList from '@/pages/lend/components/SupplyList' import { Balance, TotalSupply } from '@/bao/lib/types' export const AssetsCard = ({ - accountBalances, marketName, borrowBalances, totalSupplies, }: { - accountBalances: Balance[] marketName: string borrowBalances: Balance[] totalSupplies: TotalSupply[] }) => { return ( <> - + ) } diff --git a/src/pages/lend/components/Buttons/BorrowButton.tsx b/src/pages/lend/components/Buttons/BorrowButton.tsx new file mode 100644 index 00000000..74272d83 --- /dev/null +++ b/src/pages/lend/components/Buttons/BorrowButton.tsx @@ -0,0 +1,63 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { ActiveSupportedVault, Asset } from '@/bao/lib/types' +import Button from '@/components/Button' +import { PendingTransaction } from '@/components/Loader/Loader' +import useContract from '@/hooks/base/useContract' +import useTransactionHandler from '@/hooks/base/useTransactionHandler' +import { useApprovals } from '@/hooks/vaults/useApprovals' +import type { Erc20 } from '@/typechain/index' +import { getDisplayBalance } from '@/utils/numberFormat' +import { faExternalLink } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { BigNumber, ethers } from 'ethers' +import { useActiveLendMarket } from '@/hooks/lend/useActiveLendMarket' +import { useLendMarketApprovals } from '@/hooks/lend/useLendMarketApprovals' +import { useWeb3React } from '@web3-react/core' + +type BorrowButtonProps = { + asset: Asset + val: BigNumber + isDisabled: boolean + onHide?: () => void + marketName: string +} + +const BorrowButton = ({ asset, val, isDisabled, onHide, marketName }: BorrowButtonProps) => { + const activeLendMarket = useActiveLendMarket(marketName) + const { pendingTx, handleTx, txHash } = useTransactionHandler() + const { approvals } = useLendMarketApprovals(activeLendMarket) + const { chainId } = useWeb3React() + + const erc20 = useContract('Erc20', asset.underlyingAddress[chainId]) + + if (pendingTx) { + return ( + + + + ) + } else { + return ( + + ) + } +} + +export default BorrowButton diff --git a/src/pages/lend/components/Buttons/RepayButton.tsx b/src/pages/lend/components/Buttons/RepayButton.tsx new file mode 100644 index 00000000..f4dcbf2b --- /dev/null +++ b/src/pages/lend/components/Buttons/RepayButton.tsx @@ -0,0 +1,71 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { ActiveSupportedVault, Asset } from '@/bao/lib/types' +import Button from '@/components/Button' +import { PendingTransaction } from '@/components/Loader/Loader' +import useContract from '@/hooks/base/useContract' +import useTransactionHandler from '@/hooks/base/useTransactionHandler' +import { useApprovals } from '@/hooks/vaults/useApprovals' +import type { Erc20 } from '@/typechain/index' +import { getDisplayBalance } from '@/utils/numberFormat' +import { faExternalLink } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { BigNumber, ethers } from 'ethers' +import { useActiveLendMarket } from '@/hooks/lend/useActiveLendMarket' +import { useLendMarketApprovals } from '@/hooks/lend/useLendMarketApprovals' +import { useWeb3React } from '@web3-react/core' + +type RepayButtonProps = { + asset: Asset + val: BigNumber + isDisabled: boolean + onHide?: () => void + marketName: string +} + +const RepayButton = ({ asset, val, isDisabled, onHide, marketName }: RepayButtonProps) => { + const activeLendMarket = useActiveLendMarket(marketName) + const { pendingTx, handleTx, txHash } = useTransactionHandler() + const { approvals } = useLendMarketApprovals(activeLendMarket) + const { chainId } = useWeb3React() + + const erc20 = useContract('Erc20', asset.underlyingAddress[chainId]) + + if (pendingTx) { + return ( + + + + ) + } else { + return approvals && approvals[asset.underlyingAddress[chainId]] && approvals[asset.underlyingAddress[chainId]].gt(0) ? ( + + ) : ( + + ) + } +} + +export default RepayButton diff --git a/src/pages/lend/components/Buttons/SupplyButton.tsx b/src/pages/lend/components/Buttons/SupplyButton.tsx index f268d010..83b5fa46 100644 --- a/src/pages/lend/components/Buttons/SupplyButton.tsx +++ b/src/pages/lend/components/Buttons/SupplyButton.tsx @@ -40,14 +40,16 @@ const SupplyButton = ({ asset, val, isDisabled, onHide, marketName }: SupplyButt ) } else { - return typeof approvals != 'undefined' && approvals[asset.underlyingAddress[chainId]].gt(0) ? ( + return typeof approvals != 'undefined' && + approvals[asset.underlyingAddress[chainId]] && + approvals[asset.underlyingAddress[chainId]].gt(0) ? ( + + ) + } else { + return ( + + ) + } +} + +export default WithdrawButton diff --git a/src/pages/lend/components/Modals/BorrowModal.tsx b/src/pages/lend/components/Modals/BorrowModal.tsx index b96f5d51..6e545624 100644 --- a/src/pages/lend/components/Modals/BorrowModal.tsx +++ b/src/pages/lend/components/Modals/BorrowModal.tsx @@ -3,37 +3,84 @@ import { Asset } from '@/bao/lib/types' import Modal from '@/components/Modal' import Typography from '@/components/Typography' import Image from 'next/future/image' -import { useCallback } from 'react' +import React, { useCallback, useState } from 'react' +import Input from '@/components/Input' +import WithdrawButton from '@/pages/lend/components/Buttons/WithdrawButton' +import { parseUnits } from 'ethers/lib/utils' +import { BigNumber } from 'ethers' +import BorrowButton from '@/pages/lend/components/Buttons/BorrowButton' export type BorrowModalProps = { asset: Asset show: boolean onHide: () => void + marketName: string } -const BorrowModal = ({ asset, show, onHide }: BorrowModalProps) => { +const BorrowModal = ({ asset, show, onHide, marketName }: BorrowModalProps) => { + const [val, setVal] = useState('0') const operation = 'Borrow' const hideModal = useCallback(() => { onHide() }, [onHide]) + const handleChange = useCallback( + (e: React.FormEvent) => { + setVal(e.currentTarget.value) + }, + [setVal], + ) + + const handleSelectMax = useCallback(() => { + return setVal('0') + }, []) + return (
- Borrow + Borrow {asset.name} + {asset.name}
- - {asset.name} - {asset.name} - +
+
+
+ + Available: + + + {'0.00'} + {asset.name} + {asset.name} + +
+
+ + + +
- + + +
) } diff --git a/src/pages/lend/components/Modals/RepayModal.tsx b/src/pages/lend/components/Modals/RepayModal.tsx index 4c28814d..e79a3f50 100644 --- a/src/pages/lend/components/Modals/RepayModal.tsx +++ b/src/pages/lend/components/Modals/RepayModal.tsx @@ -3,7 +3,11 @@ import { Asset } from '@/bao/lib/types' import Modal from '@/components/Modal' import Typography from '@/components/Typography' import Image from 'next/future/image' -import React, { useCallback } from 'react' +import React, { useCallback, useState } from 'react' +import Input from '@/components/Input' +import { parseUnits } from 'ethers/lib/utils' +import { BigNumber } from 'ethers' +import RepayButton from '@/pages/lend/components/Buttons/RepayButton' export type RepayModalProps = { asset: Asset @@ -15,17 +19,30 @@ export type RepayModalProps = { const RepayModal = ({ asset, show, onHide, marketName }: RepayModalProps) => { const operation = 'Repay' + const [val, setVal] = useState('0') + const hideModal = useCallback(() => { onHide() }, [onHide]) + const handleChange = useCallback( + (e: React.FormEvent) => { + setVal(e.currentTarget.value) + }, + [setVal], + ) + + const handleSelectMax = useCallback(() => { + return setVal('0') + }, []) + return ( <>
- Repay + Repay {asset.name} {asset.name}
@@ -39,14 +56,33 @@ const RepayModal = ({ asset, show, onHide, marketName }: RepayModalProps) => { Available: + {'0.00'} {asset.name} - {asset.name} + {asset.name} + + + - + + +
diff --git a/src/pages/lend/components/Modals/SupplyModal.tsx b/src/pages/lend/components/Modals/SupplyModal.tsx index 3c583414..dcaa9bcc 100644 --- a/src/pages/lend/components/Modals/SupplyModal.tsx +++ b/src/pages/lend/components/Modals/SupplyModal.tsx @@ -3,7 +3,7 @@ import { ActiveSupportedVault, Asset, Balance } from '@/bao/lib/types' import Modal from '@/components/Modal' import { BigNumber } from 'ethers' import { parseUnits } from 'ethers/lib/utils' -import React, { useCallback, useState } from 'react' +import React, { useCallback, useEffect, useState } from 'react' import Input from '@/components/Input' import Typography from '@/components/Typography' import SupplyButton from '@/pages/lend/components/Buttons/SupplyButton' @@ -23,6 +23,7 @@ export type SupplyModalProps = { const SupplyModal = ({ asset, show, onHide, marketName, fullBalance }: SupplyModalProps) => { const [val, setVal] = useState('0') const operation = 'Supply' + const [formattedBalance, setFormattedBalance] = useState('0.00') const handleChange = useCallback( (e: React.FormEvent) => { @@ -32,6 +33,7 @@ const SupplyModal = ({ asset, show, onHide, marketName, fullBalance }: SupplyMod ) const handleSelectMax = useCallback(() => { + if (!fullBalance) return setVal('0') setVal(fullBalance.balance.toString()) }, [fullBalance, setVal]) @@ -39,6 +41,11 @@ const SupplyModal = ({ asset, show, onHide, marketName, fullBalance }: SupplyMod onHide() }, [onHide]) + useEffect(() => { + if (!fullBalance) return + setFormattedBalance(getDisplayBalance(fullBalance.balance, fullBalance.decimals)) + }, [fullBalance]) + return ( {}}> @@ -57,7 +64,8 @@ const SupplyModal = ({ asset, show, onHide, marketName, fullBalance }: SupplyMod Balance: - {fullBalance && getDisplayBalance(fullBalance.balance, fullBalance.decimals)}{' '} + {formattedBalance} + {asset.name} {asset.name} diff --git a/src/pages/lend/components/Modals/WithdrawModal.tsx b/src/pages/lend/components/Modals/WithdrawModal.tsx index 3e173281..0e625ddd 100644 --- a/src/pages/lend/components/Modals/WithdrawModal.tsx +++ b/src/pages/lend/components/Modals/WithdrawModal.tsx @@ -1,9 +1,17 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import { Asset } from '@/bao/lib/types' +import { Asset, Balance } from '@/bao/lib/types' import Modal from '@/components/Modal' import Typography from '@/components/Typography' import Image from 'next/future/image' -import React, { useCallback } from 'react' +import React, { useCallback, useMemo, useState } from 'react' +import Input from '@/components/Input' +import { decimate } from '@/utils/numberFormat' +import { BigNumber } from 'ethers' +import { useWeb3React } from '@web3-react/core' +import { useExchangeRates } from '@/hooks/lend/useExchangeRate' +import { parseUnits } from 'ethers/lib/utils' +import WithdrawButton from '@/pages/lend/components/Buttons/WithdrawButton' +import { useSupplyBalances } from '@/hooks/lend/useSupplyBalances' export type WithdrawModalProps = { asset: Asset @@ -13,7 +21,39 @@ export type WithdrawModalProps = { } const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) => { + const { chainId } = useWeb3React() + const supplyBalances = useSupplyBalances(marketName) + const { exchangeRates } = useExchangeRates(marketName) + const [val, setVal] = useState('0') const operation = 'Withdraw' + const [formattedBalance, setFormattedBalance] = useState('0.00') + + const handleChange = useCallback( + (e: React.FormEvent) => { + setVal(e.currentTarget.value) + }, + [setVal], + ) + + const supplied = useMemo( + () => + supplyBalances && + supplyBalances.find(balance => balance.address.toLowerCase() === asset.marketAddress[chainId].toLowerCase()) && + exchangeRates && + exchangeRates[asset.marketAddress[chainId]] + ? decimate( + supplyBalances + .find(balance => balance.address.toLowerCase() === asset.marketAddress[chainId].toLowerCase()) + .balance.mul(exchangeRates[asset.marketAddress[chainId]]), + ) + : BigNumber.from(0), + [supplyBalances, exchangeRates, asset.marketAddress[chainId]], + ) + + const handleSelectMax = useCallback(() => { + if (!supplied) return setVal('0') + setVal(supplied.toString()) + }, [supplied]) const hideModal = useCallback(() => { onHide() @@ -25,7 +65,7 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps)
- Withdraw + Withdraw {asset.name} {asset.name}
@@ -39,14 +79,33 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) Withdrawable: + {formattedBalance} {asset.name} - {asset.name} + {asset.name} + + + - + + +
diff --git a/src/pages/lend/components/SupplyList.tsx b/src/pages/lend/components/SupplyList.tsx index b2512864..57882f44 100644 --- a/src/pages/lend/components/SupplyList.tsx +++ b/src/pages/lend/components/SupplyList.tsx @@ -1,7 +1,7 @@ import Loader from '@/components/Loader' import Typography from '@/components/Typography' import Image from 'next/future/image' -import React, { useEffect, useMemo, useState } from 'react' +import React, { useMemo, useState } from 'react' import Config from '@/bao/lib/config' import { getDisplayBalance } from '@/utils/numberFormat' import { Asset, Balance, TotalSupply } from '@/bao/lib/types' @@ -11,15 +11,9 @@ import SupplyModal from '@/pages/lend/components/Modals/SupplyModal' import BorrowModal from '@/pages/lend/components/Modals/BorrowModal' import WithdrawModal from '@/pages/lend/components/Modals/WithdrawModal' import RepayModal from '@/pages/lend/components/Modals/RepayModal' -import { BigNumber } from 'ethers' import Tooltipped from '@/components/Tooltipped' -import { StatBlock } from '@/components/Stats' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faAngleUp, faCircleInfo } from '@fortawesome/free-solid-svg-icons' -import { Transition } from '@headlessui/react' -import Card from '@/components/Card' -export const SupplyList: React.FC = ({ accountBalances, marketName, borrowBalances, totalSupplies }) => { +export const SupplyList: React.FC = ({ marketName, borrowBalances, totalSupplies }) => { const assets = Config.lendMarkets[marketName].assets return ( @@ -34,7 +28,6 @@ export const SupplyList: React.FC = ({ accountBalances, marketN = ({ accountBalances, marketN ) } -const SupplyListItem: React.FC = ({ asset, accountBalances, marketName, borrowBalances, totalSupplies }) => { +const SupplyListItem: React.FC = ({ asset, marketName, borrowBalances, totalSupplies }) => { const { chainId } = useWeb3React() const [balance, setBalance] = useState(null) const [showSupplyModal, setShowSupplyModal] = useState(false) const [showWithdrawModal, setShowWithdrawModal] = useState(false) const [showBorrowModal, setShowBorrowModal] = useState(false) const [showRepayModal, setShowRepayModal] = useState(false) - const [showInfo, setShowInfo] = useState(false) - const [selectedOption, setSelectedOption] = useState('ETH') const yourPosition = useMemo( () => @@ -73,20 +64,6 @@ const SupplyListItem: React.FC = ({ asset, accountBalances, [totalSupplies, asset], ) - /* - function fetchBalance(asset: Asset) { - if (accountBalances !== null && accountBalances !== undefined) - return accountBalances.find(({ address }) => address === asset.underlyingAddress[chainId]) - } - - useEffect(() => { - const balance = fetchBalance(asset) - if (balance !== null && balance !== undefined) { - setBalance(balance) - setFormattedBalance(getDisplayBalance(balance.balance, balance.decimals)) - } - }, [accountBalances]) - */ return ( <>
@@ -94,7 +71,7 @@ const SupplyListItem: React.FC = ({ asset, accountBalances, content={false !== !asset.active ? 'Inactive' : 'Active'} key={asset.name} placement='top' - className='rounded-full bg-baoRed ' + className='rounded-full bg-baoRed w-[20%]' >
= ({ asset, accountBalances, 'text-baoWhite flex overflow-hidden rounded-2xl border border-baoWhite/20 bg-baoBlack shadow-lg shadow-baoBlack ring-1 ring-black ring-opacity-5 focus:outline-none select-none border-baoBlack px-2 py-3 text-sm' } > -
+
{asset.name} @@ -114,40 +91,39 @@ const SupplyListItem: React.FC = ({ asset, accountBalances,
- +
- + - - + +
Total market supply Your Position
{totalMarketSupply ? totalMarketSupply : }{yourPosition ? yourPosition : }{totalMarketSupply ? totalMarketSupply : }{yourPosition ? yourPosition : }
-
- {/* */} +
{asset.supply === true && ( <> - - + + )} {asset.borrow === true && ( <> - - + + )}
@@ -165,7 +141,7 @@ const SupplyListItem: React.FC = ({ asset, accountBalances, onHide={() => setShowWithdrawModal(!showWithdrawModal)} marketName={marketName} /> - setShowBorrowModal(!showBorrowModal)} /> + setShowBorrowModal(!showBorrowModal)} marketName={marketName} /> setShowRepayModal(!showRepayModal)} marketName={marketName} />
@@ -176,14 +152,12 @@ export default SupplyList type SupplyListItemProps = { asset: Asset - accountBalances: Balance[] marketName: string borrowBalances: Balance[] totalSupplies: TotalSupply[] } type SupplyListProps = { - accountBalances: Balance[] marketName: string borrowBalances: Balance[] totalSupplies: TotalSupply[] From 24d8f68ce411c516b01838c93df6197dd703f7e5 Mon Sep 17 00:00:00 2001 From: Daizze Date: Tue, 15 Oct 2024 00:53:20 +0200 Subject: [PATCH 15/27] First change withdrawle amount --- .../lend/components/Modals/WithdrawModal.tsx | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/pages/lend/components/Modals/WithdrawModal.tsx b/src/pages/lend/components/Modals/WithdrawModal.tsx index 0e625ddd..a670b0a6 100644 --- a/src/pages/lend/components/Modals/WithdrawModal.tsx +++ b/src/pages/lend/components/Modals/WithdrawModal.tsx @@ -5,13 +5,15 @@ import Typography from '@/components/Typography' import Image from 'next/future/image' import React, { useCallback, useMemo, useState } from 'react' import Input from '@/components/Input' -import { decimate } from '@/utils/numberFormat' +import { decimate, getDisplayBalance } from '@/utils/numberFormat' import { BigNumber } from 'ethers' import { useWeb3React } from '@web3-react/core' import { useExchangeRates } from '@/hooks/lend/useExchangeRate' import { parseUnits } from 'ethers/lib/utils' import WithdrawButton from '@/pages/lend/components/Buttons/WithdrawButton' import { useSupplyBalances } from '@/hooks/lend/useSupplyBalances' +import { useComptrollerData } from '@/hooks/lend/useComptrollerData' +import Loader from '@/components/Loader' export type WithdrawModalProps = { asset: Asset @@ -26,7 +28,7 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) const { exchangeRates } = useExchangeRates(marketName) const [val, setVal] = useState('0') const operation = 'Withdraw' - const [formattedBalance, setFormattedBalance] = useState('0.00') + const comptrollerData = useComptrollerData(marketName) const handleChange = useCallback( (e: React.FormEvent) => { @@ -35,25 +37,20 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) [setVal], ) - const supplied = useMemo( + const formattedSupplied = useMemo( () => supplyBalances && - supplyBalances.find(balance => balance.address.toLowerCase() === asset.marketAddress[chainId].toLowerCase()) && - exchangeRates && - exchangeRates[asset.marketAddress[chainId]] - ? decimate( - supplyBalances - .find(balance => balance.address.toLowerCase() === asset.marketAddress[chainId].toLowerCase()) - .balance.mul(exchangeRates[asset.marketAddress[chainId]]), - ) - : BigNumber.from(0), - [supplyBalances, exchangeRates, asset.marketAddress[chainId]], + supplyBalances.find(supply => supply.address === asset.underlyingAddress[chainId]) && + getDisplayBalance( + supplyBalances.find(supply => supply.address === asset.underlyingAddress[chainId]).balance, + asset.underlyingDecimals, + ), + [supplyBalances, asset], ) const handleSelectMax = useCallback(() => { - if (!supplied) return setVal('0') - setVal(supplied.toString()) - }, [supplied]) + formattedSupplied && setVal(formattedSupplied.toString()) + }, [formattedSupplied]) const hideModal = useCallback(() => { onHide() @@ -79,7 +76,7 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) Withdrawable: - {formattedBalance} + {formattedSupplied ? formattedSupplied : } {asset.name} {asset.name} @@ -90,7 +87,7 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) onSelectMax={handleSelectMax} onChange={handleChange} value={val} - max={supplied && supplied.toString()} + max={formattedSupplied && formattedSupplied.toString()} symbol={asset.name} className='h-12 min-w-[150px] z-20 w-full bg-baoBlack lg:h-auto' /> From 1bae45344def5ee3dba320be93ab31d4a0d08018 Mon Sep 17 00:00:00 2001 From: Daizze Date: Sat, 19 Oct 2024 01:26:15 +0200 Subject: [PATCH 16/27] Updating config and adding approval and fixng supply logic to supply button --- src/bao/lib/config.ts | 119 ++++++++++++++++-- src/hooks/lend/useActiveLendMarket.ts | 53 ++++---- src/hooks/lend/useComptrollerData.ts | 98 +++++++++++++++ src/hooks/lend/useOraclePrices.ts | 28 +++++ src/pages/lend/[market].tsx | 14 +-- .../lend/components/Buttons/BorrowButton.tsx | 2 +- .../lend/components/Buttons/RepayButton.tsx | 2 +- .../lend/components/Buttons/SupplyButton.tsx | 5 +- .../components/Buttons/WithdrawButton.tsx | 2 +- src/pages/lend/components/DebtCard.tsx | 2 +- .../lend/components/Modals/BorrowModal.tsx | 1 - .../lend/components/Modals/SupplyModal.tsx | 12 +- .../lend/components/Modals/WithdrawModal.tsx | 15 +-- src/pages/lend/components/SupplyList.tsx | 43 ++++--- 14 files changed, 304 insertions(+), 92 deletions(-) create mode 100644 src/hooks/lend/useComptrollerData.ts diff --git a/src/bao/lib/config.ts b/src/bao/lib/config.ts index 3c868494..5e14da42 100644 --- a/src/bao/lib/config.ts +++ b/src/bao/lib/config.ts @@ -812,7 +812,7 @@ export default { active: true, name: 'weETH', desc: 'Wrapped eETH', - comptroller: '0xc2eA0F9856B58CF182Cc72B8FaEc51351479232e', + comptroller: '0xf55044bb140DD3Eeb7372bd722C323c87d8AE798', oracle: '0xc7D8b6b170E0FCf4182fa29b47F35F48C402bF0F', marketAddresses: { 1: '0x353A07b25c84a522356aF2D9a7c0d7FF481733e9', @@ -863,9 +863,54 @@ export default { underlyingDecimals: 18, name: 'BaoETH', icon: '/images/tokens/baoETH.png', - supply: false, + supply: true, borrow: true, }, + { + id: 4, + active: true, + marketAddress: { + 1: '0x357FE927DC3Ee1be7ba1423bc5485BC8CD066040', + }, + underlyingAddress: { + 1: '0xc69Ad9baB1dEE23F4605a82b3354F8E40d1E5966', + }, + underlyingDecimals: 18, + name: 'PTweETHJUN', + icon: '/images/tokens/weETH.png', + supply: true, + borrow: false, + }, + { + id: 5, + active: true, + marketAddress: { + 1: '0x0930B04032E52daBC2324533c015bc27Ad5B35B7', + }, + underlyingAddress: { + 1: '0x1c085195437738d73d75DC64bC5A3E098b7f93b1', + }, + underlyingDecimals: 18, + name: 'PTweETHSEP', + icon: '/images/tokens/weETH.png', + supply: true, + borrow: false, + }, + { + id: 6, + active: true, + marketAddress: { + 1: '0x672bf1cdE0F352296fA759BefF392997C809cd8a', + }, + underlyingAddress: { + 1: '0x6ee2b5E19ECBa773a352E5B21415Dc419A700d1d', + }, + underlyingDecimals: 18, + name: 'PTweETHDEC', + icon: '/images/tokens/weETH.png', + supply: true, + borrow: false, + }, ], }, USDe: { @@ -873,32 +918,38 @@ export default { active: true, name: 'USDe', desc: 'Ethena USDe', - comptroller: '0xc2eA0F9856B58CF182Cc72B8FaEc51351479232e', - oracle: '0xc7D8b6b170E0FCf4182fa29b47F35F48C402bF0F', + comptroller: '0x46396230c61776A384c1c00c04A9784c4a2F5d8F', + oracle: '0xba2fdeb1d483acbdb196b39473e7af90268afa67', marketAddresses: { - 1: '', + 1: '0x680358d70b34a00f2d661bb4f95E1C14E5Dae93F', }, underlyingAddresses: { - 1: '', + 1: '0x4c9EDD5852cd905f086C759E8383e09bff1E68B3', }, assets: [ { id: 1, active: true, + marketAddress: { + 1: '0x680358d70b34a00f2d661bb4f95E1C14E5Dae93F', + }, underlyingAddress: { - 1: '0x353A07b25c84a522356aF2D9a7c0d7FF481733e9', + 1: '0x4c9EDD5852cd905f086C759E8383e09bff1E68B3', }, underlyingDecimals: 18, name: 'USDe', icon: '/images/tokens/USDe.png', supply: true, - borrow: true, + borrow: false, }, { id: 2, active: true, + marketAddress: { + 1: '0xe606AE6d6394a977a8B0926557BF9D291051f989', + }, underlyingAddress: { - 1: '0x085c35278fDC840b9Ca74AAB226bA8E2f95C446F', + 1: '0x7945b0A6674b175695e5d1D08aE1e6F13744Abb0', }, underlyingDecimals: 18, name: 'BaoUSD', @@ -909,8 +960,11 @@ export default { { id: 3, active: true, + marketAddress: { + 1: '0xDe10BEd5236B786cAA18Ca39FFa5de1b904a8a94', + }, underlyingAddress: { - 1: '0xdC39e6365AA75D762729513004c956D1475bED20', + 1: '0xf4edfad26EE0D23B69CA93112eccE52704E0006f', }, underlyingDecimals: 18, name: 'BaoETH', @@ -918,6 +972,51 @@ export default { supply: true, borrow: true, }, + { + id: 4, + active: true, + marketAddress: { + 1: '0xD011778057AA740BB3703Ad4d78b3c79a1aED1cb', + }, + underlyingAddress: { + 1: '0x9D39A5DE30e57443BfF2A8307A4256c8797A3497', + }, + underlyingDecimals: 18, + name: 'sUSDe', + icon: '/images/tokens/sUSDe.png', + supply: true, + borrow: false, + }, + { + id: 5, + active: true, + marketAddress: { + 1: '0x3d881b3C7B690f05E672cD0F8fCbC0aE3A7292CF', + }, + underlyingAddress: { + 1: '0xa0021EF8970104c2d008F38D92f115ad56a9B8e1', + }, + underlyingDecimals: 18, + name: 'PTUSDeJUL', + icon: '/images/tokens/USDe.png', + supply: true, + borrow: false, + }, + { + id: 6, + active: true, + marketAddress: { + 1: '0x4cd395CB5edF33b44E73257ee614413f95d1a6cD', + }, + underlyingAddress: { + 1: '0x6c9f097e044506712B58EAC670c9a5fd4BCceF13', + }, + underlyingDecimals: 18, + name: 'PTsUSDeSEP', + icon: '/images/tokens/sUSDe.png', + supply: true, + borrow: false, + }, ], }, }, diff --git a/src/hooks/lend/useActiveLendMarket.ts b/src/hooks/lend/useActiveLendMarket.ts index 5fff9a34..636a993d 100644 --- a/src/hooks/lend/useActiveLendMarket.ts +++ b/src/hooks/lend/useActiveLendMarket.ts @@ -1,42 +1,39 @@ -import Config from '@/bao/lib/config' import useTransactionProvider from '@/hooks/base/useTransactionProvider' import { Ctoken__factory, Erc20__factory } from '@/typechain/factories' import { useWeb3React } from '@web3-react/core' -import { ActiveLendMarket } from '@/bao/lib/types' +import { ActiveLendMarket, Asset } from '@/bao/lib/types' import { useCallback, useEffect, useState } from 'react' -export const useActiveLendMarket = (marketName: string): ActiveLendMarket => { +export const useActiveLendMarket = (asset: Asset): ActiveLendMarket => { const { library, account, chainId } = useWeb3React() const { transactions } = useTransactionProvider() const [lendMarket, setLendMarket] = useState(null) - const fetchLendMarket = useCallback( - async (marketName: string) => { - const signerOrProvider = account ? library.getSigner() : library - const lendMarket = Config.lendMarkets[marketName] - - if (!lendMarket) return false - const marketAddress = lendMarket.marketAddresses[chainId] - const underlyingAddress = lendMarket.underlyingAddresses[chainId] - const marketContract = Ctoken__factory.connect(marketAddress, signerOrProvider) - const underlyingContract = Erc20__factory.connect(underlyingAddress, signerOrProvider) - - const newActiveLendMarket: ActiveLendMarket = { - marketAddress: marketAddress, - marketContract: marketContract, - underlyingAddress: underlyingAddress, - underlyingContract: underlyingContract, - } - - setLendMarket(newActiveLendMarket) - }, - [chainId, library, account], - ) + const fetchLendMarket = useCallback(async () => { + if (!asset || !library || !chainId) return + + const signerOrProvider = account ? library.getSigner() : library + + const marketAddress = asset.marketAddress?.[chainId] + const underlyingAddress = asset.underlyingAddress?.[chainId] + + if (!marketAddress || !underlyingAddress) { + return + } + const marketContract = Ctoken__factory.connect(marketAddress, signerOrProvider) + const underlyingContract = Erc20__factory.connect(underlyingAddress, signerOrProvider) + + setLendMarket({ + marketAddress, + marketContract, + underlyingAddress, + underlyingContract, + }) + }, [asset, library, account, chainId]) useEffect(() => { - if (!library || !chainId) return - fetchLendMarket(marketName) - }, [fetchLendMarket, library, account, chainId, transactions]) + fetchLendMarket() + }, [fetchLendMarket]) return lendMarket } diff --git a/src/hooks/lend/useComptrollerData.ts b/src/hooks/lend/useComptrollerData.ts new file mode 100644 index 00000000..b8d130d0 --- /dev/null +++ b/src/hooks/lend/useComptrollerData.ts @@ -0,0 +1,98 @@ +import useBao from '@/hooks/base/useBao' +import { useWeb3React } from '@web3-react/core' +import { useQuery } from '@tanstack/react-query' +import { providerKey } from '@/utils/index' +import { Comptroller__factory } from '@/typechain/factories' +import Config from '@/bao/lib/config' +import { BigNumber } from 'ethers' +import { ContractCallContext, ContractCallResults, Multicall } from 'ethereum-multicall' +import { forEach } from 'lodash' + +export const useComptrollerData = (marketName: string): ComptrollerData[] => { + const bao = useBao() + const { account, library, chainId } = useWeb3React() + const signerOrProvider = account ? library.getSigner() : library + const enabled = !!bao && !!account && !!chainId + const { data: comptrollerData } = useQuery( + ['@/hooks/lend/useComptrollerData', providerKey(library, account, chainId), { enabled }], + async () => { + /* + const multicall = new Multicall({ ethersProvider: library, tryAggregate: true }) + const multicallContext: ContractCallContext[] = [] + const comptroller = Comptroller__factory.connect(Config.lendMarkets[marketName].comptroller, signerOrProvider) + + forEach(Config.lendMarkets[marketName].assets, asset => { + multicallContext.push({ + reference: asset.marketAddress[chainId], + contractAddress: comptroller.address, + abi: Comptroller__factory.abi, + calls: [ + { + reference: 'markets', + methodName: 'markets', + methodParameters: [asset.marketAddress[chainId]], + }, + { + reference: 'compBorrowState', + methodName: 'compBorrowState', + methodParameters: [asset.marketAddress[chainId]], + }, + { + reference: 'borrowRestricted', + methodName: 'borrowRestricted', + methodParameters: [asset.marketAddress[chainId]], + }, + ], + }) + }) + + const multicallResults: ContractCallResults = await multicall.call(multicallContext) + + return Object.values(multicallResults.results).map(data => { + return { + address: data.originalContractCallContext.reference, + collateralFactor: data.callsReturnContext.find(call => call.reference === 'markets')?.returnValues[1], + imfFactor: data.callsReturnContext.find(call => call.reference === 'markets')?.returnValues[2], + } + }) + + */ + + if (!enabled) return null + + const multicall = new Multicall({ ethersProvider: library, tryAggregate: true }) + const comptroller = Comptroller__factory.connect(Config.lendMarkets[marketName].comptroller, signerOrProvider) + + const multicallContext = Config.lendMarkets[marketName].assets.map(asset => ({ + reference: asset.marketAddress[chainId], + contractAddress: comptroller.address, + abi: Comptroller__factory.abi, + calls: [ + { reference: 'markets', methodName: 'markets', methodParameters: [asset.marketAddress[chainId]] }, + { reference: 'compBorrowState', methodName: 'compBorrowState', methodParameters: [asset.marketAddress[chainId]] }, + { reference: 'borrowRestricted', methodName: 'borrowRestricted', methodParameters: [asset.marketAddress[chainId]] }, + ], + })) + + const multicallResults = await multicall.call(multicallContext) + + return Object.entries(multicallResults.results).map(([address, data]) => ({ + address, + collateralFactor: data.callsReturnContext.find(call => call.reference === 'markets')?.returnValues[1], + imfFactor: data.callsReturnContext.find(call => call.reference === 'markets')?.returnValues[2], + })) + }, + + { + enabled, + }, + ) + + return comptrollerData +} + +type ComptrollerData = { + address: string + collateralFactor: BigNumber + imfFactor: BigNumber +} diff --git a/src/hooks/lend/useOraclePrices.ts b/src/hooks/lend/useOraclePrices.ts index 9829738e..ad74aab0 100644 --- a/src/hooks/lend/useOraclePrices.ts +++ b/src/hooks/lend/useOraclePrices.ts @@ -16,6 +16,7 @@ export const useOraclePrices = (marketName: string): { [p: string]: BigNumber } const { data: prices } = useQuery( ['@/hooks/lend/useOraclePrices', providerKey(library, account, chainId), { enabled }], async () => { + /* const multicall = new Multicall({ ethersProvider: library, tryAggregate: true }) const multicallContext: ContractCallContext[] = [] const oracle = VaultOracle__factory.connect(Config.lendMarkets[marketName].oracle, signerOrProvider) @@ -45,6 +46,33 @@ export const useOraclePrices = (marketName: string): { [p: string]: BigNumber } }), {}, ) + + */ + if (!enabled) return null + + const oracle = VaultOracle__factory.connect(Config.lendMarkets[marketName].oracle, signerOrProvider) + const multicall = new Multicall({ ethersProvider: library, tryAggregate: true }) + const multicallContext = Config.lendMarkets[marketName].assets.map(asset => ({ + reference: asset.marketAddress[chainId], + contractAddress: oracle.address, + abi: VaultOracle__factory.abi, + calls: [ + { + reference: 'getUnderlyingPrice', + methodName: 'getUnderlyingPrice', + methodParameters: [asset.marketAddress[chainId]], + }, + ], + })) + + const multicallResults = await multicall.call(multicallContext) + + return Object.fromEntries( + Object.entries(multicallResults.results).map(([address, result]) => [ + address, + result.callsReturnContext.find(call => call.reference === 'getUnderlyingPrice')?.returnValues[0], + ]), + ) }, { diff --git a/src/pages/lend/[market].tsx b/src/pages/lend/[market].tsx index 30b7818f..b3b38a13 100644 --- a/src/pages/lend/[market].tsx +++ b/src/pages/lend/[market].tsx @@ -157,19 +157,7 @@ const Market: NextPage<{
-
- {borrowBalances && borrowBalances.length > 0 ? ( - - ) : ( - 'test' - )} -
+
diff --git a/src/pages/lend/components/Buttons/BorrowButton.tsx b/src/pages/lend/components/Buttons/BorrowButton.tsx index 74272d83..0a4f26ac 100644 --- a/src/pages/lend/components/Buttons/BorrowButton.tsx +++ b/src/pages/lend/components/Buttons/BorrowButton.tsx @@ -23,7 +23,7 @@ type BorrowButtonProps = { } const BorrowButton = ({ asset, val, isDisabled, onHide, marketName }: BorrowButtonProps) => { - const activeLendMarket = useActiveLendMarket(marketName) + const activeLendMarket = useActiveLendMarket(asset) const { pendingTx, handleTx, txHash } = useTransactionHandler() const { approvals } = useLendMarketApprovals(activeLendMarket) const { chainId } = useWeb3React() diff --git a/src/pages/lend/components/Buttons/RepayButton.tsx b/src/pages/lend/components/Buttons/RepayButton.tsx index f4dcbf2b..953b931c 100644 --- a/src/pages/lend/components/Buttons/RepayButton.tsx +++ b/src/pages/lend/components/Buttons/RepayButton.tsx @@ -23,7 +23,7 @@ type RepayButtonProps = { } const RepayButton = ({ asset, val, isDisabled, onHide, marketName }: RepayButtonProps) => { - const activeLendMarket = useActiveLendMarket(marketName) + const activeLendMarket = useActiveLendMarket(asset) const { pendingTx, handleTx, txHash } = useTransactionHandler() const { approvals } = useLendMarketApprovals(activeLendMarket) const { chainId } = useWeb3React() diff --git a/src/pages/lend/components/Buttons/SupplyButton.tsx b/src/pages/lend/components/Buttons/SupplyButton.tsx index 83b5fa46..90fcf45f 100644 --- a/src/pages/lend/components/Buttons/SupplyButton.tsx +++ b/src/pages/lend/components/Buttons/SupplyButton.tsx @@ -23,11 +23,10 @@ type SupplyButtonProps = { } const SupplyButton = ({ asset, val, isDisabled, onHide, marketName }: SupplyButtonProps) => { - const activeLendMarket = useActiveLendMarket(marketName) + const activeLendMarket = useActiveLendMarket(asset) const { pendingTx, handleTx, txHash } = useTransactionHandler() const { approvals } = useLendMarketApprovals(activeLendMarket) const { chainId } = useWeb3React() - const erc20 = useContract('Erc20', asset.underlyingAddress[chainId]) if (pendingTx) { @@ -60,7 +59,7 @@ const SupplyButton = ({ asset, val, isDisabled, onHide, marketName }: SupplyButt disabled={!approvals} onClick={() => { // TODO- give the user a notice that we're approving max uint and instruct them how to change this value. - const tx = erc20.approve(asset.underlyingAddress[chainId], ethers.constants.MaxUint256) + const tx = erc20.approve(asset.marketAddress[chainId], val) handleTx(tx, `${marketName} Approve ${asset.name}`) }} > diff --git a/src/pages/lend/components/Buttons/WithdrawButton.tsx b/src/pages/lend/components/Buttons/WithdrawButton.tsx index e33c7b9f..42f2d80b 100644 --- a/src/pages/lend/components/Buttons/WithdrawButton.tsx +++ b/src/pages/lend/components/Buttons/WithdrawButton.tsx @@ -23,7 +23,7 @@ type WithdrawButtonProps = { } const WithdrawButton = ({ asset, val, isDisabled, onHide, marketName }: WithdrawButtonProps) => { - const activeLendMarket = useActiveLendMarket(marketName) + const activeLendMarket = useActiveLendMarket(asset) const { pendingTx, handleTx, txHash } = useTransactionHandler() const { approvals } = useLendMarketApprovals(activeLendMarket) const { chainId } = useWeb3React() diff --git a/src/pages/lend/components/DebtCard.tsx b/src/pages/lend/components/DebtCard.tsx index 82d85bd6..06bc6954 100644 --- a/src/pages/lend/components/DebtCard.tsx +++ b/src/pages/lend/components/DebtCard.tsx @@ -29,7 +29,7 @@ const DashboardCard: React.FC = ({ }: DashboardCardProps) => { const bao = useBao() const { account, chainId } = useWeb3React() - const asset = useActiveLendMarket(marketName) + const asset = useActiveLendMarket(asset) const accountLiquidity = useAccountLiquidity(marketName, supplyBalances, borrowBalances) const change = mintVal && depositVal ? BigNumber.from(mintVal).sub(BigNumber.from(depositVal)) : BigNumber.from(0) diff --git a/src/pages/lend/components/Modals/BorrowModal.tsx b/src/pages/lend/components/Modals/BorrowModal.tsx index 6e545624..43b2ad1f 100644 --- a/src/pages/lend/components/Modals/BorrowModal.tsx +++ b/src/pages/lend/components/Modals/BorrowModal.tsx @@ -5,7 +5,6 @@ import Typography from '@/components/Typography' import Image from 'next/future/image' import React, { useCallback, useState } from 'react' import Input from '@/components/Input' -import WithdrawButton from '@/pages/lend/components/Buttons/WithdrawButton' import { parseUnits } from 'ethers/lib/utils' import { BigNumber } from 'ethers' import BorrowButton from '@/pages/lend/components/Buttons/BorrowButton' diff --git a/src/pages/lend/components/Modals/SupplyModal.tsx b/src/pages/lend/components/Modals/SupplyModal.tsx index dcaa9bcc..657026b5 100644 --- a/src/pages/lend/components/Modals/SupplyModal.tsx +++ b/src/pages/lend/components/Modals/SupplyModal.tsx @@ -17,7 +17,7 @@ export type SupplyModalProps = { show: boolean onHide: () => void marketName: string - fullBalance: Balance + fullBalance: BigNumber } const SupplyModal = ({ asset, show, onHide, marketName, fullBalance }: SupplyModalProps) => { @@ -33,9 +33,9 @@ const SupplyModal = ({ asset, show, onHide, marketName, fullBalance }: SupplyMod ) const handleSelectMax = useCallback(() => { - if (!fullBalance) return setVal('0') - setVal(fullBalance.balance.toString()) - }, [fullBalance, setVal]) + if (!formattedBalance) return setVal('0') + setVal(formattedBalance) + }, [formattedBalance, setVal]) const hideModal = useCallback(() => { onHide() @@ -43,7 +43,7 @@ const SupplyModal = ({ asset, show, onHide, marketName, fullBalance }: SupplyMod useEffect(() => { if (!fullBalance) return - setFormattedBalance(getDisplayBalance(fullBalance.balance, fullBalance.decimals)) + setFormattedBalance(getDisplayBalance(fullBalance, 18)) }, [fullBalance]) return ( @@ -75,7 +75,7 @@ const SupplyModal = ({ asset, show, onHide, marketName, fullBalance }: SupplyMod onSelectMax={handleSelectMax} onChange={handleChange} value={val} - max={fullBalance && fullBalance.balance.toString()} + max={0} symbol={asset.name} className='h-12 min-w-[150px] z-20 w-full bg-baoBlack lg:h-auto' /> diff --git a/src/pages/lend/components/Modals/WithdrawModal.tsx b/src/pages/lend/components/Modals/WithdrawModal.tsx index a670b0a6..2f2956f3 100644 --- a/src/pages/lend/components/Modals/WithdrawModal.tsx +++ b/src/pages/lend/components/Modals/WithdrawModal.tsx @@ -36,17 +36,12 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) }, [setVal], ) + const formattedSupplied = useMemo(() => { + if (!supplyBalances) return null - const formattedSupplied = useMemo( - () => - supplyBalances && - supplyBalances.find(supply => supply.address === asset.underlyingAddress[chainId]) && - getDisplayBalance( - supplyBalances.find(supply => supply.address === asset.underlyingAddress[chainId]).balance, - asset.underlyingDecimals, - ), - [supplyBalances, asset], - ) + const supply = supplyBalances.find(supply => supply.address === asset.underlyingAddress[chainId]) + return supply ? getDisplayBalance(supply.balance, asset.underlyingDecimals) : null + }, [supplyBalances, asset, chainId]) const handleSelectMax = useCallback(() => { formattedSupplied && setVal(formattedSupplied.toString()) diff --git a/src/pages/lend/components/SupplyList.tsx b/src/pages/lend/components/SupplyList.tsx index 57882f44..d96adbdf 100644 --- a/src/pages/lend/components/SupplyList.tsx +++ b/src/pages/lend/components/SupplyList.tsx @@ -1,7 +1,7 @@ import Loader from '@/components/Loader' import Typography from '@/components/Typography' import Image from 'next/future/image' -import React, { useMemo, useState } from 'react' +import React, { useEffect, useMemo, useState } from 'react' import Config from '@/bao/lib/config' import { getDisplayBalance } from '@/utils/numberFormat' import { Asset, Balance, TotalSupply } from '@/bao/lib/types' @@ -12,6 +12,8 @@ import BorrowModal from '@/pages/lend/components/Modals/BorrowModal' import WithdrawModal from '@/pages/lend/components/Modals/WithdrawModal' import RepayModal from '@/pages/lend/components/Modals/RepayModal' import Tooltipped from '@/components/Tooltipped' +import { useSupplyBalances } from '@/hooks/lend/useSupplyBalances' +import { useAccountBalances } from '@/hooks/lend/useAccountBalances' export const SupplyList: React.FC = ({ marketName, borrowBalances, totalSupplies }) => { const assets = Config.lendMarkets[marketName].assets @@ -41,28 +43,35 @@ export const SupplyList: React.FC = ({ marketName, borrowBalanc const SupplyListItem: React.FC = ({ asset, marketName, borrowBalances, totalSupplies }) => { const { chainId } = useWeb3React() - const [balance, setBalance] = useState(null) + const accountBalances = useAccountBalances(marketName) const [showSupplyModal, setShowSupplyModal] = useState(false) const [showWithdrawModal, setShowWithdrawModal] = useState(false) const [showBorrowModal, setShowBorrowModal] = useState(false) const [showRepayModal, setShowRepayModal] = useState(false) - const yourPosition = useMemo( - () => - borrowBalances && - getDisplayBalance(borrowBalances.find(balance => balance.address === asset.marketAddress[chainId]).balance, asset.underlyingDecimals), - [borrowBalances, asset], - ) + const yourPosition = useMemo(() => { + if (!borrowBalances || !asset || !asset.marketAddress[chainId]) return null - const totalMarketSupply = useMemo( - () => - totalSupplies && - getDisplayBalance( - totalSupplies.find(totalSupply => totalSupply.address === asset.underlyingAddress[chainId]).totalSupply, - asset.underlyingDecimals, - ), - [totalSupplies, asset], - ) + const balance = borrowBalances.find(balance => balance.address === asset.marketAddress[chainId]) + + return balance ? getDisplayBalance(balance.balance, asset.underlyingDecimals) : null + }, [borrowBalances, asset, chainId]) + + const totalMarketSupply = useMemo(() => { + if (!totalSupplies || !asset || !asset.underlyingAddress[chainId]) return null + + const totalSupplyItem = totalSupplies.find(totalSupply => totalSupply.address === asset.underlyingAddress[chainId]) + + return totalSupplyItem ? getDisplayBalance(totalSupplyItem.totalSupply, asset.underlyingDecimals) : null + }, [totalSupplies, asset, chainId]) + + const balance = useMemo(() => { + if (!accountBalances || !asset || !asset.underlyingAddress[chainId]) return null + + const accountBalance = accountBalances.find(({ address }) => address === asset.underlyingAddress[chainId]) + + return accountBalance?.balance || null + }, [accountBalances, asset, chainId]) return ( <> From a8ada08ddaa4c7dd5eb18811124031de78aeaf6b Mon Sep 17 00:00:00 2001 From: Daizze Date: Mon, 21 Oct 2024 19:22:30 +0200 Subject: [PATCH 17/27] Several fixes updating data --- src/hooks/lend/useAccountBalances.ts | 13 ++- src/hooks/lend/useAccountLiquidity.ts | 93 +++++++++--------- src/hooks/lend/useActiveLendMarket.ts | 2 - src/hooks/lend/useBorrowApy.ts | 13 ++- src/hooks/lend/useBorrowBalances.ts | 13 ++- src/hooks/lend/useOraclePrice.ts | 13 ++- src/hooks/lend/useOraclePrices.ts | 45 ++------- src/hooks/lend/useSupplyBalances.ts | 31 ++++-- src/hooks/lend/useTotalCollateral.ts | 13 ++- src/hooks/lend/useTotalDebt.ts | 13 ++- src/hooks/lend/useTotalSupplies.ts | 13 ++- src/pages/lend/[market].tsx | 1 - .../lend/components/Modals/WithdrawModal.tsx | 10 +- src/pages/lend/components/SuppliedCard.tsx | 98 ------------------- src/pages/lend/components/SupplyList.tsx | 1 - 15 files changed, 154 insertions(+), 218 deletions(-) delete mode 100644 src/pages/lend/components/SuppliedCard.tsx diff --git a/src/hooks/lend/useAccountBalances.ts b/src/hooks/lend/useAccountBalances.ts index cbc18397..c2994f73 100644 --- a/src/hooks/lend/useAccountBalances.ts +++ b/src/hooks/lend/useAccountBalances.ts @@ -7,13 +7,14 @@ import { Erc20__factory } from '@/typechain/factories' import MultiCall from '@/utils/multicall' import Config from '@/bao/lib/config' import { Balance } from '@/bao/lib/types' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' export const useAccountBalances = (marketName: string): Balance[] => { const bao = useBao() const { account, library, chainId } = useWeb3React() - const enabled = !!bao && !!account && !!chainId - const { data: balances } = useQuery( - ['@/hooks/lend/useAccountBalance', providerKey(library, account, chainId), { enabled }], + const enabled = !!bao && !!account && !!chainId && !!marketName + const { data: balances, refetch } = useQuery( + ['@/hooks/lend/useAccountBalance', providerKey(library, account, chainId), { enabled, marketName }], async () => { const tokens = Config.lendMarkets[marketName].assets.map(asset => asset.underlyingAddress[chainId]) const contracts: Contract[] = tokens.filter(address => address !== 'ETH').map(address => Erc20__factory.connect(address, library)) @@ -46,5 +47,11 @@ export const useAccountBalances = (marketName: string): Balance[] => { }, ) + const _refetch = () => { + if (enabled) refetch() + } + + useTxReceiptUpdater(_refetch) + return balances } diff --git a/src/hooks/lend/useAccountLiquidity.ts b/src/hooks/lend/useAccountLiquidity.ts index 72cd8b9d..440d0ff2 100644 --- a/src/hooks/lend/useAccountLiquidity.ts +++ b/src/hooks/lend/useAccountLiquidity.ts @@ -10,12 +10,13 @@ import { useQuery } from '@tanstack/react-query' import { useExchangeRates } from './useExchangeRate' import { useSupplyRate } from '@/hooks/lend/useSupplyRate' import { useOraclePrice } from '@/hooks/lend/useOraclePrice' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' export type AccountLiquidity = { netApy: BigNumber - usdSupply: BigNumber - usdBorrow: BigNumber - usdBorrowable: BigNumber + supply: BigNumber + borrow: BigNumber + borrowable: BigNumber } export const useAccountLiquidity = (marketName: string, supplyBalances: Balance[], borrowBalances: Balance[]): AccountLiquidity => { @@ -28,76 +29,70 @@ export const useAccountLiquidity = (marketName: string, supplyBalances: Balance[ const enabled = !!comptroller && !!account && !!market && !!supplyBalances && !!borrowBalances && !!exchangeRates && !!price && !!supplyRate - const { data: accountLiquidity } = useQuery( + const { data: accountLiquidity, refetch } = useQuery( [ '@/hooks/vaults/useAccountLiquidity', providerKey(library, account, chainId), - { - enabled, - supplyBalances, - borrowBalances, - exchangeRates, - price, - marketName, - }, + { enabled, supplyBalances, borrowBalances, exchangeRates, price, marketName }, ], async () => { - const compAccountLiqudity = await comptroller.getAccountLiquidity(account) + const compAccountLiquidity = await comptroller.getAccountLiquidity(account) - const usdSupply = - supplyBalances && supplyBalances.length > 0 - ? Object.keys(exchangeRates).reduce((prev: BigNumber, addr: string) => { - const supply = supplyBalances.find(balance => balance.address === addr) - if (!supply) return - return prev.add(decimate(supply.balance.mul(exchangeRates[addr]).mul(price))) - }, BigNumber.from(0)) - : BigNumber.from(0) + const calculateSupply = () => { + if (!supplyBalances || supplyBalances.length === 0) return BigNumber.from(0) + return Object.keys(exchangeRates).reduce((prev, addr) => { + const supply = supplyBalances.find(balance => balance.address === addr) + if (!supply) return prev + return prev.add(decimate(supply.balance.mul(exchangeRates[addr]).mul(price))) + }, BigNumber.from(0)) + } - const usdBorrow = Object.entries(borrowBalances).reduce((prev: BigNumber, [, { address, balance }]) => { - return prev.add(balance.mul(price)) - }, BigNumber.from(0)) + const calculateBorrow = () => { + return Object.entries(borrowBalances).reduce((prev, [, { balance }]) => { + return prev.add(balance.mul(price)) + }, BigNumber.from(0)) + } - const supplyApy = - supplyBalances && supplyBalances.length > 0 - ? supplyBalances - .find(balance => balance.address === market.underlyingAddresses[chainId]) - .balance.mul(exchangeRates[market.marketAddresses[chainId]]) - .mul(price) - .mul(supplyRate) - : BigNumber.from(0) + const calculateApy = (balances: Balance[], rate: BigNumber, marketAddress: string) => { + const balance = balances.find(balance => balance.address === marketAddress) + return balance ? balance.balance.mul(price).mul(rate) : BigNumber.from(0) + } - const borrowApy = - borrowBalances && borrowBalances.length > 0 - ? borrowBalances - .find(balance => balance.address === market.marketAddresses[chainId]) - .balance.mul(price) - .mul(supplyRate) - : BigNumber.from(0) + const supply = calculateSupply() + const borrow = calculateBorrow() + const supplyApy = calculateApy(supplyBalances, supplyRate, market.marketAddresses[chainId]) + const borrowApy = calculateApy(borrowBalances, supplyRate, market.marketAddresses[chainId]) const netApy = - supplyApy.gt(borrowApy) && !usdSupply.eq(0) - ? supplyApy.sub(borrowApy).div(usdSupply) - : borrowApy.gt(supplyApy) && !usdBorrow.eq(0) - ? supplyApy.sub(borrowApy).div(usdBorrow) + supplyApy.gt(borrowApy) && !supply.isZero() + ? supplyApy.sub(borrowApy).div(supply) + : borrowApy.gt(supplyApy) && !borrow.isZero() + ? supplyApy.sub(borrowApy).div(borrow) : BigNumber.from(0) return { netApy, - usdSupply, - usdBorrow, - usdBorrowable: compAccountLiqudity[1], + supply, + borrow, + borrowable: compAccountLiquidity[1], } }, { enabled, placeholderData: { netApy: BigNumber.from(0), - usdSupply: BigNumber.from(0), - usdBorrow: BigNumber.from(0), - usdBorrowable: BigNumber.from(0), + supply: BigNumber.from(0), + borrow: BigNumber.from(0), + borrowable: BigNumber.from(0), }, }, ) + const _refetch = () => { + if (enabled) refetch() + } + + useTxReceiptUpdater(_refetch) + return accountLiquidity } diff --git a/src/hooks/lend/useActiveLendMarket.ts b/src/hooks/lend/useActiveLendMarket.ts index 636a993d..e834683b 100644 --- a/src/hooks/lend/useActiveLendMarket.ts +++ b/src/hooks/lend/useActiveLendMarket.ts @@ -1,4 +1,3 @@ -import useTransactionProvider from '@/hooks/base/useTransactionProvider' import { Ctoken__factory, Erc20__factory } from '@/typechain/factories' import { useWeb3React } from '@web3-react/core' import { ActiveLendMarket, Asset } from '@/bao/lib/types' @@ -6,7 +5,6 @@ import { useCallback, useEffect, useState } from 'react' export const useActiveLendMarket = (asset: Asset): ActiveLendMarket => { const { library, account, chainId } = useWeb3React() - const { transactions } = useTransactionProvider() const [lendMarket, setLendMarket] = useState(null) const fetchLendMarket = useCallback(async () => { diff --git a/src/hooks/lend/useBorrowApy.ts b/src/hooks/lend/useBorrowApy.ts index 36a0455e..7be7ec70 100644 --- a/src/hooks/lend/useBorrowApy.ts +++ b/src/hooks/lend/useBorrowApy.ts @@ -8,6 +8,7 @@ import MultiCall from '@/utils/multicall' import Config from '@/bao/lib/config' import { BigNumber } from 'ethers' import { parseUnits } from 'ethers/lib/utils' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' export const SECONDS_PER_BLOCK = 12 export const SECONDS_PER_DAY = 24 * 60 * 60 @@ -21,9 +22,9 @@ export const useBorrowApy = (marketName: string): BigNumber => { const bao = useBao() const { account, library, chainId } = useWeb3React() - const enabled = !!bao && !!account && !!chainId - const { data: borrowApy } = useQuery( - ['@/hooks/lend/useBorrowApy', providerKey(library, account, chainId), { enabled }], + const enabled = !!bao && !!account && !!chainId && !!marketName + const { data: borrowApy, refetch } = useQuery( + ['@/hooks/lend/useBorrowApy', providerKey(library, account, chainId), { enabled, marketName }], async () => { const address = Config.lendMarkets[marketName].marketAddresses[chainId] const contracts: Contract[] = [Ctoken__factory.connect(address, library)] @@ -52,5 +53,11 @@ export const useBorrowApy = (marketName: string): BigNumber => { }, ) + const _refetch = () => { + if (enabled) refetch() + } + + useTxReceiptUpdater(_refetch) + return borrowApy } diff --git a/src/hooks/lend/useBorrowBalances.ts b/src/hooks/lend/useBorrowBalances.ts index eb7eb816..3dd14639 100644 --- a/src/hooks/lend/useBorrowBalances.ts +++ b/src/hooks/lend/useBorrowBalances.ts @@ -8,14 +8,15 @@ import MultiCall from '@/utils/multicall' import Config from '@/bao/lib/config' import { Balance } from '@/bao/lib/types' import { BigNumber } from 'ethers' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' export const useBorrowBalances = (marketName: string): Balance[] => { const bao = useBao() const { account, library, chainId } = useWeb3React() - const enabled = !!bao && !!account && !!chainId - const { data: balances } = useQuery( - ['@/hooks/lend/useBorrowBalances', providerKey(library, account, chainId), { enabled }], + const enabled = !!bao && !!account && !!chainId && !!marketName + const { data: balances, refetch } = useQuery( + ['@/hooks/lend/useBorrowBalances', providerKey(library, account, chainId), { enabled, marketName }], async () => { const tokens = Config.lendMarkets[marketName].assets.map(asset => asset.marketAddress[chainId]) const contracts: Contract[] = tokens.map(address => Ctoken__factory.connect(address, library)) @@ -48,5 +49,11 @@ export const useBorrowBalances = (marketName: string): Balance[] => { }, ) + const _refetch = () => { + if (enabled) refetch() + } + + useTxReceiptUpdater(_refetch) + return balances } diff --git a/src/hooks/lend/useOraclePrice.ts b/src/hooks/lend/useOraclePrice.ts index 4503a690..fd409b7e 100644 --- a/src/hooks/lend/useOraclePrice.ts +++ b/src/hooks/lend/useOraclePrice.ts @@ -5,15 +5,16 @@ import { providerKey } from '@/utils/index' import { VaultOracle__factory } from '@/typechain/factories' import Config from '@/bao/lib/config' import { BigNumber } from 'ethers' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' export const useOraclePrice = (marketName: string): BigNumber => { const bao = useBao() const { account, library, chainId } = useWeb3React() const signerOrProvider = account ? library.getSigner() : library - const enabled = !!bao && !!account && !!chainId - const { data: price } = useQuery( - ['@/hooks/lend/useOraclePrice', providerKey(library, account, chainId), { enabled }], + const enabled = !!bao && !!account && !!chainId && !!marketName + const { data: price, refetch } = useQuery( + ['@/hooks/lend/useOraclePrice', providerKey(library, account, chainId), { enabled, marketName }], async () => { const oracle = VaultOracle__factory.connect(Config.lendMarkets[marketName].oracle, signerOrProvider) const address = Config.lendMarkets[marketName].marketAddresses[chainId] @@ -25,5 +26,11 @@ export const useOraclePrice = (marketName: string): BigNumber => { }, ) + const _refetch = () => { + if (enabled) refetch() + } + + useTxReceiptUpdater(_refetch) + return price } diff --git a/src/hooks/lend/useOraclePrices.ts b/src/hooks/lend/useOraclePrices.ts index ad74aab0..a4d47766 100644 --- a/src/hooks/lend/useOraclePrices.ts +++ b/src/hooks/lend/useOraclePrices.ts @@ -7,47 +7,16 @@ import Config from '@/bao/lib/config' import { BigNumber } from 'ethers' import { ContractCallContext, ContractCallResults, Multicall } from 'ethereum-multicall' import { forEach } from 'lodash' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' export const useOraclePrices = (marketName: string): { [p: string]: BigNumber } => { const bao = useBao() const { account, library, chainId } = useWeb3React() const signerOrProvider = account ? library.getSigner() : library - const enabled = !!bao && !!account && !!chainId - const { data: prices } = useQuery( - ['@/hooks/lend/useOraclePrices', providerKey(library, account, chainId), { enabled }], + const enabled = !!bao && !!account && !!chainId && !!marketName + const { data: prices, refetch } = useQuery( + ['@/hooks/lend/useOraclePrices', providerKey(library, account, chainId), { enabled, marketName }], async () => { - /* - const multicall = new Multicall({ ethersProvider: library, tryAggregate: true }) - const multicallContext: ContractCallContext[] = [] - const oracle = VaultOracle__factory.connect(Config.lendMarkets[marketName].oracle, signerOrProvider) - - forEach(Config.lendMarkets[marketName].assets, asset => { - multicallContext.push({ - reference: asset.marketAddress[chainId], - contractAddress: oracle.address, - abi: VaultOracle__factory.abi, - calls: [ - { - reference: 'getUnderlyingPrice', - methodName: 'getUnderlyingPrice', - methodParameters: [asset.marketAddress[chainId]], - }, - ], - }) - }) - - const multicallResults: ContractCallResults = await multicall.call(multicallContext) - - return Object.keys(multicallResults.results).reduce( - (price: { [key: string]: BigNumber }, address: string) => ({ - ...price, - [address]: multicallResults.results[address].callsReturnContext.find(call => call.reference === 'getUnderlyingPrice') - ?.returnValues[0], - }), - {}, - ) - - */ if (!enabled) return null const oracle = VaultOracle__factory.connect(Config.lendMarkets[marketName].oracle, signerOrProvider) @@ -80,5 +49,11 @@ export const useOraclePrices = (marketName: string): { [p: string]: BigNumber } }, ) + const _refetch = () => { + if (enabled) refetch() + } + + useTxReceiptUpdater(_refetch) + return prices } diff --git a/src/hooks/lend/useSupplyBalances.ts b/src/hooks/lend/useSupplyBalances.ts index d6c2f840..2f3832e2 100644 --- a/src/hooks/lend/useSupplyBalances.ts +++ b/src/hooks/lend/useSupplyBalances.ts @@ -3,28 +3,37 @@ import { useWeb3React } from '@web3-react/core' import { useQuery } from '@tanstack/react-query' import { providerKey } from '@/utils/index' import { Contract } from '@ethersproject/contracts' -import { Erc20__factory } from '@/typechain/factories' +import { Ctoken__factory, Erc20__factory } from '@/typechain/factories' import MultiCall from '@/utils/multicall' import { Balance } from '@/bao/lib/types' import Config from '@/bao/lib/config' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' export const useSupplyBalances = (marketName: string): Balance[] => { const bao = useBao() const { account, library, chainId } = useWeb3React() - const enabled = !!bao && !!account && !!chainId - const { data: balances } = useQuery( - ['@/hooks/lend/useSupplyBalances', providerKey(library, account, chainId), { enabled }], + const enabled = !!bao && !!account && !!chainId && !!marketName + const { data: balances, refetch } = useQuery( + ['@/hooks/lend/useSupplyBalances', providerKey(library, account, chainId), { enabled, marketName }], async () => { - const contracts: Contract[] = [Erc20__factory.connect(Config.lendMarkets[marketName].underlyingAddresses[chainId], library)] - + const contracts: Contract[] = Config.lendMarkets[marketName].assets + .map(asset => asset.marketAddress[chainId]) + .map(address => Ctoken__factory.connect(address, library)) const res = MultiCall.parseCallResults( await bao.multicall.call( MultiCall.createCallContext( contracts.map(contract => ({ ref: contract.address, contract, - calls: [{ method: 'symbol' }, { method: 'balanceOf', params: [account] }], + calls: [ + { method: 'symbol' }, + { + method: 'balanceOfUnderlying', + params: [account], + }, + { method: 'decimals' }, + ], })), ), ), @@ -35,7 +44,7 @@ export const useSupplyBalances = (marketName: string): Balance[] => { address, symbol: res[address][0].values[0], balance: res[address][1].values[0], - decimals: 18, + decimals: res[address][2].values[0], } }) }, @@ -44,5 +53,11 @@ export const useSupplyBalances = (marketName: string): Balance[] => { }, ) + const _refetch = () => { + if (enabled) refetch() + } + + useTxReceiptUpdater(_refetch) + return balances } diff --git a/src/hooks/lend/useTotalCollateral.ts b/src/hooks/lend/useTotalCollateral.ts index 6fd3f557..3b74bfce 100644 --- a/src/hooks/lend/useTotalCollateral.ts +++ b/src/hooks/lend/useTotalCollateral.ts @@ -9,15 +9,16 @@ import Config from '@/bao/lib/config' import { BigNumber } from 'ethers' import { useOraclePrices } from './useOraclePrices' import { decimate, getDisplayBalance } from '../../utils/numberFormat' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' export const useTotalCollateral = (marketName: string): BigNumber => { const bao = useBao() const { account, library, chainId } = useWeb3React() const prices = useOraclePrices(marketName) - const enabled = !!bao && !!account && !!chainId && !!prices - const { data: totalCollateral } = useQuery( - ['@/hooks/lend/useTotalCollateral', providerKey(library, account, chainId), { enabled }], + const enabled = !!bao && !!account && !!chainId && !!prices && !!marketName + const { data: totalCollateral, refetch } = useQuery( + ['@/hooks/lend/useTotalCollateral', providerKey(library, account, chainId), { enabled, prices, marketName }], async () => { const addresses = Config.lendMarkets[marketName].assets.map(asset => asset.marketAddress[chainId]) const contracts: Contract[] = addresses.map(address => Ctoken__factory.connect(address, library)) @@ -51,5 +52,11 @@ export const useTotalCollateral = (marketName: string): BigNumber => { }, ) + const _refetch = () => { + if (enabled) refetch() + } + + useTxReceiptUpdater(_refetch) + return totalCollateral } diff --git a/src/hooks/lend/useTotalDebt.ts b/src/hooks/lend/useTotalDebt.ts index 71a7b98e..c9da10c3 100644 --- a/src/hooks/lend/useTotalDebt.ts +++ b/src/hooks/lend/useTotalDebt.ts @@ -7,14 +7,15 @@ import { Ctoken__factory } from '@/typechain/factories' import MultiCall from '@/utils/multicall' import Config from '@/bao/lib/config' import { BigNumber } from 'ethers' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' export const useTotalDebt = (marketName: string): BigNumber => { const bao = useBao() const { account, library, chainId } = useWeb3React() - const enabled = !!bao && !!account && !!chainId - const { data: totalDebt } = useQuery( - ['@/hooks/lend/useTotalDebts', providerKey(library, account, chainId), { enabled }], + const enabled = !!bao && !!account && !!chainId && !!marketName + const { data: totalDebt, refetch } = useQuery( + ['@/hooks/lend/useTotalDebts', providerKey(library, account, chainId), { enabled, marketName }], async () => { const address = Config.lendMarkets[marketName].marketAddresses[chainId] const contracts: Contract[] = [Ctoken__factory.connect(address, library)] @@ -42,5 +43,11 @@ export const useTotalDebt = (marketName: string): BigNumber => { }, ) + const _refetch = () => { + if (enabled) refetch() + } + + useTxReceiptUpdater(_refetch) + return totalDebt } diff --git a/src/hooks/lend/useTotalSupplies.ts b/src/hooks/lend/useTotalSupplies.ts index 7f7847a3..a8164d17 100644 --- a/src/hooks/lend/useTotalSupplies.ts +++ b/src/hooks/lend/useTotalSupplies.ts @@ -7,14 +7,15 @@ import { Ctoken__factory } from '@/typechain/factories' import MultiCall from '@/utils/multicall' import Config from '@/bao/lib/config' import { TotalSupply } from '@/bao/lib/types' +import { useTxReceiptUpdater } from '@/hooks/base/useTransactionProvider' export const useTotalSupplies = (marketName: string): TotalSupply[] => { const bao = useBao() const { account, library, chainId } = useWeb3React() - const enabled = !!bao && !!account && !!chainId - const { data: totalSupplies } = useQuery( - ['@/hooks/lend/useTotalSupplies', providerKey(library, account, chainId), { enabled }], + const enabled = !!bao && !!account && !!chainId && !!marketName + const { data: totalSupplies, refetch } = useQuery( + ['@/hooks/lend/useTotalSupplies', providerKey(library, account, chainId), { enabled, marketName }], async () => { const tokens = Config.lendMarkets[marketName].assets.map(asset => asset.underlyingAddress[chainId]) const contracts: Contract[] = tokens.map(address => Ctoken__factory.connect(address, library)) @@ -57,5 +58,11 @@ export const useTotalSupplies = (marketName: string): TotalSupply[] => { }, ) + const _refetch = () => { + if (enabled) refetch() + } + + useTxReceiptUpdater(_refetch) + return totalSupplies } diff --git a/src/pages/lend/[market].tsx b/src/pages/lend/[market].tsx index b3b38a13..cdc1a5a1 100644 --- a/src/pages/lend/[market].tsx +++ b/src/pages/lend/[market].tsx @@ -45,7 +45,6 @@ const Market: NextPage<{ marketName: string }> = ({ marketName }) => { const borrowBalances = useBorrowBalances(marketName) - const supplyBalances = useSupplyBalances(marketName) const totalSupplies = useTotalSupplies(marketName) const oraclePrice = useOraclePrice(marketName) const totalCollateral = useTotalCollateral(marketName) diff --git a/src/pages/lend/components/Modals/WithdrawModal.tsx b/src/pages/lend/components/Modals/WithdrawModal.tsx index 2f2956f3..5291b86c 100644 --- a/src/pages/lend/components/Modals/WithdrawModal.tsx +++ b/src/pages/lend/components/Modals/WithdrawModal.tsx @@ -36,12 +36,16 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) }, [setVal], ) - const formattedSupplied = useMemo(() => { + + const supply = useMemo(() => { if (!supplyBalances) return null + return supplyBalances.find(supply => supply.address === asset.marketAddress[chainId]) + }, [supplyBalances, asset, chainId]) - const supply = supplyBalances.find(supply => supply.address === asset.underlyingAddress[chainId]) + const formattedSupplied = useMemo(() => { + if (!supply) return null return supply ? getDisplayBalance(supply.balance, asset.underlyingDecimals) : null - }, [supplyBalances, asset, chainId]) + }, [supply, asset, chainId]) const handleSelectMax = useCallback(() => { formattedSupplied && setVal(formattedSupplied.toString()) diff --git a/src/pages/lend/components/SuppliedCard.tsx b/src/pages/lend/components/SuppliedCard.tsx deleted file mode 100644 index 02c779aa..00000000 --- a/src/pages/lend/components/SuppliedCard.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import Card from '@/components/Card/Card' -import Typography from '@/components/Typography' -import { useWeb3React } from '@web3-react/core' -import React, { useEffect, useState } from 'react' -import { useSupplyBalances } from '@/hooks/lend/useSupplyBalances' -import Config from '@/bao/lib/config' -import { Asset, Balance } from '@/bao/lib/types' -import { getDisplayBalance } from '@/utils/numberFormat' -import Image from 'next/future/image' -import Loader from '@/components/Loader' -import { BigNumber } from 'ethers' - -export const SuppliedCard = ({ marketName, onUpdate }: { marketName: string; onUpdate: (updatedState: any) => void }) => { - const { account, chainId } = useWeb3React() - const assets = Config.lendMarkets[marketName].assets - const suppliedBalances = useSupplyBalances(marketName) - const [supplied, setSupplied] = useState([]) - - useEffect(() => { - if (suppliedBalances) { - const suppliedAddresses = suppliedBalances.filter(balance => balance.balance.gt(BigNumber.from(0))).map(balance => balance.address) - setSupplied(assets.filter(asset => asset.supply === true && suppliedAddresses.includes(asset.underlyingAddress[chainId]))) - } - }, [suppliedBalances]) - - return ( - <> - - Supplied - - - {supplied && supplied.length > 0 ? ( - supplied.map(asset => ( - - )) - ) : ( - - {suppliedBalances ? 'No assets supplied' : } - - )} - - - ) -} - -const SuppliedListItem: React.FC = ({ asset, suppliedBalances, marketName }) => { - const { chainId } = useWeb3React() - const [formattedBalance, setFormattedBalance] = useState(null) - const [balance, setBalance] = useState(null) - const [showSupplyModal, setShowSupplyModal] = useState(false) - const [showBorrowModal, setShowBorrowModal] = useState(false) - - function fetchBalance(asset: Asset) { - if (suppliedBalances !== null && suppliedBalances !== undefined) - return suppliedBalances.find(({ address }) => address === asset.underlyingAddress[chainId]) - } - - useEffect(() => { - const balance = fetchBalance(asset) - if (balance !== null && balance !== undefined) { - setBalance(balance) - setFormattedBalance(getDisplayBalance(balance.balance, balance.decimals)) - } - }, [suppliedBalances]) - - return ( - <> -
-
-
-
-
- {asset.name} - - - {asset.name} - - -
-
-
-
{formattedBalance ? formattedBalance : }
-
-
- - ) -} - -type SuppliedListItemProps = { - asset: Asset - suppliedBalances: Balance[] - marketName: string -} - -export default SuppliedCard diff --git a/src/pages/lend/components/SupplyList.tsx b/src/pages/lend/components/SupplyList.tsx index d96adbdf..675c4671 100644 --- a/src/pages/lend/components/SupplyList.tsx +++ b/src/pages/lend/components/SupplyList.tsx @@ -12,7 +12,6 @@ import BorrowModal from '@/pages/lend/components/Modals/BorrowModal' import WithdrawModal from '@/pages/lend/components/Modals/WithdrawModal' import RepayModal from '@/pages/lend/components/Modals/RepayModal' import Tooltipped from '@/components/Tooltipped' -import { useSupplyBalances } from '@/hooks/lend/useSupplyBalances' import { useAccountBalances } from '@/hooks/lend/useAccountBalances' export const SupplyList: React.FC = ({ marketName, borrowBalances, totalSupplies }) => { From cfbc89e74f87977846a9b73a2e36d0e60a3683ea Mon Sep 17 00:00:00 2001 From: Daizze Date: Wed, 23 Oct 2024 22:59:57 +0200 Subject: [PATCH 18/27] Fixing issues for build --- .../lend/components/Buttons/RepayButton.tsx | 3 +-- src/pages/lend/components/DebtCard.tsx | 20 +++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/pages/lend/components/Buttons/RepayButton.tsx b/src/pages/lend/components/Buttons/RepayButton.tsx index 953b931c..a47eed42 100644 --- a/src/pages/lend/components/Buttons/RepayButton.tsx +++ b/src/pages/lend/components/Buttons/RepayButton.tsx @@ -1,10 +1,9 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import { ActiveSupportedVault, Asset } from '@/bao/lib/types' +import { Asset } from '@/bao/lib/types' import Button from '@/components/Button' import { PendingTransaction } from '@/components/Loader/Loader' import useContract from '@/hooks/base/useContract' import useTransactionHandler from '@/hooks/base/useTransactionHandler' -import { useApprovals } from '@/hooks/vaults/useApprovals' import type { Erc20 } from '@/typechain/index' import { getDisplayBalance } from '@/utils/numberFormat' import { faExternalLink } from '@fortawesome/free-solid-svg-icons' diff --git a/src/pages/lend/components/DebtCard.tsx b/src/pages/lend/components/DebtCard.tsx index 06bc6954..906bfa4d 100644 --- a/src/pages/lend/components/DebtCard.tsx +++ b/src/pages/lend/components/DebtCard.tsx @@ -11,6 +11,7 @@ import { faDashboard } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { useActiveLendMarket } from '@/hooks/lend/useActiveLendMarket' import useHealthFactor from '@/hooks/lend/useHealthFactor' +import Config from '@/bao/lib/config' type DashboardCardProps = { marketName: string @@ -29,14 +30,17 @@ const DashboardCard: React.FC = ({ }: DashboardCardProps) => { const bao = useBao() const { account, chainId } = useWeb3React() - const asset = useActiveLendMarket(asset) + const asset = Config.lendMarkets[marketName].assets.find( + asset => asset.marketAddress[chainId] === Config.lendMarkets[marketName].marketAddresses[chainId], + ) + const activeLendMarket = useActiveLendMarket(asset) const accountLiquidity = useAccountLiquidity(marketName, supplyBalances, borrowBalances) const change = mintVal && depositVal ? BigNumber.from(mintVal).sub(BigNumber.from(depositVal)) : BigNumber.from(0) - const borrow = accountLiquidity ? accountLiquidity.usdBorrow : BigNumber.from(0) + const borrow = accountLiquidity ? accountLiquidity.borrow : BigNumber.from(0) const newBorrow = borrow ? borrow.sub(change.gt(0) ? change : 0) : BigNumber.from(0) - const borrowable = accountLiquidity ? accountLiquidity.usdBorrow.add(exponentiate(accountLiquidity.usdBorrowable)) : BigNumber.from(0) - const newBorrowable = asset && decimate(borrowable).sub(BigNumber.from(parseUnits(formatUnits(change, 36 - 18)))) + const borrowable = accountLiquidity ? accountLiquidity.borrow.add(exponentiate(accountLiquidity.borrowable)) : BigNumber.from(0) + const newBorrowable = activeLendMarket && decimate(borrowable).sub(BigNumber.from(parseUnits(formatUnits(change, 36 - 18)))) const borrowChange = borrow.add(exponentiate(change)) const healthFactor = useHealthFactor(marketName, borrowBalances, borrowChange) @@ -44,7 +48,7 @@ const DashboardCard: React.FC = ({ const barPercentage = parseFloat( getDisplayBalance( accountLiquidity && newBorrowable && !newBorrowable.eq(0) - ? (parseFloat(accountLiquidity.usdBorrow.toString()) / parseFloat(newBorrowable.toString())) * 100 + ? (parseFloat(accountLiquidity.borrow.toString()) / parseFloat(newBorrowable.toString())) * 100 : 0, 18, 2, @@ -99,7 +103,7 @@ const DashboardCard: React.FC = ({ $ {`${ bao && account && accountLiquidity - ? getDisplayBalance(decimate(BigNumber.from(accountLiquidity.usdSupply.toString())), 18, 2) + ? getDisplayBalance(decimate(BigNumber.from(accountLiquidity.supply.toString())), 18, 2) : 0 }`}{' '} @@ -109,7 +113,7 @@ const DashboardCard: React.FC = ({ Your Debt - ${accountLiquidity ? getDisplayBalance(decimate(accountLiquidity.usdBorrow), 18, 2) : 0} + ${accountLiquidity ? getDisplayBalance(decimate(accountLiquidity.borrow), 18, 2) : 0}
@@ -125,7 +129,7 @@ const DashboardCard: React.FC = ({ Debt Limit Remaining - ${getDisplayBalance(accountLiquidity ? accountLiquidity.usdBorrowable.sub(change) : BigNumber.from(0))} + ${getDisplayBalance(accountLiquidity ? accountLiquidity.borrowable.sub(change) : BigNumber.from(0))}
From 0416ed7ddb5295e87a16919ccfd096ac689bc8e2 Mon Sep 17 00:00:00 2001 From: Daizze Date: Fri, 25 Oct 2024 00:44:23 +0200 Subject: [PATCH 19/27] Fixing calculations borrow modal --- .../lend/components/Modals/BorrowModal.tsx | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/pages/lend/components/Modals/BorrowModal.tsx b/src/pages/lend/components/Modals/BorrowModal.tsx index 43b2ad1f..e1b54cea 100644 --- a/src/pages/lend/components/Modals/BorrowModal.tsx +++ b/src/pages/lend/components/Modals/BorrowModal.tsx @@ -3,11 +3,18 @@ import { Asset } from '@/bao/lib/types' import Modal from '@/components/Modal' import Typography from '@/components/Typography' import Image from 'next/future/image' -import React, { useCallback, useState } from 'react' +import React, { useCallback, useMemo, useState } from 'react' import Input from '@/components/Input' -import { parseUnits } from 'ethers/lib/utils' -import { BigNumber } from 'ethers' +import { formatUnits, parseUnits } from 'ethers/lib/utils' +import { BigNumber, FixedNumber } from 'ethers' import BorrowButton from '@/pages/lend/components/Buttons/BorrowButton' +import { useActiveLendMarket } from '@/hooks/lend/useActiveLendMarket' +import { useAccountLiquidity } from '@/hooks/lend/useAccountLiquidity' +import { useSupplyBalances } from '@/hooks/lend/useSupplyBalances' +import { useBorrowBalances } from '@/hooks/lend/useBorrowBalances' +import { decimate, getDisplayBalance, getFullDisplayBalance } from '@/utils/numberFormat' +import { useOraclePrices } from '@/hooks/lend/useOraclePrices' +import { useWeb3React } from '@web3-react/core' export type BorrowModalProps = { asset: Asset @@ -17,7 +24,13 @@ export type BorrowModalProps = { } const BorrowModal = ({ asset, show, onHide, marketName }: BorrowModalProps) => { + const { chainId } = useWeb3React() const [val, setVal] = useState('0') + const activeLendMarket = useActiveLendMarket(asset) + const supplyBalances = useSupplyBalances(marketName) + const borrowBalances = useBorrowBalances(marketName) + const accountliquidity = useAccountLiquidity(marketName, supplyBalances, borrowBalances) + const prices = useOraclePrices(marketName) const operation = 'Borrow' const hideModal = useCallback(() => { @@ -31,9 +44,22 @@ const BorrowModal = ({ asset, show, onHide, marketName }: BorrowModalProps) => { [setVal], ) + const borrowable = useMemo(() => { + if (!prices || !accountliquidity) return null + + const marketPrice = prices[asset.marketAddress[chainId]] + if (!marketPrice) return null + + return BigNumber.from(FixedNumber.from(formatUnits(accountliquidity.borrowable)).divUnsafe(FixedNumber.from(formatUnits(marketPrice))))q + }, [accountliquidity, asset, chainId, prices]) + + const formattedBorrowable = useMemo(() => { + return borrowable ? getDisplayBalance(borrowable) : null + }, [borrowable]) + const handleSelectMax = useCallback(() => { - return setVal('0') - }, []) + setVal(formattedBorrowable || '0.00') + }, [formattedBorrowable]) return ( @@ -53,7 +79,7 @@ const BorrowModal = ({ asset, show, onHide, marketName }: BorrowModalProps) => { Available: - {'0.00'} + {formattedBorrowable ? formattedBorrowable : '0.00'} {asset.name} {asset.name} From 32208b158364353bba0b81d774b9946ce105c000 Mon Sep 17 00:00:00 2001 From: Daizze Date: Mon, 28 Oct 2024 21:03:53 +0100 Subject: [PATCH 20/27] Minor bugfix typo --- src/pages/lend/components/Modals/BorrowModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/lend/components/Modals/BorrowModal.tsx b/src/pages/lend/components/Modals/BorrowModal.tsx index e1b54cea..e439bf23 100644 --- a/src/pages/lend/components/Modals/BorrowModal.tsx +++ b/src/pages/lend/components/Modals/BorrowModal.tsx @@ -50,7 +50,7 @@ const BorrowModal = ({ asset, show, onHide, marketName }: BorrowModalProps) => { const marketPrice = prices[asset.marketAddress[chainId]] if (!marketPrice) return null - return BigNumber.from(FixedNumber.from(formatUnits(accountliquidity.borrowable)).divUnsafe(FixedNumber.from(formatUnits(marketPrice))))q + return BigNumber.from(FixedNumber.from(formatUnits(accountliquidity.borrowable)).divUnsafe(FixedNumber.from(formatUnits(marketPrice)))) }, [accountliquidity, asset, chainId, prices]) const formattedBorrowable = useMemo(() => { From 757a19c37ccc38bc601e362fe449ef578913c17b Mon Sep 17 00:00:00 2001 From: Daizze Date: Thu, 7 Nov 2024 00:33:11 +0100 Subject: [PATCH 21/27] Adjustments for repay in Lend --- src/hooks/lend/useBorrowBalances.ts | 2 +- src/pages/lend/[market].tsx | 9 ++--- src/pages/lend/components/AssetsCard.tsx | 6 ++-- .../lend/components/Modals/BorrowModal.tsx | 10 +++--- .../lend/components/Modals/RepayModal.tsx | 36 ++++++++++++++++--- src/pages/lend/components/SupplyList.tsx | 18 +++++----- 6 files changed, 51 insertions(+), 30 deletions(-) diff --git a/src/hooks/lend/useBorrowBalances.ts b/src/hooks/lend/useBorrowBalances.ts index 3dd14639..06871c3e 100644 --- a/src/hooks/lend/useBorrowBalances.ts +++ b/src/hooks/lend/useBorrowBalances.ts @@ -27,7 +27,7 @@ export const useBorrowBalances = (marketName: string): Balance[] => { contracts.map(contract => ({ ref: contract.address, contract, - calls: [{ method: 'symbol' }, { method: 'balanceOfUnderlying', params: [account] }], + calls: [{ method: 'symbol' }, { method: 'borrowBalanceStored', params: [account] }], })), ), ), diff --git a/src/pages/lend/[market].tsx b/src/pages/lend/[market].tsx index cdc1a5a1..07c632a8 100644 --- a/src/pages/lend/[market].tsx +++ b/src/pages/lend/[market].tsx @@ -8,9 +8,6 @@ import Link from 'next/link' import React, { useCallback, useMemo, useState } from 'react' import Config from '@/bao/lib/config' import AssetsCard from '@/pages/lend/components/AssetsCard' -import { useAccountBalances } from '@/hooks/lend/useAccountBalances' -import DebtCard from '@/pages/lend/components/DebtCard' -import { useBorrowBalances } from '@/hooks/lend/useBorrowBalances' import { useTotalSupplies } from '@/hooks/lend/useTotalSupplies' import { useOraclePrice } from '@/hooks/lend/useOraclePrice' import { decimate, getDisplayBalance } from '@/utils/numberFormat' @@ -44,7 +41,7 @@ export async function getStaticProps({ params }: { params: any }) { const Market: NextPage<{ marketName: string }> = ({ marketName }) => { - const borrowBalances = useBorrowBalances(marketName) + const supplyBalances = useSupplyBalances(marketName) const totalSupplies = useTotalSupplies(marketName) const oraclePrice = useOraclePrice(marketName) const totalCollateral = useTotalCollateral(marketName) @@ -71,7 +68,6 @@ const Market: NextPage<{ const handleSupplyVal = useCallback( (updatedState: any) => { - // update the parent component's state with the new value setSupplyVal(updatedState) }, [supplyVal], @@ -79,7 +75,6 @@ const Market: NextPage<{ const handleBorrowVal = useCallback( (updatedState: any) => { - // update the parent component's state with the new value setBorrowVal(updatedState) }, [borrowVal], @@ -160,7 +155,7 @@ const Market: NextPage<{
- +
diff --git a/src/pages/lend/components/AssetsCard.tsx b/src/pages/lend/components/AssetsCard.tsx index f126abef..febfa5b9 100644 --- a/src/pages/lend/components/AssetsCard.tsx +++ b/src/pages/lend/components/AssetsCard.tsx @@ -4,16 +4,16 @@ import { Balance, TotalSupply } from '@/bao/lib/types' export const AssetsCard = ({ marketName, - borrowBalances, + supplyBalances, totalSupplies, }: { marketName: string - borrowBalances: Balance[] + supplyBalances: Balance[] totalSupplies: TotalSupply[] }) => { return ( <> - + ) } diff --git a/src/pages/lend/components/Modals/BorrowModal.tsx b/src/pages/lend/components/Modals/BorrowModal.tsx index e439bf23..b022e041 100644 --- a/src/pages/lend/components/Modals/BorrowModal.tsx +++ b/src/pages/lend/components/Modals/BorrowModal.tsx @@ -12,7 +12,7 @@ import { useActiveLendMarket } from '@/hooks/lend/useActiveLendMarket' import { useAccountLiquidity } from '@/hooks/lend/useAccountLiquidity' import { useSupplyBalances } from '@/hooks/lend/useSupplyBalances' import { useBorrowBalances } from '@/hooks/lend/useBorrowBalances' -import { decimate, getDisplayBalance, getFullDisplayBalance } from '@/utils/numberFormat' +import { getDisplayBalance } from '@/utils/numberFormat' import { useOraclePrices } from '@/hooks/lend/useOraclePrices' import { useWeb3React } from '@web3-react/core' @@ -29,7 +29,7 @@ const BorrowModal = ({ asset, show, onHide, marketName }: BorrowModalProps) => { const activeLendMarket = useActiveLendMarket(asset) const supplyBalances = useSupplyBalances(marketName) const borrowBalances = useBorrowBalances(marketName) - const accountliquidity = useAccountLiquidity(marketName, supplyBalances, borrowBalances) + const accountLiquidity = useAccountLiquidity(marketName, supplyBalances, borrowBalances) const prices = useOraclePrices(marketName) const operation = 'Borrow' @@ -45,13 +45,13 @@ const BorrowModal = ({ asset, show, onHide, marketName }: BorrowModalProps) => { ) const borrowable = useMemo(() => { - if (!prices || !accountliquidity) return null + if (!prices || !accountLiquidity) return null const marketPrice = prices[asset.marketAddress[chainId]] if (!marketPrice) return null - return BigNumber.from(FixedNumber.from(formatUnits(accountliquidity.borrowable)).divUnsafe(FixedNumber.from(formatUnits(marketPrice)))) - }, [accountliquidity, asset, chainId, prices]) + return BigNumber.from(FixedNumber.from(formatUnits(accountLiquidity.borrowable)).divUnsafe(FixedNumber.from(formatUnits(marketPrice)))) + }, [accountLiquidity, asset, chainId, prices]) const formattedBorrowable = useMemo(() => { return borrowable ? getDisplayBalance(borrowable) : null diff --git a/src/pages/lend/components/Modals/RepayModal.tsx b/src/pages/lend/components/Modals/RepayModal.tsx index e79a3f50..8ef487f0 100644 --- a/src/pages/lend/components/Modals/RepayModal.tsx +++ b/src/pages/lend/components/Modals/RepayModal.tsx @@ -3,11 +3,15 @@ import { Asset } from '@/bao/lib/types' import Modal from '@/components/Modal' import Typography from '@/components/Typography' import Image from 'next/future/image' -import React, { useCallback, useState } from 'react' +import React, { useCallback, useMemo, useState } from 'react' import Input from '@/components/Input' import { parseUnits } from 'ethers/lib/utils' import { BigNumber } from 'ethers' import RepayButton from '@/pages/lend/components/Buttons/RepayButton' +import { useBorrowBalances } from '@/hooks/lend/useBorrowBalances' +import { useAccountBalances } from '@/hooks/lend/useAccountBalances' +import { getDisplayBalance } from '@/utils/numberFormat' +import { useWeb3React } from '@web3-react/core' export type RepayModalProps = { asset: Asset @@ -17,7 +21,29 @@ export type RepayModalProps = { } const RepayModal = ({ asset, show, onHide, marketName }: RepayModalProps) => { + const { chainId } = useWeb3React() const operation = 'Repay' + const accountBalances = useAccountBalances(marketName) + const borrowBalances = useBorrowBalances(marketName) + + const maxRepay = useMemo(() => { + if (!borrowBalances || !accountBalances) return BigNumber.from(0) + + const borrowBalance = borrowBalances.find( + balance => balance.address.toLowerCase() === asset.marketAddress[chainId].toLowerCase(), + )?.balance + const accountBalance = accountBalances.find( + balance => balance.address.toLowerCase() === asset.underlyingAddress[chainId].toLowerCase(), + )?.balance + + if (!borrowBalance || !accountBalances) return BigNumber.from(0) + + return accountBalance.lt(borrowBalance) ? accountBalance : borrowBalance + }, [borrowBalances, accountBalances]) + + const maxRepayFormatted = useMemo(() => { + return maxRepay ? getDisplayBalance(maxRepay) : null + }, [maxRepay]) const [val, setVal] = useState('0') @@ -33,8 +59,8 @@ const RepayModal = ({ asset, show, onHide, marketName }: RepayModalProps) => { ) const handleSelectMax = useCallback(() => { - return setVal('0') - }, []) + setVal(maxRepayFormatted || '0.00') + }, [maxRepayFormatted]) return ( <> @@ -56,7 +82,7 @@ const RepayModal = ({ asset, show, onHide, marketName }: RepayModalProps) => { Available: - {'0.00'} + {maxRepayFormatted ? maxRepayFormatted : '0.00'} {asset.name} {asset.name} @@ -67,7 +93,7 @@ const RepayModal = ({ asset, show, onHide, marketName }: RepayModalProps) => { onSelectMax={handleSelectMax} onChange={handleChange} value={val} - max={0} + max={maxRepayFormatted} symbol={asset.name} className='h-12 min-w-[150px] z-20 w-full bg-baoBlack lg:h-auto' /> diff --git a/src/pages/lend/components/SupplyList.tsx b/src/pages/lend/components/SupplyList.tsx index 675c4671..06c96f1e 100644 --- a/src/pages/lend/components/SupplyList.tsx +++ b/src/pages/lend/components/SupplyList.tsx @@ -1,7 +1,7 @@ import Loader from '@/components/Loader' import Typography from '@/components/Typography' import Image from 'next/future/image' -import React, { useEffect, useMemo, useState } from 'react' +import React, { useMemo, useState } from 'react' import Config from '@/bao/lib/config' import { getDisplayBalance } from '@/utils/numberFormat' import { Asset, Balance, TotalSupply } from '@/bao/lib/types' @@ -14,7 +14,7 @@ import RepayModal from '@/pages/lend/components/Modals/RepayModal' import Tooltipped from '@/components/Tooltipped' import { useAccountBalances } from '@/hooks/lend/useAccountBalances' -export const SupplyList: React.FC = ({ marketName, borrowBalances, totalSupplies }) => { +export const SupplyList: React.FC = ({ marketName, supplyBalances, totalSupplies }) => { const assets = Config.lendMarkets[marketName].assets return ( @@ -29,7 +29,7 @@ export const SupplyList: React.FC = ({ marketName, borrowBalanc @@ -40,7 +40,7 @@ export const SupplyList: React.FC = ({ marketName, borrowBalanc ) } -const SupplyListItem: React.FC = ({ asset, marketName, borrowBalances, totalSupplies }) => { +const SupplyListItem: React.FC = ({ asset, marketName, supplyBalances, totalSupplies }) => { const { chainId } = useWeb3React() const accountBalances = useAccountBalances(marketName) const [showSupplyModal, setShowSupplyModal] = useState(false) @@ -49,12 +49,12 @@ const SupplyListItem: React.FC = ({ asset, marketName, borr const [showRepayModal, setShowRepayModal] = useState(false) const yourPosition = useMemo(() => { - if (!borrowBalances || !asset || !asset.marketAddress[chainId]) return null + if (!supplyBalances || !asset || !asset.marketAddress[chainId]) return null - const balance = borrowBalances.find(balance => balance.address === asset.marketAddress[chainId]) + const balance = supplyBalances.find(balance => balance.address === asset.marketAddress[chainId]) return balance ? getDisplayBalance(balance.balance, asset.underlyingDecimals) : null - }, [borrowBalances, asset, chainId]) + }, [supplyBalances, asset, chainId]) const totalMarketSupply = useMemo(() => { if (!totalSupplies || !asset || !asset.underlyingAddress[chainId]) return null @@ -161,12 +161,12 @@ export default SupplyList type SupplyListItemProps = { asset: Asset marketName: string - borrowBalances: Balance[] + supplyBalances: Balance[] totalSupplies: TotalSupply[] } type SupplyListProps = { marketName: string - borrowBalances: Balance[] + supplyBalances: Balance[] totalSupplies: TotalSupply[] } From 058b5631a21a2a0a72817dff9d8c3a3fbeffbc4a Mon Sep 17 00:00:00 2001 From: Daizze Date: Sun, 10 Nov 2024 01:14:33 +0100 Subject: [PATCH 22/27] Adjustments withdraw --- src/hooks/lend/useComptrollerData.ts | 42 --------------- .../lend/components/Modals/WithdrawModal.tsx | 54 +++++++++++++++++-- 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/hooks/lend/useComptrollerData.ts b/src/hooks/lend/useComptrollerData.ts index b8d130d0..95375133 100644 --- a/src/hooks/lend/useComptrollerData.ts +++ b/src/hooks/lend/useComptrollerData.ts @@ -16,48 +16,6 @@ export const useComptrollerData = (marketName: string): ComptrollerData[] => { const { data: comptrollerData } = useQuery( ['@/hooks/lend/useComptrollerData', providerKey(library, account, chainId), { enabled }], async () => { - /* - const multicall = new Multicall({ ethersProvider: library, tryAggregate: true }) - const multicallContext: ContractCallContext[] = [] - const comptroller = Comptroller__factory.connect(Config.lendMarkets[marketName].comptroller, signerOrProvider) - - forEach(Config.lendMarkets[marketName].assets, asset => { - multicallContext.push({ - reference: asset.marketAddress[chainId], - contractAddress: comptroller.address, - abi: Comptroller__factory.abi, - calls: [ - { - reference: 'markets', - methodName: 'markets', - methodParameters: [asset.marketAddress[chainId]], - }, - { - reference: 'compBorrowState', - methodName: 'compBorrowState', - methodParameters: [asset.marketAddress[chainId]], - }, - { - reference: 'borrowRestricted', - methodName: 'borrowRestricted', - methodParameters: [asset.marketAddress[chainId]], - }, - ], - }) - }) - - const multicallResults: ContractCallResults = await multicall.call(multicallContext) - - return Object.values(multicallResults.results).map(data => { - return { - address: data.originalContractCallContext.reference, - collateralFactor: data.callsReturnContext.find(call => call.reference === 'markets')?.returnValues[1], - imfFactor: data.callsReturnContext.find(call => call.reference === 'markets')?.returnValues[2], - } - }) - - */ - if (!enabled) return null const multicall = new Multicall({ ethersProvider: library, tryAggregate: true }) diff --git a/src/pages/lend/components/Modals/WithdrawModal.tsx b/src/pages/lend/components/Modals/WithdrawModal.tsx index 5291b86c..090f7149 100644 --- a/src/pages/lend/components/Modals/WithdrawModal.tsx +++ b/src/pages/lend/components/Modals/WithdrawModal.tsx @@ -5,7 +5,7 @@ import Typography from '@/components/Typography' import Image from 'next/future/image' import React, { useCallback, useMemo, useState } from 'react' import Input from '@/components/Input' -import { decimate, getDisplayBalance } from '@/utils/numberFormat' +import { decimate, exponentiate, getDisplayBalance, sqrt } from '@/utils/numberFormat' import { BigNumber } from 'ethers' import { useWeb3React } from '@web3-react/core' import { useExchangeRates } from '@/hooks/lend/useExchangeRate' @@ -14,6 +14,10 @@ import WithdrawButton from '@/pages/lend/components/Buttons/WithdrawButton' import { useSupplyBalances } from '@/hooks/lend/useSupplyBalances' import { useComptrollerData } from '@/hooks/lend/useComptrollerData' import Loader from '@/components/Loader' +import { useAccountLiquidity } from '@/hooks/lend/useAccountLiquidity' +import { useBorrowBalances } from '@/hooks/lend/useBorrowBalances' +import { useOraclePrice } from '@/hooks/lend/useOraclePrice' +import { useOraclePrices } from '@/hooks/lend/useOraclePrices' export type WithdrawModalProps = { asset: Asset @@ -25,10 +29,13 @@ export type WithdrawModalProps = { const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) => { const { chainId } = useWeb3React() const supplyBalances = useSupplyBalances(marketName) + const borrowBalances = useBorrowBalances(marketName) const { exchangeRates } = useExchangeRates(marketName) const [val, setVal] = useState('0') const operation = 'Withdraw' const comptrollerData = useComptrollerData(marketName) + const accountLiquidity = useAccountLiquidity(marketName, supplyBalances, borrowBalances) + const prices = useOraclePrices(marketName) const handleChange = useCallback( (e: React.FormEvent) => { @@ -38,17 +45,54 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) ) const supply = useMemo(() => { - if (!supplyBalances) return null - return supplyBalances.find(supply => supply.address === asset.marketAddress[chainId]) + return supplyBalances?.find(supply => supply.address === asset.marketAddress[chainId]) || null }, [supplyBalances, asset, chainId]) const formattedSupplied = useMemo(() => { if (!supply) return null - return supply ? getDisplayBalance(supply.balance, asset.underlyingDecimals) : null + return getDisplayBalance(supply.balance, asset.underlyingDecimals) }, [supply, asset, chainId]) + const assetComptrollerData = useMemo(() => { + return comptrollerData?.find(data => data.address === asset.marketAddress) || null + }, [comptrollerData, asset]) + + const imfFactor = useMemo(() => { + if (!assetComptrollerData || !accountLiquidity) return null + + let imfFactor = assetComptrollerData.imfFactor + + if (accountLiquidity) { + const sqrtBalance = sqrt(supply.balance) + const num = exponentiate(parseUnits('1.1')) + const denom = decimate(imfFactor.mul(sqrtBalance).add(parseUnits('1'))) + imfFactor = num.div(denom) + } + return imfFactor + }, [assetComptrollerData, accountLiquidity]) + + const price = useMemo(() => { + return prices?.[asset.marketAddress[chainId]] || null + }, [prices, asset, chainId]) + + const withdrawable = useMemo(() => { + if (!assetComptrollerData || !accountLiquidity || !imfFactor || !price) return BigNumber.from(0) + + const collateralFactor = assetComptrollerData.collateralFactor + const borrowable = accountLiquidity.borrowable + + if (imfFactor.gt(collateralFactor) && price.gt(0)) { + const factor = collateralFactor.mul(price).gt(0) ? decimate(collateralFactor.mul(price)) : decimate(imfFactor).mul(price) + + return exponentiate(borrowable).div(factor) + } + + return BigNumber.from(0) + }, [assetComptrollerData, accountLiquidity, imfFactor, price]) + const handleSelectMax = useCallback(() => { - formattedSupplied && setVal(formattedSupplied.toString()) + const max = !(accountLiquidity && accountLiquidity.borrowable) || withdrawable.gt(supply.balance) ? supply.balance : withdrawable + setVal(getDisplayBalance(max)) }, [formattedSupplied]) const hideModal = useCallback(() => { From 35864d76adb62f3107d3cf42498255ff5182c5f5 Mon Sep 17 00:00:00 2001 From: Daizze Date: Tue, 12 Nov 2024 23:48:17 +0100 Subject: [PATCH 23/27] Adjustments front end in modals --- src/hooks/lend/useAccountLiquidity.ts | 2 +- src/hooks/lend/useComptrollerData.ts | 4 +- src/hooks/lend/useOraclePrices.ts | 2 +- .../lend/components/Modals/BorrowModal.tsx | 28 ++++++----- .../lend/components/Modals/RepayModal.tsx | 26 +++++----- .../lend/components/Modals/SupplyModal.tsx | 40 ++++++++-------- .../lend/components/Modals/WithdrawModal.tsx | 48 +++++++++++-------- 7 files changed, 82 insertions(+), 68 deletions(-) diff --git a/src/hooks/lend/useAccountLiquidity.ts b/src/hooks/lend/useAccountLiquidity.ts index 440d0ff2..42167a6a 100644 --- a/src/hooks/lend/useAccountLiquidity.ts +++ b/src/hooks/lend/useAccountLiquidity.ts @@ -74,7 +74,7 @@ export const useAccountLiquidity = (marketName: string, supplyBalances: Balance[ netApy, supply, borrow, - borrowable: compAccountLiquidity[1], + borrowable: BigNumber.from(compAccountLiquidity[1]), } }, { diff --git a/src/hooks/lend/useComptrollerData.ts b/src/hooks/lend/useComptrollerData.ts index 95375133..0ae2711e 100644 --- a/src/hooks/lend/useComptrollerData.ts +++ b/src/hooks/lend/useComptrollerData.ts @@ -36,8 +36,8 @@ export const useComptrollerData = (marketName: string): ComptrollerData[] => { return Object.entries(multicallResults.results).map(([address, data]) => ({ address, - collateralFactor: data.callsReturnContext.find(call => call.reference === 'markets')?.returnValues[1], - imfFactor: data.callsReturnContext.find(call => call.reference === 'markets')?.returnValues[2], + collateralFactor: BigNumber.from(data.callsReturnContext.find(call => call.reference === 'markets')?.returnValues[1]), + imfFactor: BigNumber.from(data.callsReturnContext.find(call => call.reference === 'markets')?.returnValues[2]), })) }, diff --git a/src/hooks/lend/useOraclePrices.ts b/src/hooks/lend/useOraclePrices.ts index a4d47766..c6fdcaa0 100644 --- a/src/hooks/lend/useOraclePrices.ts +++ b/src/hooks/lend/useOraclePrices.ts @@ -39,7 +39,7 @@ export const useOraclePrices = (marketName: string): { [p: string]: BigNumber } return Object.fromEntries( Object.entries(multicallResults.results).map(([address, result]) => [ address, - result.callsReturnContext.find(call => call.reference === 'getUnderlyingPrice')?.returnValues[0], + BigNumber.from(result.callsReturnContext.find(call => call.reference === 'getUnderlyingPrice')?.returnValues[0]), ]), ) }, diff --git a/src/pages/lend/components/Modals/BorrowModal.tsx b/src/pages/lend/components/Modals/BorrowModal.tsx index b022e041..9a3eceba 100644 --- a/src/pages/lend/components/Modals/BorrowModal.tsx +++ b/src/pages/lend/components/Modals/BorrowModal.tsx @@ -25,7 +25,7 @@ export type BorrowModalProps = { const BorrowModal = ({ asset, show, onHide, marketName }: BorrowModalProps) => { const { chainId } = useWeb3React() - const [val, setVal] = useState('0') + const [val, setVal] = useState(BigNumber.from(0)) const activeLendMarket = useActiveLendMarket(asset) const supplyBalances = useSupplyBalances(marketName) const borrowBalances = useBorrowBalances(marketName) @@ -39,7 +39,7 @@ const BorrowModal = ({ asset, show, onHide, marketName }: BorrowModalProps) => { const handleChange = useCallback( (e: React.FormEvent) => { - setVal(e.currentTarget.value) + setVal(BigNumber.from(e.currentTarget.value)) }, [setVal], ) @@ -58,8 +58,16 @@ const BorrowModal = ({ asset, show, onHide, marketName }: BorrowModalProps) => { }, [borrowable]) const handleSelectMax = useCallback(() => { - setVal(formattedBorrowable || '0.00') - }, [formattedBorrowable]) + setVal(borrowable) + }, [borrowable]) + + const formattedVal = useMemo(() => { + return getDisplayBalance(val) + }, [val]) + + const disabled = useMemo(() => { + return val.eq(BigNumber.from(0)) + }, [val]) return ( @@ -89,8 +97,8 @@ const BorrowModal = ({ asset, show, onHide, marketName }: BorrowModalProps) => { @@ -98,13 +106,7 @@ const BorrowModal = ({ asset, show, onHide, marketName }: BorrowModalProps) => {
- + ) diff --git a/src/pages/lend/components/Modals/RepayModal.tsx b/src/pages/lend/components/Modals/RepayModal.tsx index 8ef487f0..8422850d 100644 --- a/src/pages/lend/components/Modals/RepayModal.tsx +++ b/src/pages/lend/components/Modals/RepayModal.tsx @@ -45,7 +45,7 @@ const RepayModal = ({ asset, show, onHide, marketName }: RepayModalProps) => { return maxRepay ? getDisplayBalance(maxRepay) : null }, [maxRepay]) - const [val, setVal] = useState('0') + const [val, setVal] = useState(BigNumber.from(0)) const hideModal = useCallback(() => { onHide() @@ -53,14 +53,22 @@ const RepayModal = ({ asset, show, onHide, marketName }: RepayModalProps) => { const handleChange = useCallback( (e: React.FormEvent) => { - setVal(e.currentTarget.value) + setVal(BigNumber.from(e.currentTarget.value)) }, [setVal], ) const handleSelectMax = useCallback(() => { - setVal(maxRepayFormatted || '0.00') - }, [maxRepayFormatted]) + setVal(maxRepay) + }, [maxRepay]) + + const formattedVal = useMemo(() => { + return getDisplayBalance(val) + }, [val]) + + const disabled = useMemo(() => { + return val.eq(BigNumber.from(0)) + }, [val]) return ( <> @@ -92,7 +100,7 @@ const RepayModal = ({ asset, show, onHide, marketName }: RepayModalProps) => { { - + diff --git a/src/pages/lend/components/Modals/SupplyModal.tsx b/src/pages/lend/components/Modals/SupplyModal.tsx index 657026b5..50d89671 100644 --- a/src/pages/lend/components/Modals/SupplyModal.tsx +++ b/src/pages/lend/components/Modals/SupplyModal.tsx @@ -3,7 +3,7 @@ import { ActiveSupportedVault, Asset, Balance } from '@/bao/lib/types' import Modal from '@/components/Modal' import { BigNumber } from 'ethers' import { parseUnits } from 'ethers/lib/utils' -import React, { useCallback, useEffect, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import Input from '@/components/Input' import Typography from '@/components/Typography' import SupplyButton from '@/pages/lend/components/Buttons/SupplyButton' @@ -21,30 +21,36 @@ export type SupplyModalProps = { } const SupplyModal = ({ asset, show, onHide, marketName, fullBalance }: SupplyModalProps) => { - const [val, setVal] = useState('0') + const [val, setVal] = useState(BigNumber.from(0)) const operation = 'Supply' - const [formattedBalance, setFormattedBalance] = useState('0.00') const handleChange = useCallback( (e: React.FormEvent) => { - setVal(e.currentTarget.value) + setVal(BigNumber.from(e.currentTarget.value)) }, [setVal], ) + const formattedBalance = useMemo(() => { + if (!fullBalance) return '0' + return getDisplayBalance(fullBalance) + }, [fullBalance]) + const handleSelectMax = useCallback(() => { - if (!formattedBalance) return setVal('0') - setVal(formattedBalance) - }, [formattedBalance, setVal]) + setVal(fullBalance) + }, [fullBalance, setVal]) const hideModal = useCallback(() => { onHide() }, [onHide]) - useEffect(() => { - if (!fullBalance) return - setFormattedBalance(getDisplayBalance(fullBalance, 18)) - }, [fullBalance]) + const formattedVal = useMemo(() => { + return getDisplayBalance(val) + }, [val]) + + const disabled = useMemo(() => { + return val.eq(BigNumber.from(0)) + }, [val]) return ( {}}> @@ -74,8 +80,8 @@ const SupplyModal = ({ asset, show, onHide, marketName, fullBalance }: SupplyMod @@ -83,13 +89,7 @@ const SupplyModal = ({ asset, show, onHide, marketName, fullBalance }: SupplyMod - + ) diff --git a/src/pages/lend/components/Modals/WithdrawModal.tsx b/src/pages/lend/components/Modals/WithdrawModal.tsx index 090f7149..e9d5a798 100644 --- a/src/pages/lend/components/Modals/WithdrawModal.tsx +++ b/src/pages/lend/components/Modals/WithdrawModal.tsx @@ -31,7 +31,7 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) const supplyBalances = useSupplyBalances(marketName) const borrowBalances = useBorrowBalances(marketName) const { exchangeRates } = useExchangeRates(marketName) - const [val, setVal] = useState('0') + const [val, setVal] = useState(BigNumber.from(0)) const operation = 'Withdraw' const comptrollerData = useComptrollerData(marketName) const accountLiquidity = useAccountLiquidity(marketName, supplyBalances, borrowBalances) @@ -39,7 +39,7 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) const handleChange = useCallback( (e: React.FormEvent) => { - setVal(e.currentTarget.value) + setVal(BigNumber.from(e.currentTarget.value)) }, [setVal], ) @@ -54,11 +54,11 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) }, [supply, asset, chainId]) const assetComptrollerData = useMemo(() => { - return comptrollerData?.find(data => data.address === asset.marketAddress) || null + return comptrollerData?.find(data => data.address === asset.marketAddress[chainId]) || null }, [comptrollerData, asset]) const imfFactor = useMemo(() => { - if (!assetComptrollerData || !accountLiquidity) return null + if (!assetComptrollerData || !accountLiquidity || !supply) return null let imfFactor = assetComptrollerData.imfFactor @@ -66,10 +66,10 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) const sqrtBalance = sqrt(supply.balance) const num = exponentiate(parseUnits('1.1')) const denom = decimate(imfFactor.mul(sqrtBalance).add(parseUnits('1'))) - imfFactor = num.div(denom) + return num.div(denom) } return imfFactor - }, [assetComptrollerData, accountLiquidity]) + }, [supply, assetComptrollerData, accountLiquidity]) const price = useMemo(() => { return prices?.[asset.marketAddress[chainId]] || null @@ -83,22 +83,38 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) if (imfFactor.gt(collateralFactor) && price.gt(0)) { const factor = collateralFactor.mul(price).gt(0) ? decimate(collateralFactor.mul(price)) : decimate(imfFactor).mul(price) - return exponentiate(borrowable).div(factor) } return BigNumber.from(0) }, [assetComptrollerData, accountLiquidity, imfFactor, price]) + const max = useMemo(() => { + if (!accountLiquidity || !withdrawable || !supply) return BigNumber.from(0) + + return !(accountLiquidity && accountLiquidity.borrowable) || withdrawable.gt(supply.balance) ? supply.balance : withdrawable + }, [accountLiquidity, withdrawable, supply]) + + const formattedMax = useMemo(() => { + return getDisplayBalance(max) + }, [max]) + const handleSelectMax = useCallback(() => { - const max = !(accountLiquidity && accountLiquidity.borrowable) || withdrawable.gt(supply.balance) ? supply.balance : withdrawable - setVal(getDisplayBalance(max)) - }, [formattedSupplied]) + setVal(max) + }, [max]) const hideModal = useCallback(() => { onHide() }, [onHide]) + const disabled = useMemo(() => { + return val.eq(BigNumber.from(0)) + }, [val]) + + const formattedVal = useMemo(() => { + return getDisplayBalance(val) + }, [val]) + return ( <> @@ -129,8 +145,8 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) @@ -138,13 +154,7 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) - + From 6a02700c63da8527c4d54a5a14959de6ec1ddd5b Mon Sep 17 00:00:00 2001 From: Daizze Date: Tue, 12 Nov 2024 23:55:15 +0100 Subject: [PATCH 24/27] Reenabling DebtCard --- src/pages/lend/[market].tsx | 5 ++++- src/pages/lend/components/DebtCard.tsx | 18 ++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/pages/lend/[market].tsx b/src/pages/lend/[market].tsx index 07c632a8..c4e30d50 100644 --- a/src/pages/lend/[market].tsx +++ b/src/pages/lend/[market].tsx @@ -17,6 +17,7 @@ import { BigNumber } from 'ethers' import { useTotalDebt } from '@/hooks/lend/useTotalDebt' import { useBorrowApy } from '@/hooks/lend/useBorrowApy' import { useSupplyBalances } from '@/hooks/lend/useSupplyBalances' +import DebtCard from '@/pages/lend/components/DebtCard' export async function getStaticPaths() { const paths: { params: { market: string } }[] = [] @@ -151,7 +152,9 @@ const Market: NextPage<{ -
+
+ +
diff --git a/src/pages/lend/components/DebtCard.tsx b/src/pages/lend/components/DebtCard.tsx index 906bfa4d..6e652bfa 100644 --- a/src/pages/lend/components/DebtCard.tsx +++ b/src/pages/lend/components/DebtCard.tsx @@ -12,31 +12,25 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { useActiveLendMarket } from '@/hooks/lend/useActiveLendMarket' import useHealthFactor from '@/hooks/lend/useHealthFactor' import Config from '@/bao/lib/config' +import { useBorrowBalances } from '@/hooks/lend/useBorrowBalances' +import { useSupplyBalances } from '@/hooks/lend/useSupplyBalances' type DashboardCardProps = { marketName: string - borrowBalances: Balance[] - supplyBalances: Balance[] - mintVal: string - depositVal: string } -const DashboardCard: React.FC = ({ - marketName, - mintVal, - borrowBalances, - supplyBalances, - depositVal, -}: DashboardCardProps) => { +const DashboardCard: React.FC = ({ marketName }: DashboardCardProps) => { const bao = useBao() const { account, chainId } = useWeb3React() const asset = Config.lendMarkets[marketName].assets.find( asset => asset.marketAddress[chainId] === Config.lendMarkets[marketName].marketAddresses[chainId], ) const activeLendMarket = useActiveLendMarket(asset) + const borrowBalances = useBorrowBalances(marketName) + const supplyBalances = useSupplyBalances(marketName) const accountLiquidity = useAccountLiquidity(marketName, supplyBalances, borrowBalances) - const change = mintVal && depositVal ? BigNumber.from(mintVal).sub(BigNumber.from(depositVal)) : BigNumber.from(0) + const change = BigNumber.from(0) const borrow = accountLiquidity ? accountLiquidity.borrow : BigNumber.from(0) const newBorrow = borrow ? borrow.sub(change.gt(0) ? change : 0) : BigNumber.from(0) const borrowable = accountLiquidity ? accountLiquidity.borrow.add(exponentiate(accountLiquidity.borrowable)) : BigNumber.from(0) From 9dcf6629b25c044fcab2afc295164a3785edf871 Mon Sep 17 00:00:00 2001 From: Daizze Date: Thu, 14 Nov 2024 23:18:14 +0100 Subject: [PATCH 25/27] Minor build fix --- src/pages/lend/components/Modals/WithdrawModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/lend/components/Modals/WithdrawModal.tsx b/src/pages/lend/components/Modals/WithdrawModal.tsx index e9d5a798..0f0bc65b 100644 --- a/src/pages/lend/components/Modals/WithdrawModal.tsx +++ b/src/pages/lend/components/Modals/WithdrawModal.tsx @@ -60,7 +60,7 @@ const WithdrawModal = ({ asset, show, onHide, marketName }: WithdrawModalProps) const imfFactor = useMemo(() => { if (!assetComptrollerData || !accountLiquidity || !supply) return null - let imfFactor = assetComptrollerData.imfFactor + const imfFactor = assetComptrollerData.imfFactor if (accountLiquidity) { const sqrtBalance = sqrt(supply.balance) From 5ed31b19c9da04bc5abea955e2bc4dee6fc78841 Mon Sep 17 00:00:00 2001 From: fabiaz84 <79611566+fabiaz84@users.noreply.github.com> Date: Fri, 15 Nov 2024 00:01:15 +0100 Subject: [PATCH 26/27] Update Nav.tsx --- src/components/Nav/Nav.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/Nav/Nav.tsx b/src/components/Nav/Nav.tsx index 283d9eae..33029e76 100644 --- a/src/components/Nav/Nav.tsx +++ b/src/components/Nav/Nav.tsx @@ -20,10 +20,11 @@ const Nav: FC = ({ href, exact }) => { const navigation = [ ['0', 'BORROW', '/vaults'], - ['1', 'SWAP', '/swap'], - ['2', 'EARN', '/earn'], - ['3', 'VEBAO', '/vebao'], - ['4', 'LEND', '/lend'], + ['1', 'STAKE', '/stake'], + ['2', 'SWAP', '/swap'], + ['3', 'EARN', '/earn'], + ['4', 'VEBAO', '/vebao'], + ['5', 'LEND', '/lend'], ] return ( From 9e7e2797ec793c7da757a2a5c52493d21dd01f6a Mon Sep 17 00:00:00 2001 From: fabiaZ84 Date: Fri, 15 Nov 2024 00:14:33 +0100 Subject: [PATCH 27/27] nav fix --- src/components/Nav/Nav.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Nav/Nav.tsx b/src/components/Nav/Nav.tsx index 47294cb4..33029e76 100644 --- a/src/components/Nav/Nav.tsx +++ b/src/components/Nav/Nav.tsx @@ -25,7 +25,6 @@ const Nav: FC = ({ href, exact }) => { ['3', 'EARN', '/earn'], ['4', 'VEBAO', '/vebao'], ['5', 'LEND', '/lend'], - ] return (