mirror of https://github.com/certusone/oyster.git
feat: add views
This commit is contained in:
parent
2579c4a1bc
commit
856329b151
|
@ -1,4 +1,4 @@
|
|||
@import "~antd/dist/antd.dark.less";
|
||||
@import "~antd/dist/antd.less";
|
||||
@import "./ant-custom.less";
|
||||
|
||||
body {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "~antd/dist/antd.css";
|
||||
@import "~antd/dist/antd.dark.less";
|
||||
@import "~antd/dist/antd.less";
|
||||
@primary-color: #ff00a8;
|
||||
@popover-background: #1a2029;
|
||||
|
|
|
@ -6,7 +6,8 @@ import {
|
|||
PieChartOutlined,
|
||||
GithubOutlined,
|
||||
BankOutlined,
|
||||
LogoutOutlined
|
||||
LogoutOutlined,
|
||||
HomeOutlined
|
||||
} from '@ant-design/icons';
|
||||
|
||||
import BasicLayout, { DefaultFooter, PageContainer } from '@ant-design/pro-layout';
|
||||
|
@ -34,14 +35,41 @@ export const AppLayout = (props: any) => {
|
|||
]}
|
||||
menuContentRender={() => {
|
||||
return <Menu theme="dark" defaultSelectedKeys={['1']} mode="inline">
|
||||
<Menu.Item key="1" icon={<BankOutlined />}>
|
||||
Deposit
|
||||
<Menu.Item key="1" icon={<HomeOutlined />}>
|
||||
<Link
|
||||
to={{
|
||||
pathname: "/",
|
||||
}}
|
||||
>
|
||||
Home
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="2" icon={<LogoutOutlined />}>
|
||||
Borrow
|
||||
</Menu.Item>
|
||||
<Menu.Item key="3" icon={<PieChartOutlined />}>
|
||||
<Menu.Item key="2" icon={<PieChartOutlined />}>
|
||||
<Link
|
||||
to={{
|
||||
pathname: "/dashboard",
|
||||
}}
|
||||
>
|
||||
Dashboard
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="3" icon={<BankOutlined />}>
|
||||
<Link
|
||||
to={{
|
||||
pathname: "/deposit",
|
||||
}}
|
||||
>
|
||||
Deposit
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="4" icon={<LogoutOutlined />}>
|
||||
<Link
|
||||
to={{
|
||||
pathname: "/borrow",
|
||||
}}
|
||||
>
|
||||
Borrow
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
}}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useConnectionConfig } from "../../contexts/connection";
|
|||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export const TokenIcon = (props: {
|
||||
mintAddress: string | PublicKey;
|
||||
mintAddress?: string | PublicKey;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
}) => {
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { PublicKey } from "@solana/web3.js";
|
||||
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
||||
import { LendingReserveParser } from "../models/lending";
|
||||
import { cache } from './../contexts/accounts';
|
||||
import { LendingReserve, LendingReserveParser } from "../models/lending";
|
||||
import { cache, ParsedAccount } from './../contexts/accounts';
|
||||
|
||||
const getLendingReserves = () => {
|
||||
return cache.byParser(LendingReserveParser).map(id => cache.get(id)).filter(acc => acc !== undefined) as any[];
|
||||
};
|
||||
|
||||
export function useLendingReserves() {
|
||||
const [reserveAccounts, setReserveAccounts] = useState<any[]>([]);
|
||||
const [reserveAccounts, setReserveAccounts] = useState<ParsedAccount<LendingReserve>[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setReserveAccounts(getLendingReserves());
|
||||
|
@ -27,3 +28,24 @@ export function useLendingReserves() {
|
|||
reserveAccounts,
|
||||
};
|
||||
}
|
||||
|
||||
export function useLendingReserve(address: string | PublicKey) {
|
||||
const id = typeof address === 'string' ? address : address?.toBase58();
|
||||
const [reserveAccount, setReserveAccount] = useState<ParsedAccount<LendingReserve>>();
|
||||
|
||||
useEffect(() => {
|
||||
setReserveAccount(cache.get(id));
|
||||
|
||||
const dispose = cache.emitter.onCache((args) => {
|
||||
if (args.id === id) {
|
||||
setReserveAccount(cache.get(id));
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
dispose();
|
||||
};
|
||||
}, [setReserveAccount])
|
||||
|
||||
return reserveAccount;
|
||||
}
|
|
@ -2,7 +2,7 @@ import { PublicKey } from "@solana/web3.js";
|
|||
import { useConnectionConfig } from "../contexts/connection";
|
||||
import { getTokenName } from "../utils/utils";
|
||||
|
||||
export function useTokenName(mintAddress: string | PublicKey) {
|
||||
export function useTokenName(mintAddress?: string | PublicKey) {
|
||||
const { tokenMap } = useConnectionConfig();
|
||||
const address = typeof mintAddress === 'string' ? mintAddress : mintAddress?.toBase58();
|
||||
return getTokenName(tokenMap, address);
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useMint } from "../contexts/accounts";
|
|||
import { convert } from "../utils/utils";
|
||||
import { useUserAccounts } from "./useUserAccounts";
|
||||
|
||||
export function useUserBalance(mint: PublicKey) {
|
||||
export function useUserBalance(mint?: PublicKey) {
|
||||
const { userAccounts } = useUserAccounts();
|
||||
|
||||
const mintInfo = useMint(mint);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { HashRouter, Route } from "react-router-dom";
|
||||
import { HashRouter, Route, Switch } from "react-router-dom";
|
||||
import React from "react";
|
||||
import { WalletProvider } from "./contexts/wallet";
|
||||
import { ConnectionProvider } from "./contexts/connection";
|
||||
|
@ -7,7 +7,14 @@ import { MarketProvider } from "./contexts/market";
|
|||
import { LendingProvider } from "./contexts/lending";
|
||||
import { AppLayout } from "./components/Layout";
|
||||
|
||||
import { DepositList } from './views/deposit/list/list';
|
||||
import {
|
||||
HomeView,
|
||||
DepositView,
|
||||
DepositAddView,
|
||||
BorrowView,
|
||||
ReserveView,
|
||||
DashboardView,
|
||||
} from './views';
|
||||
|
||||
export function Routes() {
|
||||
return (
|
||||
|
@ -19,7 +26,14 @@ export function Routes() {
|
|||
<MarketProvider>
|
||||
<LendingProvider>
|
||||
<AppLayout>
|
||||
<Route exact path="/" component={() => <DepositList />} />
|
||||
<Switch>
|
||||
<Route exact path="/" component={() => <HomeView />} />
|
||||
<Route exact path="/dashboard" children={<DashboardView />} />
|
||||
<Route path="/reserve/:id" children={<ReserveView />} />
|
||||
<Route exact path="/deposit" component={() => <DepositView />} />
|
||||
<Route path="/deposit/:id" children={<DepositAddView />} />
|
||||
<Route exact path="/borrow" children={<BorrowView />} />
|
||||
</Switch>
|
||||
</AppLayout>
|
||||
</LendingProvider>
|
||||
</MarketProvider>
|
||||
|
|
|
@ -49,9 +49,13 @@ export function shortenAddress(address: string, chars = 4): string {
|
|||
|
||||
export function getTokenName(
|
||||
map: KnownTokenMap,
|
||||
mintAddress: string,
|
||||
mintAddress?: string,
|
||||
shorten = true
|
||||
): string {
|
||||
if(!mintAddress) {
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
const knownSymbol = map.get(mintAddress)?.tokenSymbol;
|
||||
if (knownSymbol) {
|
||||
return knownSymbol;
|
||||
|
@ -62,9 +66,13 @@ export function getTokenName(
|
|||
|
||||
export function getTokenIcon(
|
||||
map: KnownTokenMap,
|
||||
mintAddress: string | PublicKey,
|
||||
mintAddress?: string | PublicKey,
|
||||
): string | undefined {
|
||||
const address = typeof mintAddress === 'string' ? mintAddress : mintAddress?.toBase58();
|
||||
if(!address) {
|
||||
return;
|
||||
}
|
||||
|
||||
return map.get(address)?.icon;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import React, { useMemo } from "react";
|
||||
import { useTokenName, useUserAccounts, useUserBalance } from '../../hooks';
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import { TokenIcon } from "../../components/TokenIcon";
|
||||
import { formatNumber } from "../../utils/utils";
|
||||
import { Button } from "antd";
|
||||
|
||||
export const BorrowView = () => {
|
||||
|
||||
return <div style={{ display: 'flex', justifyContent: 'space-around' }}>
|
||||
Borrow
|
||||
</div>;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import React, { useMemo } from "react";
|
||||
import { useLendingReserves, useTokenName, useUserAccounts, useUserBalance } from '../../hooks';
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import { TokenIcon } from "../../components/TokenIcon";
|
||||
import { formatNumber } from "../../utils/utils";
|
||||
import { Button } from "antd";
|
||||
|
||||
export const DashboardView = () => {
|
||||
const { reserveAccounts } = useLendingReserves();
|
||||
|
||||
return <div>
|
||||
DASHBOARD
|
||||
</div>;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
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 } from "../../../utils/utils";
|
||||
import { Button } from "antd";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useAccount } from "../../../contexts/accounts";
|
||||
|
||||
export const DepositAddView = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const lendingReserve = useLendingReserve(id);
|
||||
const reserve = lendingReserve?.info;
|
||||
|
||||
const name = useTokenName(reserve?.liquidityMint);
|
||||
const tokenBalance = useUserBalance(reserve?.liquidityMint);
|
||||
const collateralBalance = useUserBalance(reserve?.collateralMint);
|
||||
|
||||
return <div style={{ display: 'flex', justifyContent: 'space-around' }}>
|
||||
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
||||
{name}
|
||||
<div>{formatNumber.format(tokenBalance)} {name}</div>
|
||||
<div>{formatNumber.format(collateralBalance)} {name}</div>
|
||||
<div>--</div>
|
||||
<Button>Deposit</Button>
|
||||
|
||||
ADD: {id}
|
||||
</div>;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
export * from './view';
|
||||
export * from './add';
|
|
@ -1,23 +1,24 @@
|
|||
import React from "react";
|
||||
import { Input } from "antd";
|
||||
import { useLendingReserves, useTokenName } from './../../../hooks';
|
||||
import { useLendingReserves, useTokenName } from '../../../hooks';
|
||||
import { LendingReserve } from "../../../models/lending";
|
||||
import { getTokenName } from "../../../utils/utils";
|
||||
import { useConnectionConfig } from "../../../contexts/connection";
|
||||
import { TokenIcon } from "../../../components/TokenIcon";
|
||||
import { ReserveItem } from './item';
|
||||
|
||||
export const DepositList = () => {
|
||||
export const DepositView = () => {
|
||||
const { reserveAccounts } = useLendingReserves();
|
||||
return (
|
||||
<div>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-around' }}>
|
||||
<div>Asset</div>
|
||||
<div>Your wallet balance</div>
|
||||
<div>Your balance in Oyster</div>
|
||||
<div>APY</div>
|
||||
<div>Action</div>
|
||||
</div>
|
||||
{reserveAccounts.map(account => <ReserveItem reserve={account.info} />)}
|
||||
{reserveAccounts.map(account => <ReserveItem reserve={account.info} address={account.pubkey} />)}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,20 +1,26 @@
|
|||
import React, { useMemo } from "react";
|
||||
import { useTokenName, useUserAccounts, useUserBalance } from './../../../hooks';
|
||||
import { useTokenName, useUserAccounts, useUserBalance } from '../../../hooks';
|
||||
import { LendingReserve } from "../../../models/lending";
|
||||
import { TokenIcon } from "../../../components/TokenIcon";
|
||||
import { formatNumber } from "../../../utils/utils";
|
||||
import { Button } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export const ReserveItem = (props: { reserve: LendingReserve }) => {
|
||||
export const ReserveItem = (props: { reserve: LendingReserve, address: PublicKey }) => {
|
||||
const name = useTokenName(props.reserve.liquidityMint);
|
||||
const tokenBalance = useUserBalance(props.reserve.liquidityMint);
|
||||
const collateralBalance = useUserBalance(props.reserve.collateralMint);
|
||||
|
||||
return <div style={{ display: 'flex', justifyContent: 'space-around' }}>
|
||||
<TokenIcon mintAddress={props.reserve.liquidityMint} />
|
||||
{name}
|
||||
<span><TokenIcon mintAddress={props.reserve.liquidityMint} />{name}</span>
|
||||
<div>{formatNumber.format(tokenBalance)} {name}</div>
|
||||
<div>{formatNumber.format(collateralBalance)} {name}</div>
|
||||
<Button >Deposit</Button>
|
||||
<div>--</div>
|
||||
<Link to={`/deposit/${props.address.toBase58()}`}>
|
||||
<Button>
|
||||
<span>Deposit</span>
|
||||
</Button>
|
||||
</Link>
|
||||
</div>;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import React, { useMemo } from "react";
|
||||
import { useLendingReserves, useTokenName, useUserAccounts, useUserBalance } from '../../hooks';
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import { TokenIcon } from "../../components/TokenIcon";
|
||||
import { formatNumber } from "../../utils/utils";
|
||||
import { Button } from "antd";
|
||||
import { LendingReserveItem } from "./item";
|
||||
|
||||
export const HomeView = () => {
|
||||
const { reserveAccounts } = useLendingReserves();
|
||||
|
||||
// TODO: add total Liquidity amount ...
|
||||
|
||||
return <div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-around' }}>
|
||||
<div>Asset</div>
|
||||
<div>Market Size</div>
|
||||
<div>Total Borrowed</div>
|
||||
<div>Deposit APY</div>
|
||||
<div>Borrow APY</div>
|
||||
</div>
|
||||
{reserveAccounts.map(account => <LendingReserveItem reserve={account.info} address={account.pubkey} />)}
|
||||
</div>;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import React, { useMemo } from "react";
|
||||
import { useTokenName, useUserAccounts, useUserBalance } from '../../hooks';
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import { TokenIcon } from "../../components/TokenIcon";
|
||||
import { formatNumber } from "../../utils/utils";
|
||||
import { Button, Card } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export const LendingReserveItem = (props: { reserve: LendingReserve, address: PublicKey }) => {
|
||||
const name = useTokenName(props.reserve.liquidityMint);
|
||||
const tokenBalance = useUserBalance(props.reserve.liquidityMint);
|
||||
const collateralBalance = useUserBalance(props.reserve.collateralMint);
|
||||
|
||||
return <Card>
|
||||
<Link to={`/reserve/${props.address.toBase58()}`}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-around' }}>
|
||||
<span><TokenIcon mintAddress={props.reserve.liquidityMint} />{name}</span>
|
||||
<div>{formatNumber.format(tokenBalance)} {name}</div>
|
||||
<div>{formatNumber.format(collateralBalance)} {name}</div>
|
||||
<div>--</div>
|
||||
</div>
|
||||
|
||||
</Link>
|
||||
</Card>;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export { HomeView } from './home';
|
||||
export { BorrowView } from './borrow';
|
||||
export { DashboardView } from './dashboard';
|
||||
export { DepositView, DepositAddView } from './deposit';
|
||||
export { ReserveView } from './reserve';
|
|
@ -0,0 +1,30 @@
|
|||
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 } from "../../utils/utils";
|
||||
import { Button } from "antd";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useAccount } from "../../contexts/accounts";
|
||||
|
||||
export const ReserveView = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
|
||||
const lendingReserve = useLendingReserve(id);
|
||||
const reserve = lendingReserve?.info;
|
||||
|
||||
const name = useTokenName(reserve?.liquidityMint);
|
||||
const tokenBalance = useUserBalance(reserve?.liquidityMint);
|
||||
const collateralBalance = useUserBalance(reserve?.collateralMint);
|
||||
|
||||
return <div style={{ display: 'flex', justifyContent: 'space-around' }}>
|
||||
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
||||
{name}
|
||||
<div>{formatNumber.format(tokenBalance)} {name}</div>
|
||||
<div>{formatNumber.format(collateralBalance)} {name}</div>
|
||||
<div>--</div>
|
||||
<Button>Deposit</Button>
|
||||
|
||||
ADD: {id}
|
||||
</div>;
|
||||
}
|
Loading…
Reference in New Issue