mirror of https://github.com/certusone/oyster.git
feat: borrow amount Type
This commit is contained in:
parent
49324447ba
commit
00db4555be
|
@ -23,18 +23,21 @@ import {
|
|||
LendingObligationLayout,
|
||||
borrowInstruction,
|
||||
LendingMarket,
|
||||
BorrowAmountType,
|
||||
LendingObligation,
|
||||
} from "../models";
|
||||
import { toLamports } from "../utils/utils";
|
||||
|
||||
export const borrow = async (
|
||||
from: TokenAccount,
|
||||
amount: number,
|
||||
amountType: BorrowAmountType,
|
||||
|
||||
borrowReserve: LendingReserve,
|
||||
borrowReserveAddress: PublicKey,
|
||||
borrowReserve: ParsedAccount<LendingReserve>,
|
||||
|
||||
depositReserve: LendingReserve,
|
||||
depositReserveAddress: PublicKey,
|
||||
depositReserve: ParsedAccount<LendingReserve>,
|
||||
|
||||
exsistingObligation: ParsedAccount<LendingObligation> | undefined,
|
||||
|
||||
connection: Connection,
|
||||
wallet: any
|
||||
|
@ -82,7 +85,7 @@ export const borrow = async (
|
|||
instructions,
|
||||
cleanupInstructions,
|
||||
accountRentExempt,
|
||||
borrowReserve.liquidityMint,
|
||||
borrowReserve.info.liquidityMint,
|
||||
signers
|
||||
);
|
||||
|
||||
|
@ -108,23 +111,44 @@ export const borrow = async (
|
|||
cleanupInstructions = [];
|
||||
|
||||
const [authority] = await PublicKey.findProgramAddress(
|
||||
[depositReserve.lendingMarket.toBuffer()],
|
||||
[depositReserve.info.lendingMarket.toBuffer()],
|
||||
LENDING_PROGRAM_ID
|
||||
);
|
||||
|
||||
const mint = (await cache.query(
|
||||
connection,
|
||||
depositReserve.collateralMint,
|
||||
MintParser
|
||||
)) as ParsedAccount<MintInfo>;
|
||||
const amountLamports = toLamports(amount, mint?.info);
|
||||
|
||||
|
||||
let amountLamports: number = 0;
|
||||
let fromLamports: number = 0;
|
||||
if(amountType === BorrowAmountType.LiquidityBorrowAmount) {
|
||||
// approve max transfer
|
||||
// TODO: improve contrain by using dex market data
|
||||
const approvedAmount = from.info.amount.toNumber();
|
||||
|
||||
fromLamports = approvedAmount - accountRentExempt;
|
||||
|
||||
const mint = (await cache.query(
|
||||
connection,
|
||||
borrowReserve.info.liquidityMint,
|
||||
MintParser
|
||||
)) as ParsedAccount<MintInfo>;
|
||||
|
||||
amountLamports = toLamports(amount, mint?.info);
|
||||
} else if(amountType === BorrowAmountType.CollateralDepositAmount) {
|
||||
const mint = (await cache.query(
|
||||
connection,
|
||||
depositReserve.info.collateralMint,
|
||||
MintParser
|
||||
)) as ParsedAccount<MintInfo>;
|
||||
amountLamports = toLamports(amount, mint?.info);
|
||||
fromLamports = amountLamports;
|
||||
}
|
||||
|
||||
const fromAccount = ensureSplAccount(
|
||||
instructions,
|
||||
cleanupInstructions,
|
||||
from,
|
||||
wallet.publicKey,
|
||||
amountLamports + accountRentExempt,
|
||||
fromLamports + accountRentExempt,
|
||||
signers
|
||||
);
|
||||
|
||||
|
@ -136,17 +160,17 @@ export const borrow = async (
|
|||
authority,
|
||||
wallet.publicKey,
|
||||
[],
|
||||
amountLamports
|
||||
fromLamports
|
||||
)
|
||||
);
|
||||
|
||||
const market = cache.get(depositReserve.lendingMarket) as ParsedAccount<
|
||||
const market = cache.get(depositReserve.info.lendingMarket) as ParsedAccount<
|
||||
LendingMarket
|
||||
>;
|
||||
|
||||
const dexMarketAddress = borrowReserve.dexMarketOption
|
||||
? borrowReserve.dexMarket
|
||||
: depositReserve.dexMarket;
|
||||
const dexMarketAddress = borrowReserve.info.dexMarketOption
|
||||
? borrowReserve.info.dexMarket
|
||||
: depositReserve.info.dexMarket;
|
||||
const dexMarket = cache.get(dexMarketAddress);
|
||||
|
||||
if (!dexMarket) {
|
||||
|
@ -154,7 +178,7 @@ export const borrow = async (
|
|||
}
|
||||
|
||||
const dexOrderBookSide = market.info.quoteMint.equals(
|
||||
depositReserve.liquidityMint
|
||||
depositReserve.info.liquidityMint
|
||||
)
|
||||
? dexMarket?.info.bids
|
||||
: dexMarket?.info.asks;
|
||||
|
@ -169,12 +193,13 @@ export const borrow = async (
|
|||
instructions.push(
|
||||
borrowInstruction(
|
||||
amountLamports,
|
||||
amountType,
|
||||
fromAccount,
|
||||
toAccount,
|
||||
depositReserveAddress,
|
||||
depositReserve.collateralSupply,
|
||||
borrowReserveAddress,
|
||||
borrowReserve.liquiditySupply,
|
||||
depositReserve.pubkey,
|
||||
depositReserve.info.collateralSupply,
|
||||
borrowReserve.pubkey,
|
||||
borrowReserve.info.liquiditySupply,
|
||||
|
||||
obligation,
|
||||
obligationMint,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { useTokenName, useUserBalance } from "../../hooks";
|
||||
import { LendingReserve, LendingReserveParser } from "../../models";
|
||||
import { BorrowAmountType, LendingReserve, LendingReserveParser } from "../../models";
|
||||
import { TokenIcon } from "../TokenIcon";
|
||||
import { Button, Card } from "antd";
|
||||
import { cache, ParsedAccount } from "../../contexts/accounts";
|
||||
|
@ -15,15 +15,13 @@ import { LABELS } from "../../constants";
|
|||
|
||||
export const BorrowInput = (props: {
|
||||
className?: string;
|
||||
reserve: LendingReserve;
|
||||
address: PublicKey;
|
||||
reserve: ParsedAccount<LendingReserve>;
|
||||
}) => {
|
||||
const connection = useConnection();
|
||||
const { wallet } = useWallet();
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
const borrowReserve = props.reserve;
|
||||
const borrowReserveAddress = props.address;
|
||||
|
||||
const [collateralReserveMint, setCollateralReserveMint] = useState<string>();
|
||||
|
||||
|
@ -36,7 +34,7 @@ export const BorrowInput = (props: {
|
|||
return cache.get(id) as ParsedAccount<LendingReserve>;
|
||||
}, [collateralReserveMint]);
|
||||
|
||||
const name = useTokenName(borrowReserve?.liquidityMint);
|
||||
const name = useTokenName(borrowReserve?.info.liquidityMint);
|
||||
const { accounts: fromAccounts } = useUserBalance(
|
||||
collateralReserve?.info.collateralMint
|
||||
);
|
||||
|
@ -50,10 +48,11 @@ export const BorrowInput = (props: {
|
|||
borrow(
|
||||
fromAccounts[0],
|
||||
parseFloat(value),
|
||||
// TODO: switch to collateral when user is using slider
|
||||
BorrowAmountType.LiquidityBorrowAmount,
|
||||
borrowReserve,
|
||||
borrowReserveAddress,
|
||||
collateralReserve.info,
|
||||
collateralReserve.pubkey,
|
||||
collateralReserve,
|
||||
undefined,
|
||||
connection,
|
||||
wallet
|
||||
);
|
||||
|
@ -64,7 +63,6 @@ export const BorrowInput = (props: {
|
|||
collateralReserve,
|
||||
borrowReserve,
|
||||
fromAccounts,
|
||||
borrowReserveAddress,
|
||||
]);
|
||||
|
||||
const bodyStyle: React.CSSProperties = {
|
||||
|
@ -86,7 +84,7 @@ export const BorrowInput = (props: {
|
|||
>
|
||||
<div className="borrow-input-title">{LABELS.BORROW_QUESTION}</div>
|
||||
<div className="token-input">
|
||||
<TokenIcon mintAddress={borrowReserve?.liquidityMint} />
|
||||
<TokenIcon mintAddress={borrowReserve?.info.liquidityMint} />
|
||||
<NumericInput
|
||||
value={value}
|
||||
onChange={(val: any) => {
|
||||
|
@ -105,7 +103,7 @@ export const BorrowInput = (props: {
|
|||
</div>
|
||||
<div className="borrow-input-title">{LABELS.SELECT_COLLATERAL}</div>
|
||||
<CollateralSelector
|
||||
reserve={borrowReserve}
|
||||
reserve={borrowReserve.info}
|
||||
mint={collateralReserveMint}
|
||||
onMintChange={setCollateralReserveMint}
|
||||
/>
|
||||
|
|
|
@ -20,13 +20,13 @@ export const ReserveUtilizationChart = (props: {
|
|||
|
||||
const liquidityMint = useMint(props.reserve.liquidityMint);
|
||||
const avilableLiquidity = fromLamports(
|
||||
props.reserve.totalLiquidity.toNumber(),
|
||||
props.reserve.availableLiqudity.toNumber(),
|
||||
liquidityMint
|
||||
);
|
||||
|
||||
const totalBorrows = useMemo(
|
||||
() =>
|
||||
fromLamports(wadToLamports(props.reserve.totalBorrowsWad), liquidityMint),
|
||||
fromLamports(wadToLamports(props.reserve.borrowedLiquidityWad), liquidityMint),
|
||||
[props.reserve, liquidityMint]
|
||||
);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { LendingReserve } from "../../models/lending";
|
|||
import { TokenIcon } from "../../components/TokenIcon";
|
||||
import { formatNumber, formatPct, fromLamports } from "../../utils/utils";
|
||||
import { Card, Typography } from "antd";
|
||||
import { useMint } from "../../contexts/accounts";
|
||||
import { ParsedAccount, useMint } from "../../contexts/accounts";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
|
@ -17,17 +17,16 @@ export enum SideReserveOverviewMode {
|
|||
|
||||
export const SideReserveOverview = (props: {
|
||||
className?: string;
|
||||
reserve: LendingReserve;
|
||||
address: PublicKey;
|
||||
reserve: ParsedAccount<LendingReserve>;
|
||||
mode: SideReserveOverviewMode;
|
||||
}) => {
|
||||
const reserve = props.reserve;
|
||||
const reserve = props.reserve.info;
|
||||
const mode = props.mode;
|
||||
const name = useTokenName(reserve?.liquidityMint);
|
||||
const liquidityMint = useMint(props.reserve.liquidityMint);
|
||||
const liquidityMint = useMint(reserve.liquidityMint);
|
||||
|
||||
const totalLiquidity = fromLamports(
|
||||
props.reserve.totalLiquidity.toNumber(),
|
||||
const availableLiqudity = fromLamports(
|
||||
reserve.availableLiqudity.toNumber(),
|
||||
liquidityMint
|
||||
);
|
||||
|
||||
|
@ -102,7 +101,7 @@ export const SideReserveOverview = (props: {
|
|||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Link to={`/reserve/${props.address}`}>
|
||||
<Link to={`/reserve/${props.reserve.pubkey}`}>
|
||||
<TokenIcon
|
||||
mintAddress={reserve?.liquidityMint}
|
||||
style={{ width: 30, height: 30 }}
|
||||
|
@ -124,7 +123,7 @@ export const SideReserveOverview = (props: {
|
|||
Available liquidity:
|
||||
</Text>
|
||||
<div className="card-cell ">
|
||||
{formatNumber.format(totalLiquidity)} {name}
|
||||
{formatNumber.format(availableLiqudity)} {name}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -21,9 +21,14 @@ export const LABELS = {
|
|||
BORROW_QUESTION: "How much would you like to borrow?",
|
||||
BORROW_ACTION: "Borrow",
|
||||
TABLE_TITLE_ASSET: "Asset",
|
||||
TABLE_TITLE_LOAN_BALANCE: "Your loan balan",
|
||||
TABLE_TITLE_LOAN_BALANCE: "Your loan balance",
|
||||
TABLE_TITLE_DEPOSIT_BALANCE: "Your deposit balane",
|
||||
TABLE_TITLE_APY: "APY",
|
||||
TABLE_TITLE_APR: "APR",
|
||||
TABLE_TITLE_BORROW_APR: "Borrow APR",
|
||||
TABLE_TITLE_DEPOSIT_APR: "Deposit APY",
|
||||
TABLE_TITLE_TOTAL_BORROWED: "Total Borrowed",
|
||||
TABLE_TITLE_MARKET_SIZE: "Market Size",
|
||||
TABLE_TITLE_ACTION: "Action",
|
||||
TABLE_TITLE_MAX_BORROW: "Available fro you",
|
||||
DASHBOARD_TITLE_LOANS: "Loans",
|
||||
|
|
|
@ -8,3 +8,4 @@ export * from "./useLendingObligations";
|
|||
export * from "./useUserObligations";
|
||||
export * from "./useUserObligationByReserve";
|
||||
export * from "./useBorrowedAmount";
|
||||
export * from "./useUserDeposits";
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import { PublicKey } from "@solana/web3.js";
|
||||
import { useMint } from "../contexts/accounts";
|
||||
import { LendingReserve } from "../models/lending";
|
||||
import { fromLamports } from "../utils/utils";
|
||||
import { useUserBalance } from "./useUserBalance";
|
||||
|
||||
export function useCollateralBalance(reserve?: LendingReserve) {
|
||||
export function useCollateralBalance(reserve?: LendingReserve, account?: PublicKey) {
|
||||
const mint = useMint(reserve?.collateralMint);
|
||||
const { balanceLamports, accounts } = useUserBalance(reserve?.collateralMint);
|
||||
const { balanceLamports, accounts } = useUserBalance(reserve?.collateralMint, account);
|
||||
|
||||
const collateralRatioLamports =
|
||||
(reserve?.totalLiquidity.toNumber() || 0) *
|
||||
(reserve?.availableLiqudity.toNumber() || 0) *
|
||||
(balanceLamports / (reserve?.collateralMintSupply.toNumber() || 1));
|
||||
|
||||
return {
|
||||
|
|
|
@ -4,12 +4,12 @@ import { useMint } from "../contexts/accounts";
|
|||
import { fromLamports } from "../utils/utils";
|
||||
import { useUserAccounts } from "./useUserAccounts";
|
||||
|
||||
export function useUserBalance(mint?: PublicKey) {
|
||||
export function useUserBalance(mint?: PublicKey, account?: PublicKey) {
|
||||
const { userAccounts } = useUserAccounts();
|
||||
const mintInfo = useMint(mint);
|
||||
const accounts = useMemo(() => {
|
||||
return userAccounts
|
||||
.filter((acc) => mint?.equals(acc.info.mint))
|
||||
.filter((acc) => mint?.equals(acc.info.mint) && (!account || account.equals(acc.pubkey)))
|
||||
.sort((a, b) => b.info.amount.sub(a.info.amount).toNumber());
|
||||
}, [userAccounts, mint]);
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import { ParsedAccount } from "../contexts/accounts";
|
||||
import { LendingReserve } from "../models/lending";
|
||||
import { useUserAccounts } from "./useUserAccounts";
|
||||
import { useLendingReserves } from "./useLendingReserves";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export function useUserDeposits() {
|
||||
const { userAccounts } = useUserAccounts();
|
||||
const { reserveAccounts } = useLendingReserves();
|
||||
|
||||
const reservesByCollateralMint = useMemo(() => {
|
||||
return reserveAccounts.reduce((result, item) => {
|
||||
result.set(item.info.collateralMint.toBase58(), item);
|
||||
return result;
|
||||
}
|
||||
, new Map<string, ParsedAccount<LendingReserve>>())
|
||||
}, [reserveAccounts]);
|
||||
|
||||
const userDeposits = useMemo(() => {
|
||||
return userAccounts
|
||||
.filter((acc) => reservesByCollateralMint.has(acc.info.mint.toBase58()))
|
||||
.map(item => ({
|
||||
account: item,
|
||||
reserve: reservesByCollateralMint.get(item.info.mint.toBase58()) as ParsedAccount<LendingReserve>,
|
||||
}));
|
||||
}, [userAccounts, reservesByCollateralMint]);
|
||||
|
||||
return {
|
||||
userDeposits
|
||||
};
|
||||
}
|
|
@ -12,6 +12,11 @@ import * as Layout from "./../../utils/layout";
|
|||
import { LendingInstruction } from "./lending";
|
||||
import { LendingReserve } from "./reserve";
|
||||
|
||||
export enum BorrowAmountType {
|
||||
LiquidityBorrowAmount = 0,
|
||||
CollateralDepositAmount = 1
|
||||
}
|
||||
|
||||
/// Borrow tokens from a reserve by depositing collateral tokens. The number of borrowed tokens
|
||||
/// is calculated by market price. The debt obligation is tokenized.
|
||||
///
|
||||
|
@ -33,7 +38,8 @@ import { LendingReserve } from "./reserve";
|
|||
/// 15 `[]` Rent sysvar
|
||||
/// 16 '[]` Token program id
|
||||
export const borrowInstruction = (
|
||||
collateralAmount: number | BN,
|
||||
amount: number | BN,
|
||||
amountType: BorrowAmountType,
|
||||
from: PublicKey, // Collateral input SPL Token account. $authority can transfer $collateralAmount
|
||||
to: PublicKey, // Liquidity output SPL Token account,
|
||||
depositReserve: PublicKey,
|
||||
|
@ -55,17 +61,20 @@ export const borrowInstruction = (
|
|||
): TransactionInstruction => {
|
||||
const dataLayout = BufferLayout.struct([
|
||||
BufferLayout.u8("instruction"),
|
||||
Layout.uint64("collateralAmount"),
|
||||
Layout.uint64("amount"),
|
||||
BufferLayout.u8("amountType"),
|
||||
]);
|
||||
|
||||
const data = Buffer.alloc(dataLayout.span);
|
||||
dataLayout.encode(
|
||||
{
|
||||
instruction: LendingInstruction.BorrowLiquidity,
|
||||
collateralAmount: new BN(collateralAmount),
|
||||
amount: new BN(amount),
|
||||
amountType,
|
||||
},
|
||||
data
|
||||
);
|
||||
debugger;
|
||||
|
||||
const keys = [
|
||||
{ pubkey: from, isSigner: false, isWritable: true },
|
||||
|
@ -104,9 +113,9 @@ export const borrowInstruction = (
|
|||
// deposit APY utilization currentUtilizationRate * borrowAPY
|
||||
|
||||
export const calculateBorrowAPY = (reserve: LendingReserve) => {
|
||||
const totalBorrows = reserve.totalBorrowsWad.div(WAD).toNumber();
|
||||
const totalBorrows = reserve.borrowedLiquidityWad.div(WAD).toNumber();
|
||||
const currentUtilization =
|
||||
totalBorrows / (reserve.totalLiquidity.toNumber() + totalBorrows);
|
||||
totalBorrows / (reserve.availableLiqudity.toNumber() + totalBorrows);
|
||||
const optimalUtilization = reserve.config.optimalUtilizationRate;
|
||||
let borrowAPY;
|
||||
if (currentUtilization < optimalUtilization) {
|
||||
|
|
|
@ -64,9 +64,9 @@ export const depositInstruction = (
|
|||
};
|
||||
|
||||
export const calculateDepositAPY = (reserve: LendingReserve) => {
|
||||
const totalBorrows = reserve.totalBorrowsWad.div(WAD).toNumber();
|
||||
const totalBorrows = reserve.borrowedLiquidityWad.div(WAD).toNumber();
|
||||
const currentUtilization =
|
||||
totalBorrows / (reserve.totalLiquidity.toNumber() + totalBorrows);
|
||||
totalBorrows / (reserve.availableLiqudity.toNumber() + totalBorrows);
|
||||
|
||||
const borrowAPY = calculateBorrowAPY(reserve);
|
||||
return currentUtilization * borrowAPY;
|
||||
|
|
|
@ -45,9 +45,9 @@ export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.
|
|||
),
|
||||
|
||||
Layout.uint128("cumulativeBorrowRateWad"),
|
||||
Layout.uint128("totalBorrowsWad"),
|
||||
Layout.uint128("borrowedLiquidityWad"),
|
||||
|
||||
Layout.uint64("totalLiquidity"),
|
||||
Layout.uint64("availableLiqudity"),
|
||||
Layout.uint64("collateralMintSupply"),
|
||||
]
|
||||
);
|
||||
|
@ -83,9 +83,9 @@ export interface LendingReserve {
|
|||
// collateralFactor: number;
|
||||
|
||||
cumulativeBorrowRateWad: BN;
|
||||
totalBorrowsWad: BN;
|
||||
borrowedLiquidityWad: BN;
|
||||
|
||||
totalLiquidity: BN;
|
||||
availableLiqudity: BN;
|
||||
collateralMintSupply: BN;
|
||||
|
||||
// Layout.uint128("cumulative_borrow_rate"),
|
||||
|
|
|
@ -32,6 +32,11 @@ export function Routes() {
|
|||
<AppLayout>
|
||||
<Switch>
|
||||
<Route exact path="/" component={() => <HomeView />} />
|
||||
<Route
|
||||
exact
|
||||
path="/dashboard/deposits"
|
||||
children={<DashboardView />}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/dashboard"
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import React from "react";
|
||||
import {
|
||||
useCollateralBalance,
|
||||
useTokenName,
|
||||
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 ReserveItem = (props: {
|
||||
reserve: LendingReserve;
|
||||
address: PublicKey;
|
||||
}) => {
|
||||
const name = useTokenName(props.reserve.liquidityMint);
|
||||
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
|
||||
const { balance: collateralBalance } = useCollateralBalance(props.reserve);
|
||||
|
||||
return (
|
||||
<Link to={`/deposit/${props.address.toBase58()}`}>
|
||||
<Card>
|
||||
<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>
|
||||
<div>
|
||||
<Button>
|
||||
<span>Deposit</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
import React from "react";
|
||||
import { LABELS } from "../../constants";
|
||||
import { useUserDeposits, useUserObligations } from "./../../hooks";
|
||||
import { ObligationItem } from "./obligationItem";
|
||||
import "./style.less";
|
||||
|
||||
export const DashboardView = () => {
|
||||
const { userObligations } = useUserObligations();
|
||||
const { userDeposits } = useUserDeposits();
|
||||
|
||||
return (
|
||||
<div className="dashboard-container">
|
||||
<div className="dashboard-left">
|
||||
<span>{LABELS.DASHBOARD_TITLE_DEPOSITS}</span>
|
||||
</div>
|
||||
<div className="dashboard-right">
|
||||
<span>{LABELS.DASHBOARD_TITLE_LOANS}</span>
|
||||
{userObligations.length > 0 && (
|
||||
<div className="dashboard-item dashboard-header">
|
||||
<div>{LABELS.TABLE_TITLE_ASSET}</div>
|
||||
<div>{LABELS.TABLE_TITLE_LOAN_BALANCE}</div>
|
||||
<div>{LABELS.TABLE_TITLE_APY}</div>
|
||||
<div>{LABELS.TABLE_TITLE_ACTION}</div>
|
||||
</div>
|
||||
)}
|
||||
{userObligations.map((item) => {
|
||||
return <ObligationItem obligation={item.obligation} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
import React from "react";
|
||||
import { useTokenName } from "../../hooks";
|
||||
import { LendingObligation, LendingReserve } from "../../models/lending";
|
||||
import { TokenIcon } from "../../components/TokenIcon";
|
||||
import { wadToLamports, formatNumber, fromLamports } from "../../utils/utils";
|
||||
import { Button, Card } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import { cache, ParsedAccount, useMint } from "../../contexts/accounts";
|
||||
|
||||
export const ObligationItem = (props: {
|
||||
obligation: ParsedAccount<LendingObligation>;
|
||||
}) => {
|
||||
const { obligation } = props;
|
||||
|
||||
const borrowReserve = cache.get(
|
||||
obligation.info.borrowReserve
|
||||
) as ParsedAccount<LendingReserve>;
|
||||
|
||||
const name = useTokenName(borrowReserve?.info.liquidityMint);
|
||||
|
||||
const liquidityMint = useMint(borrowReserve.info.liquidityMint);
|
||||
|
||||
const borrowAmount = fromLamports(
|
||||
wadToLamports(obligation.info.borrowAmountWad),
|
||||
liquidityMint
|
||||
);
|
||||
|
||||
return (
|
||||
<Link to={`/repay/loan/${obligation.pubkey.toBase58()}`}>
|
||||
<Card>
|
||||
<div className="dashboard-item">
|
||||
<span style={{ display: "flex" }}>
|
||||
<TokenIcon mintAddress={borrowReserve?.info.liquidityMint} />
|
||||
{name}
|
||||
</span>
|
||||
<div>
|
||||
{formatNumber.format(borrowAmount)} {name}
|
||||
</div>
|
||||
<div>--</div>
|
||||
<div>
|
||||
<Button>
|
||||
<span>Repay</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
.dashboard-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
& > div, span {
|
||||
flex: 20%;
|
||||
height: 22px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
& > :first-child {
|
||||
flex: 80px
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-header {
|
||||
margin: 0px 30px;
|
||||
|
||||
& > div {
|
||||
flex: 20%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
& > :first-child {
|
||||
text-align: left;
|
||||
flex: 80px
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dashboard-right {
|
||||
flex: 50%;
|
||||
}
|
||||
|
||||
.dashboard-left {
|
||||
flex: 50%;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.dashboard-right, .dashboard-left {
|
||||
flex: 100%;
|
||||
}
|
||||
}
|
|
@ -12,9 +12,8 @@ import {
|
|||
export const BorrowReserveView = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const lendingReserve = useLendingReserve(id);
|
||||
const reserve = lendingReserve?.info;
|
||||
|
||||
if (!reserve || !lendingReserve) {
|
||||
if (!lendingReserve) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -23,13 +22,11 @@ export const BorrowReserveView = () => {
|
|||
<div className="borrow-reserve-container">
|
||||
<BorrowInput
|
||||
className="borrow-reserve-item borrow-reserve-item-left"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey}
|
||||
reserve={lendingReserve}
|
||||
/>
|
||||
<SideReserveOverview
|
||||
className="borrow-reserve-item borrow-reserve-item-right"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey}
|
||||
reserve={lendingReserve}
|
||||
mode={SideReserveOverviewMode.Borrow}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -10,33 +10,34 @@ import { formatNumber } from "../../utils/utils";
|
|||
import { Button, Card } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { TokenAccount } from "../../models";
|
||||
import { ParsedAccount } from "../../contexts/accounts";
|
||||
|
||||
export const ReserveItem = (props: {
|
||||
reserve: LendingReserve;
|
||||
address: PublicKey;
|
||||
export const DepositItem = (props: {
|
||||
reserve: ParsedAccount<LendingReserve>;
|
||||
account: TokenAccount;
|
||||
}) => {
|
||||
const name = useTokenName(props.reserve.liquidityMint);
|
||||
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
|
||||
const { balance: collateralBalance } = useCollateralBalance(props.reserve);
|
||||
const account = props.account.info;
|
||||
|
||||
const mintAddress = props.reserve.info.liquidityMint;
|
||||
const name = useTokenName(mintAddress);
|
||||
const { balance: collateralBalance } = useCollateralBalance(props.reserve.info, props.account.pubkey);
|
||||
|
||||
return (
|
||||
<Link to={`/deposit/${props.address.toBase58()}`}>
|
||||
<Link to={`/withdraw/${props.reserve.pubkey.toBase58()}`}>
|
||||
<Card>
|
||||
<div className="deposit-item">
|
||||
<span style={{ display: "flex" }}>
|
||||
<TokenIcon mintAddress={props.reserve.liquidityMint} />
|
||||
<TokenIcon mintAddress={mintAddress} />
|
||||
{name}
|
||||
</span>
|
||||
<div>
|
||||
{formatNumber.format(tokenBalance)} {name}
|
||||
</div>
|
||||
<div>
|
||||
{formatNumber.format(collateralBalance)} {name}
|
||||
</div>
|
||||
<div>--</div>
|
||||
<div>
|
||||
<Button>
|
||||
<span>Deposit</span>
|
||||
<span>Withdraw</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,24 +1,37 @@
|
|||
import React from "react";
|
||||
import { LABELS } from "../../constants";
|
||||
import { useUserObligations } from "./../../hooks";
|
||||
import { useUserDeposits, useUserObligations } from "./../../hooks";
|
||||
import { DepositItem } from "./depositItem";
|
||||
import { ObligationItem } from "./obligationItem";
|
||||
import "./style.less";
|
||||
|
||||
export const DashboardView = () => {
|
||||
const { userObligations } = useUserObligations();
|
||||
const { userDeposits } = useUserDeposits();
|
||||
|
||||
return (
|
||||
<div className="dashboard-container">
|
||||
<div>
|
||||
<div className="dashboard-left">
|
||||
<span>{LABELS.DASHBOARD_TITLE_DEPOSITS}</span>
|
||||
{userDeposits.length > 0 && (
|
||||
<div className="dashboard-item dashboard-header">
|
||||
<div>{LABELS.TABLE_TITLE_DEPOSIT_BALANCE}</div>
|
||||
<div>{LABELS.TABLE_TITLE_LOAN_BALANCE}</div>
|
||||
<div>{LABELS.TABLE_TITLE_APY}</div>
|
||||
<div>{LABELS.TABLE_TITLE_ACTION}</div>
|
||||
</div>
|
||||
)}
|
||||
{userDeposits.map(deposit => <DepositItem reserve={deposit.reserve} account={deposit.account} />)
|
||||
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<div className="dashboard-right">
|
||||
<span>{LABELS.DASHBOARD_TITLE_LOANS}</span>
|
||||
{userObligations.length > 0 && (
|
||||
<div className="dashboard-item dashboard-header">
|
||||
<div>{LABELS.TABLE_TITLE_ASSET}</div>
|
||||
<div>{LABELS.TABLE_TITLE_LOAN_BALANCE}</div>
|
||||
<div>{LABELS.TABLE_TITLE_APY}</div>
|
||||
<div>{LABELS.TABLE_TITLE_APR}</div>
|
||||
<div>{LABELS.TABLE_TITLE_ACTION}</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -27,3 +27,24 @@
|
|||
flex: 80px
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dashboard-right {
|
||||
flex: 50%;
|
||||
}
|
||||
|
||||
.dashboard-left {
|
||||
flex: 50%;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.dashboard-right, .dashboard-left {
|
||||
flex: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,8 +34,7 @@ export const DepositReserveView = () => {
|
|||
/>
|
||||
<SideReserveOverview
|
||||
className="deposit-reserve-item deposit-reserve-item-right"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey}
|
||||
reserve={lendingReserve}
|
||||
mode={SideReserveOverviewMode.Deposit}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React from "react";
|
||||
import { LABELS } from "../../constants";
|
||||
import { useLendingReserves } from "../../hooks";
|
||||
import { LendingReserveItem } from "./item";
|
||||
import "./itemStyle.less";
|
||||
|
@ -11,11 +12,11 @@ export const HomeView = () => {
|
|||
return (
|
||||
<div className="flexColumn">
|
||||
<div className="home-item home-header">
|
||||
<div>Asset</div>
|
||||
<div>Market Size</div>
|
||||
<div>Total Borrowed</div>
|
||||
<div>Deposit APY</div>
|
||||
<div>Borrow APY</div>
|
||||
<div>{LABELS.TABLE_TITLE_ASSET}</div>
|
||||
<div>{LABELS.TABLE_TITLE_MARKET_SIZE}</div>
|
||||
<div>{LABELS.TABLE_TITLE_TOTAL_BORROWED}</div>
|
||||
<div>{LABELS.TABLE_TITLE_DEPOSIT_APR}</div>
|
||||
<div>{LABELS.TABLE_TITLE_BORROW_APR}</div>
|
||||
</div>
|
||||
{reserveAccounts.map((account) => (
|
||||
<LendingReserveItem reserve={account.info} address={account.pubkey} />
|
||||
|
|
|
@ -21,14 +21,14 @@ export const LendingReserveItem = (props: {
|
|||
|
||||
const liquidityMint = useMint(props.reserve.liquidityMint);
|
||||
|
||||
const totalLiquidity = fromLamports(
|
||||
props.reserve.totalLiquidity.toNumber(),
|
||||
const availableLiqudity = fromLamports(
|
||||
props.reserve.availableLiqudity.toNumber(),
|
||||
liquidityMint
|
||||
);
|
||||
|
||||
const totalBorrows = useMemo(
|
||||
() =>
|
||||
fromLamports(wadToLamports(props.reserve.totalBorrowsWad), liquidityMint),
|
||||
fromLamports(wadToLamports(props.reserve.borrowedLiquidityWad), liquidityMint),
|
||||
[props.reserve, liquidityMint]
|
||||
);
|
||||
|
||||
|
@ -40,7 +40,7 @@ export const LendingReserveItem = (props: {
|
|||
props.reserve,
|
||||
]);
|
||||
|
||||
const marketSize = totalLiquidity+totalBorrows;
|
||||
const marketSize = availableLiqudity+totalBorrows;
|
||||
|
||||
return (
|
||||
<Link to={`/reserve/${props.address.toBase58()}`}>
|
||||
|
|
|
@ -38,8 +38,7 @@ export const RepayReserveView = () => {
|
|||
/>
|
||||
<SideReserveOverview
|
||||
className="repay-reserve-item repay-reserve-item-right"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey}
|
||||
reserve={lendingReserve}
|
||||
mode={SideReserveOverviewMode.Borrow}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -34,8 +34,7 @@ export const WithdrawView = () => {
|
|||
/>
|
||||
<SideReserveOverview
|
||||
className="deposit-reserve-item deposit-reserve-item-right"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey}
|
||||
reserve={lendingReserve}
|
||||
mode={SideReserveOverviewMode.Deposit}
|
||||
/>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue