From 1a7f557650888df1608bf341d17522f4cad48761 Mon Sep 17 00:00:00 2001 From: bartosz-lipinski <264380+bartosz-lipinski@users.noreply.github.com> Date: Thu, 19 Nov 2020 17:33:02 -0600 Subject: [PATCH] feat: deposit cleanup --- src/App.less | 10 +++ src/components/DepositAdd/index.tsx | 4 +- src/components/DepositInfoLine/index.tsx | 4 +- src/components/Layout/index.tsx | 17 ++++- src/components/ReserveStatus/index.tsx | 45 +++++++++++ src/components/ReserveStatus/style.less | 0 src/components/UserLendingCard/index.tsx | 96 ++++++++++++++++++++++++ src/contexts/lending.tsx | 16 ++-- src/hooks/index.ts | 3 +- src/hooks/useCollateralBalance.ts | 16 ++++ src/hooks/useUserBalance.ts | 12 +-- src/utils/utils.ts | 16 +++- src/views/borrow/index.tsx | 2 +- src/views/dashboard/index.tsx | 2 +- src/views/deposit/add/style.less | 6 +- src/views/deposit/view/index.tsx | 5 +- src/views/deposit/view/item.tsx | 14 ++-- src/views/deposit/view/itemStyle.less | 29 +++++++ src/views/home/index.tsx | 5 +- src/views/home/item.tsx | 3 +- src/views/home/itemStyle.less | 28 +++++++ src/views/reserve/index.tsx | 38 +++++++--- src/views/reserve/style.less | 30 ++++++++ 23 files changed, 348 insertions(+), 53 deletions(-) create mode 100644 src/components/ReserveStatus/index.tsx create mode 100644 src/components/ReserveStatus/style.less create mode 100644 src/components/UserLendingCard/index.tsx create mode 100644 src/hooks/useCollateralBalance.ts create mode 100644 src/views/deposit/view/itemStyle.less create mode 100644 src/views/home/itemStyle.less create mode 100644 src/views/reserve/style.less diff --git a/src/App.less b/src/App.less index aa88bef..f024fcd 100644 --- a/src/App.less +++ b/src/App.less @@ -202,6 +202,16 @@ body { } } +.ant-layout-content { + display: flex; +} + +.flexColumn { + display: flex; + flex-direction: column; + flex: 1; +} + .card-row { box-sizing: border-box; margin: 5px 0px; diff --git a/src/components/DepositAdd/index.tsx b/src/components/DepositAdd/index.tsx index 78dd45d..6a31550 100644 --- a/src/components/DepositAdd/index.tsx +++ b/src/components/DepositAdd/index.tsx @@ -57,10 +57,10 @@ export const DepositAdd = (props: { className?: string, reserve: LendingReserve, const bodyStyle: React.CSSProperties = { display: 'flex', + flex: 1, justifyContent: 'center', - width: '100%', + alignItems: 'center', height: '100%', - alignItems: 'center' }; return diff --git a/src/components/DepositInfoLine/index.tsx b/src/components/DepositInfoLine/index.tsx index 7befe0c..0d23f7e 100644 --- a/src/components/DepositInfoLine/index.tsx +++ b/src/components/DepositInfoLine/index.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useState } from "react"; -import { useLendingReserve, useTokenName, useUserAccounts, useUserBalance } from './../../hooks'; +import { useLendingReserve, useTokenName, useUserAccounts, useUserBalance, useCollateralBalance } from './../../hooks'; import { LendingReserve, LendingReserveParser } from "../../models/lending"; import { TokenIcon } from "../../components/TokenIcon"; import { formatNumber } from "../../utils/utils"; @@ -19,7 +19,7 @@ export const DepositInfoLine = (props: { address: PublicKey }) => { const name = useTokenName(props.reserve.liquidityMint); const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint); - const { balance: collateralBalance } = useUserBalance(props.reserve.collateralMint); + const { balance: collateralBalance } = useCollateralBalance(props.reserve); return
diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index 7dcf401..48ea6dd 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -7,21 +7,25 @@ import { GithubOutlined, BankOutlined, LogoutOutlined, - HomeOutlined + HomeOutlined, + RocketOutlined } from '@ant-design/icons'; import BasicLayout, { DefaultFooter, PageContainer } from '@ant-design/pro-layout'; import { AppBar } from "./../AppBar"; import { Link, useLocation } from "react-router-dom"; +import { useConnectionConfig } from "../../contexts/connection"; export const AppLayout = (props: any) => { + const { env } = useConnectionConfig(); const location = useLocation(); console.log(location.pathname) const paths: { [key: string]: string } = { '/dashboard': '2', '/deposit': '3', - '/borrow': '4' + '/borrow': '4', + '/faucet': '4', }; @@ -84,6 +88,15 @@ export const AppLayout = (props: any) => { Borrow + {env !== "mainnet-beta" && }> + + Faucet + + } }} > diff --git a/src/components/ReserveStatus/index.tsx b/src/components/ReserveStatus/index.tsx new file mode 100644 index 0000000..f71afcd --- /dev/null +++ b/src/components/ReserveStatus/index.tsx @@ -0,0 +1,45 @@ +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { useLendingReserve, useTokenName, useUserAccounts, useUserBalance } from './../../hooks'; +import { LendingReserve, LendingReserveParser } from "../../models/lending"; +import { TokenIcon } from "../../components/TokenIcon"; +import { formatNumber } from "../../utils/utils"; +import { Button, Card } from "antd"; +import { useParams } from "react-router-dom"; +import { cache, useAccount } from "../../contexts/accounts"; +import { NumericInput } from "../../components/Input/numeric"; +import { useConnection } from "../../contexts/connection"; +import { useWallet } from "../../contexts/wallet"; +import { deposit } from './../../actions/deposit'; +import { PublicKey } from "@solana/web3.js"; +import './style.less'; + +export const ReserveStatus = (props: { className?: string, reserve: LendingReserve, address: PublicKey }) => { + const connection = useConnection(); + const { wallet } = useWallet(); + const { id } = useParams<{ id: string }>(); + const [value, setValue] = useState(''); + + const reserve = props.reserve; + const address = props.address; + + const name = useTokenName(reserve?.liquidityMint); + const { balance: tokenBalance, accounts: fromAccounts } = useUserBalance(reserve?.liquidityMint); + + + const bodyStyle: React.CSSProperties = { + display: 'flex', + justifyContent: 'center', + alignItems: 'center' + }; + + return Reserve Status & Configuration + } + bodyStyle={bodyStyle}> + +
+ TODO: Reserve Status - add chart +
+
; +} \ No newline at end of file diff --git a/src/components/ReserveStatus/style.less b/src/components/ReserveStatus/style.less new file mode 100644 index 0000000..e69de29 diff --git a/src/components/UserLendingCard/index.tsx b/src/components/UserLendingCard/index.tsx new file mode 100644 index 0000000..fad6f5b --- /dev/null +++ b/src/components/UserLendingCard/index.tsx @@ -0,0 +1,96 @@ +import React, { useMemo } from "react"; +import { useLendingReserve, useTokenName, useUserAccounts, useUserBalance } from './../../hooks'; +import { LendingReserve, LendingReserveParser } from "../../models/lending"; +import { TokenIcon } from "../../components/TokenIcon"; +import { formatNumber, formatPct, fromLamports } from "../../utils/utils"; +import { Button, Card, Typography } from "antd"; +import { useParams } from "react-router-dom"; +import { useAccount, useMint } from "../../contexts/accounts"; +import { PublicKey } from "@solana/web3.js"; + +const { Text } = Typography; + +export const UserLendingCard = (props: { + className?: string; + reserve: LendingReserve, + address: PublicKey, +}) => { + const reserve = props.reserve; + const name = useTokenName(reserve?.liquidityMint); + const liquidityMint = useMint(props.reserve.liquidityMint); + + const totalLiquidity = fromLamports(props.reserve.totalLiquidity.toNumber(), liquidityMint); + + // TODO: calculate + const borrowed = 0; + const healthFactor = '--'; + const ltv = 0.75; + const available = 0; + + + return + Your Information +
+ }> +

Borrows

+ +
+ + Borrowed + +
+ {formatNumber.format(borrowed)} {name} +
+
+ +
+ + Health factor: + +
+ {healthFactor} +
+
+ +
+ + Loan to value: + +
+ {formatNumber.format(ltv)} +
+
+ +
+ + Available to you: + +
+ {formatNumber.format(available)} {name} +
+
+ +

Deposits

+ +
+ + Wallet balance: + +
+ {formatNumber.format(totalLiquidity)} {name} +
+
+ +
+ + You already deposited: + +
+ {formatNumber.format(totalLiquidity)} {name} +
+
+ + +
; +} \ No newline at end of file diff --git a/src/contexts/lending.tsx b/src/contexts/lending.tsx index 31bb9f0..af2be17 100644 --- a/src/contexts/lending.tsx +++ b/src/contexts/lending.tsx @@ -1,8 +1,8 @@ import React, { useCallback, useContext, useEffect, useState } from "react"; import { useConnection } from "./connection"; import { LENDING_PROGRAM_ID } from "./../constants/ids"; -import { LendingReserveLayout, LendingMarketLayout, LendingMarket, LendingMarketParser, isLendingReserve, isLendingMarket, LendingReserveParser } from "./../models/lending"; -import { cache, getMultipleAccounts } from "./accounts"; +import { LendingReserveLayout, LendingMarketLayout, LendingMarket, LendingMarketParser, isLendingReserve, isLendingMarket, LendingReserveParser, LendingReserve } from "./../models/lending"; +import { cache, getMultipleAccounts, ParsedAccount } from "./accounts"; import { AccountInfo, PublicKey } from "@solana/web3.js"; import { isForInStatement } from "typescript"; @@ -52,14 +52,10 @@ export const useLending = () => { console.log(accounts); - const toQuery = accounts - .map( - (p) => - [ - // TODO: add dependent accounts .... - ].filter((p) => p) as string[] - ) - .flat(); + const toQuery = [ + ...accounts.filter(acc => (acc?.info as LendingReserve).lendingMarket !== undefined) + .map(acc => (acc?.info as LendingReserve).collateralMint.toBase58()) + ].flat().filter((p) => p) as string[]; // This will pre-cache all accounts used by pools // All those accounts are updated whenever there is a change diff --git a/src/hooks/index.ts b/src/hooks/index.ts index a60be1c..089e0b0 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -2,4 +2,5 @@ export * from './useUserAccounts'; export * from './useAccountByMint'; export * from './useLendingReserves'; export * from './useTokenName'; -export * from './useUserBalance'; \ No newline at end of file +export * from './useUserBalance'; +export * from './useCollateralBalance'; diff --git a/src/hooks/useCollateralBalance.ts b/src/hooks/useCollateralBalance.ts new file mode 100644 index 0000000..a290751 --- /dev/null +++ b/src/hooks/useCollateralBalance.ts @@ -0,0 +1,16 @@ +import { PublicKey } from "@solana/web3.js"; +import { useMemo } from "react"; +import { useAccount, useMint } from "../contexts/accounts"; +import { LendingReserve } from "../models/lending"; +import { fromLamports } from "../utils/utils"; +import { useUserAccounts } from "./useUserAccounts"; +import { useUserBalance } from "./useUserBalance"; + +export function useCollateralBalance(reserve?: LendingReserve) { + const mint = useMint(reserve?.collateralMint); + const { balance: nativeBalance, accounts } = useUserBalance(reserve?.collateralMint, true); + + const balance = fromLamports((reserve?.totalLiquidity.toNumber() || 0) * (nativeBalance / (reserve?.collateralMintSupply.toNumber() || 1)), mint); + + return { balance, accounts }; +} \ No newline at end of file diff --git a/src/hooks/useUserBalance.ts b/src/hooks/useUserBalance.ts index d0d9df1..01fdadf 100644 --- a/src/hooks/useUserBalance.ts +++ b/src/hooks/useUserBalance.ts @@ -4,7 +4,7 @@ import { useMint } from "../contexts/accounts"; import { fromLamports } from "../utils/utils"; import { useUserAccounts } from "./useUserAccounts"; -export function useUserBalance(mint?: PublicKey) { +export function useUserBalance(mint?: PublicKey, inLamports = false) { const { userAccounts } = useUserAccounts(); const mintInfo = useMint(mint); const accounts = useMemo(() => { @@ -13,11 +13,11 @@ export function useUserBalance(mint?: PublicKey) { .sort((a, b) => b.info.amount.sub(a.info.amount).toNumber()); }, [userAccounts]); - const balance = useMemo(() => - fromLamports(accounts - .reduce((res, item) => res += item.info.amount.toNumber(), 0) - , mintInfo), - [accounts, mintInfo]); + const balance = useMemo(() => { + const result = accounts + .reduce((res, item) => res += item.info.amount.toNumber(), 0); + return inLamports ? result : fromLamports(result , mintInfo); + },[accounts, mintInfo]); return { balance, accounts }; } \ No newline at end of file diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 9b8f9c1..b9da1bb 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -52,7 +52,7 @@ export function getTokenName( mintAddress?: string, shorten = true ): string { - if(!mintAddress) { + if (!mintAddress) { return 'N/A'; } @@ -69,7 +69,7 @@ export function getTokenIcon( mintAddress?: string | PublicKey, ): string | undefined { const address = typeof mintAddress === 'string' ? mintAddress : mintAddress?.toBase58(); - if(!address) { + if (!address) { return; } @@ -162,12 +162,22 @@ export const formatUSD = new Intl.NumberFormat("en-US", { currency: "USD", }); -export const formatNumber = new Intl.NumberFormat("en-US", { +const numberFormater = new Intl.NumberFormat("en-US", { style: "decimal", minimumFractionDigits: 2, maximumFractionDigits: 2, }); +export const formatNumber = { + format: (val?: number) => { + if (!val) { + return '--'; + } + + return numberFormater.format(val); + } +} + export const formatPct = new Intl.NumberFormat("en-US", { style: "percent", minimumFractionDigits: 2, diff --git a/src/views/borrow/index.tsx b/src/views/borrow/index.tsx index 1573056..f7ab5ea 100644 --- a/src/views/borrow/index.tsx +++ b/src/views/borrow/index.tsx @@ -7,7 +7,7 @@ import { Button } from "antd"; export const BorrowView = () => { - return
+ return
Borrow
; } \ No newline at end of file diff --git a/src/views/dashboard/index.tsx b/src/views/dashboard/index.tsx index 129b524..584fef2 100644 --- a/src/views/dashboard/index.tsx +++ b/src/views/dashboard/index.tsx @@ -8,7 +8,7 @@ import { Button } from "antd"; export const DashboardView = () => { const { reserveAccounts } = useLendingReserves(); - return
+ return
DASHBOARD: TODO: 1. Add deposits diff --git a/src/views/deposit/add/style.less b/src/views/deposit/add/style.less index 98ea34b..9bd6390 100644 --- a/src/views/deposit/add/style.less +++ b/src/views/deposit/add/style.less @@ -1,8 +1,7 @@ .deposit-add { display: flex; flex-direction: column; - height: 100%; - width: 100%; + flex: 1; } .deposit-add-item { @@ -12,8 +11,7 @@ .deposit-add-container { display: flex; flex-wrap: wrap; - height: 100%; - width: 100%; + flex: 1; } .deposit-add-item-left { diff --git a/src/views/deposit/view/index.tsx b/src/views/deposit/view/index.tsx index ccef31d..186e13f 100644 --- a/src/views/deposit/view/index.tsx +++ b/src/views/deposit/view/index.tsx @@ -6,12 +6,13 @@ import { getTokenName } from "../../../utils/utils"; import { useConnectionConfig } from "../../../contexts/connection"; import { TokenIcon } from "../../../components/TokenIcon"; import { ReserveItem } from './item'; +import './itemStyle.less'; export const DepositView = () => { const { reserveAccounts } = useLendingReserves(); return ( -
-
+
+
Asset
Your wallet balance
Your balance in Oyster
diff --git a/src/views/deposit/view/item.tsx b/src/views/deposit/view/item.tsx index 1480a20..3fdd42e 100644 --- a/src/views/deposit/view/item.tsx +++ b/src/views/deposit/view/item.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from "react"; -import { useTokenName, useUserAccounts, useUserBalance } from '../../../hooks'; +import { useCollateralBalance, useTokenName, useUserAccounts, useUserBalance } from '../../../hooks'; import { LendingReserve } from "../../../models/lending"; import { TokenIcon } from "../../../components/TokenIcon"; import { formatNumber } from "../../../utils/utils"; @@ -10,18 +10,20 @@ import { PublicKey } from "@solana/web3.js"; export const ReserveItem = (props: { reserve: LendingReserve, address: PublicKey }) => { const name = useTokenName(props.reserve.liquidityMint); const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint); - const { balance: collateralBalance } = useUserBalance(props.reserve.collateralMint); + const { balance: collateralBalance } = useCollateralBalance(props.reserve); return -
+
{name}
{formatNumber.format(tokenBalance)} {name}
{formatNumber.format(collateralBalance)} {name}
--
- +
+ +
; diff --git a/src/views/deposit/view/itemStyle.less b/src/views/deposit/view/itemStyle.less new file mode 100644 index 0000000..ce6197b --- /dev/null +++ b/src/views/deposit/view/itemStyle.less @@ -0,0 +1,29 @@ +.deposit-item { + display: flex; + justify-content: space-between; + align-items: center; + + & > div, span { + flex: 20%; + height: 22px; + text-align: right; + } + + & > :first-child { + flex: 80px + } +} + +.deposit-header { + margin: 0px 30px; + + & > div { + flex: 20%; + text-align: right; + } + + & > :first-child { + text-align: left; + flex: 80px + } +} \ No newline at end of file diff --git a/src/views/home/index.tsx b/src/views/home/index.tsx index 3aa9d89..cc965ca 100644 --- a/src/views/home/index.tsx +++ b/src/views/home/index.tsx @@ -5,14 +5,15 @@ import { TokenIcon } from "../../components/TokenIcon"; import { formatNumber } from "../../utils/utils"; import { Button } from "antd"; import { LendingReserveItem } from "./item"; +import './itemStyle.less'; export const HomeView = () => { const { reserveAccounts } = useLendingReserves(); // TODO: add total Liquidity amount ... - return
-
+ return
+
Asset
Market Size
Total Borrowed
diff --git a/src/views/home/item.tsx b/src/views/home/item.tsx index 236ace2..17261dc 100644 --- a/src/views/home/item.tsx +++ b/src/views/home/item.tsx @@ -24,11 +24,12 @@ export const LendingReserveItem = (props: { reserve: LendingReserve, address: Pu return -
+
{name}
{formatNumber.format(totalLiquidity)} {name}
{totalBorrows} {name}
--
+
--
diff --git a/src/views/home/itemStyle.less b/src/views/home/itemStyle.less new file mode 100644 index 0000000..9a76f13 --- /dev/null +++ b/src/views/home/itemStyle.less @@ -0,0 +1,28 @@ +.home-item { + display: flex; + justify-content: space-between; + align-items: center; + + & > div, span { + flex: 20%; + text-align: right; + } + + & > :first-child { + flex: 80px + } +} + +.home-header { + margin: 0px 30px; + + & > div { + flex: 20%; + text-align: right; + } + + & > :first-child { + text-align: left; + flex: 80px + } +} \ No newline at end of file diff --git a/src/views/reserve/index.tsx b/src/views/reserve/index.tsx index 945a8f8..1f9b118 100644 --- a/src/views/reserve/index.tsx +++ b/src/views/reserve/index.tsx @@ -1,24 +1,42 @@ -import React, { useMemo } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { useLendingReserve, useTokenName, useUserAccounts, useUserBalance } from './../../hooks'; import { LendingReserve, LendingReserveParser } from "../../models/lending"; import { TokenIcon } from "../../components/TokenIcon"; import { formatNumber } from "../../utils/utils"; import { Button, Card } from "antd"; import { useParams } from "react-router-dom"; -import { useAccount } from "../../contexts/accounts"; +import { cache, useAccount } from "../../contexts/accounts"; +import { NumericInput } from "../../components/Input/numeric"; +import { useConnection } from "../../contexts/connection"; +import { useWallet } from "../../contexts/wallet"; +import { deposit } from './../../actions/deposit'; +import './style.less'; + +import { DepositAdd } from './../../components/DepositAdd'; +import { UserLendingCard } from './../../components/UserLendingCard'; +import { ReserveStatus } from './../../components/ReserveStatus'; export const ReserveView = () => { + const connection = useConnection(); + const { wallet } = useWallet(); const { id } = useParams<{ id: string }>(); - const lendingReserve = useLendingReserve(id); const reserve = lendingReserve?.info; - const name = useTokenName(reserve?.liquidityMint); - const { balance: tokenBalance } = useUserBalance(reserve?.liquidityMint); - const { balance: collateralBalance } = useUserBalance(reserve?.collateralMint); + if (!reserve || !lendingReserve) { + return null; + } - return - - - ; + return
+
+ + +
+
; } \ No newline at end of file diff --git a/src/views/reserve/style.less b/src/views/reserve/style.less new file mode 100644 index 0000000..75c6bd8 --- /dev/null +++ b/src/views/reserve/style.less @@ -0,0 +1,30 @@ +.reserve-overview { + display: flex; + flex-direction: column; + flex: 1; +} + +.reserve-overview-item { + margin: 4px; +} + +.reserve-overview-container { + display: flex; + flex-wrap: wrap; + flex: 1; +} + +.reserve-overview-item-left { + flex: 60%; +} + +.reserve-overview-item-right { + flex: 30%; +} + +/* Responsive layout - makes a one column layout instead of a two-column layout */ +@media (max-width: 600px) { + .reserve-overview-item-right, .reserve-overview-item-left { + flex: 100%; + } +} \ No newline at end of file