feat: deposit cleanup

This commit is contained in:
bartosz-lipinski 2020-11-19 17:33:02 -06:00
parent b4a2e4c564
commit 1a7f557650
23 changed files with 348 additions and 53 deletions

View File

@ -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;

View File

@ -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 <Card className={props.className} bodyStyle={bodyStyle}>

View File

@ -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 <Card className={props.className} bodyStyle={{ display: 'flex', justifyContent: 'space-around', }} >
<div className="deposit-info-line-item ">

View File

@ -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
</Link>
</Menu.Item>
{env !== "mainnet-beta" && <Menu.Item key="5" icon={<RocketOutlined />}>
<Link
to={{
pathname: "/faucet",
}}
>
Faucet
</Link>
</Menu.Item>}
</Menu>
}}
>

View File

@ -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 <Card className={props.className}
title={
<>Reserve Status &amp; Configuration</>
}
bodyStyle={bodyStyle}>
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}>
TODO: Reserve Status - add chart
</div>
</Card >;
}

View File

View File

@ -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 <Card className={props.className} title={
<div style={{ display: 'flex', alignItems: 'center', fontSize: '1.2rem', justifyContent: 'center' }}>
Your Information
</div>
}>
<h3>Borrows</h3>
<div className="card-row">
<Text type="secondary" className="card-cell ">
Borrowed
</Text>
<div className="card-cell ">
{formatNumber.format(borrowed)} {name}
</div>
</div>
<div className="card-row">
<Text type="secondary" className="card-cell ">
Health factor:
</Text>
<div className="card-cell ">
{healthFactor}
</div>
</div>
<div className="card-row">
<Text type="secondary" className="card-cell ">
Loan to value:
</Text>
<div className="card-cell ">
{formatNumber.format(ltv)}
</div>
</div>
<div className="card-row">
<Text type="secondary" className="card-cell ">
Available to you:
</Text>
<div className="card-cell ">
{formatNumber.format(available)} {name}
</div>
</div>
<h3>Deposits</h3>
<div className="card-row">
<Text type="secondary" className="card-cell ">
Wallet balance:
</Text>
<div className="card-cell ">
{formatNumber.format(totalLiquidity)} {name}
</div>
</div>
<div className="card-row">
<Text type="secondary" className="card-cell ">
You already deposited:
</Text>
<div className="card-cell ">
{formatNumber.format(totalLiquidity)} {name}
</div>
</div>
</Card>;
}

View File

@ -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

View File

@ -2,4 +2,5 @@ export * from './useUserAccounts';
export * from './useAccountByMint';
export * from './useLendingReserves';
export * from './useTokenName';
export * from './useUserBalance';
export * from './useUserBalance';
export * from './useCollateralBalance';

View File

@ -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 };
}

View File

@ -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 };
}

View File

@ -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,

View File

@ -7,7 +7,7 @@ import { Button } from "antd";
export const BorrowView = () => {
return <div style={{ display: 'flex', justifyContent: 'space-around' }}>
return <div className="flexColumn">
Borrow
</div>;
}

View File

@ -8,7 +8,7 @@ import { Button } from "antd";
export const DashboardView = () => {
const { reserveAccounts } = useLendingReserves();
return <div>
return <div className="flexColumn">
DASHBOARD:
TODO:
1. Add deposits

View File

@ -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 {

View File

@ -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 (
<div>
<div style={{ display: 'flex', justifyContent: 'space-around' }}>
<div className="flexColumn">
<div className="deposit-item deposit-header">
<div>Asset</div>
<div>Your wallet balance</div>
<div>Your balance in Oyster</div>

View File

@ -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 <Link to={`/deposit/${props.address.toBase58()}`}>
<Card>
<div style={{ display: 'flex', justifyContent: 'space-around', alignItems: 'center' }}>
<div className="deposit-item">
<span style={{ display: 'flex' }}><TokenIcon mintAddress={props.reserve.liquidityMint} />{name}</span>
<div>{formatNumber.format(tokenBalance)} {name}</div>
<div>{formatNumber.format(collateralBalance)} {name}</div>
<div>--</div>
<Button>
<span>Deposit</span>
</Button>
<div>
<Button>
<span>Deposit</span>
</Button>
</div>
</div>
</Card>
</Link>;

View File

@ -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
}
}

View File

@ -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 <div>
<div style={{ display: 'flex', justifyContent: 'space-around' }}>
return <div className="flexColumn">
<div className="home-item home-header">
<div>Asset</div>
<div>Market Size</div>
<div>Total Borrowed</div>

View File

@ -24,11 +24,12 @@ export const LendingReserveItem = (props: { reserve: LendingReserve, address: Pu
return <Link to={`/reserve/${props.address.toBase58()}`}>
<Card>
<div style={{ display: 'flex', justifyContent: 'space-around', alignItems: 'center' }}>
<div className="home-item">
<span style={{ display: 'flex' }}><TokenIcon mintAddress={props.reserve.liquidityMint} />{name}</span>
<div>{formatNumber.format(totalLiquidity)} {name}</div>
<div>{totalBorrows} {name}</div>
<div>--</div>
<div>--</div>
</div>
</Card>

View File

@ -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
}
}

View File

@ -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 <Card>
</Card>;
return <div className="reserve-overview">
<div className="reserve-overview-container">
<ReserveStatus
className="reserve-overview-item reserve-overview-item-left"
reserve={reserve}
address={lendingReserve.pubkey} />
<UserLendingCard
className="reserve-overview-item reserve-overview-item-right"
reserve={reserve}
address={lendingReserve.pubkey} />
</div>
</div>;
}

View File

@ -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%;
}
}