feat: borrow amount Type

This commit is contained in:
bartosz-lipinski 2020-11-25 23:20:24 -06:00
parent 49324447ba
commit 00db4555be
26 changed files with 382 additions and 101 deletions

View File

@ -23,18 +23,21 @@ import {
LendingObligationLayout, LendingObligationLayout,
borrowInstruction, borrowInstruction,
LendingMarket, LendingMarket,
BorrowAmountType,
LendingObligation,
} from "../models"; } from "../models";
import { toLamports } from "../utils/utils"; import { toLamports } from "../utils/utils";
export const borrow = async ( export const borrow = async (
from: TokenAccount, from: TokenAccount,
amount: number, amount: number,
amountType: BorrowAmountType,
borrowReserve: LendingReserve, borrowReserve: ParsedAccount<LendingReserve>,
borrowReserveAddress: PublicKey,
depositReserve: LendingReserve, depositReserve: ParsedAccount<LendingReserve>,
depositReserveAddress: PublicKey,
exsistingObligation: ParsedAccount<LendingObligation> | undefined,
connection: Connection, connection: Connection,
wallet: any wallet: any
@ -82,7 +85,7 @@ export const borrow = async (
instructions, instructions,
cleanupInstructions, cleanupInstructions,
accountRentExempt, accountRentExempt,
borrowReserve.liquidityMint, borrowReserve.info.liquidityMint,
signers signers
); );
@ -108,23 +111,44 @@ export const borrow = async (
cleanupInstructions = []; cleanupInstructions = [];
const [authority] = await PublicKey.findProgramAddress( const [authority] = await PublicKey.findProgramAddress(
[depositReserve.lendingMarket.toBuffer()], [depositReserve.info.lendingMarket.toBuffer()],
LENDING_PROGRAM_ID LENDING_PROGRAM_ID
); );
const mint = (await cache.query(
connection,
depositReserve.collateralMint, let amountLamports: number = 0;
MintParser let fromLamports: number = 0;
)) as ParsedAccount<MintInfo>; if(amountType === BorrowAmountType.LiquidityBorrowAmount) {
const amountLamports = toLamports(amount, mint?.info); // 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( const fromAccount = ensureSplAccount(
instructions, instructions,
cleanupInstructions, cleanupInstructions,
from, from,
wallet.publicKey, wallet.publicKey,
amountLamports + accountRentExempt, fromLamports + accountRentExempt,
signers signers
); );
@ -136,17 +160,17 @@ export const borrow = async (
authority, authority,
wallet.publicKey, wallet.publicKey,
[], [],
amountLamports fromLamports
) )
); );
const market = cache.get(depositReserve.lendingMarket) as ParsedAccount< const market = cache.get(depositReserve.info.lendingMarket) as ParsedAccount<
LendingMarket LendingMarket
>; >;
const dexMarketAddress = borrowReserve.dexMarketOption const dexMarketAddress = borrowReserve.info.dexMarketOption
? borrowReserve.dexMarket ? borrowReserve.info.dexMarket
: depositReserve.dexMarket; : depositReserve.info.dexMarket;
const dexMarket = cache.get(dexMarketAddress); const dexMarket = cache.get(dexMarketAddress);
if (!dexMarket) { if (!dexMarket) {
@ -154,7 +178,7 @@ export const borrow = async (
} }
const dexOrderBookSide = market.info.quoteMint.equals( const dexOrderBookSide = market.info.quoteMint.equals(
depositReserve.liquidityMint depositReserve.info.liquidityMint
) )
? dexMarket?.info.bids ? dexMarket?.info.bids
: dexMarket?.info.asks; : dexMarket?.info.asks;
@ -169,12 +193,13 @@ export const borrow = async (
instructions.push( instructions.push(
borrowInstruction( borrowInstruction(
amountLamports, amountLamports,
amountType,
fromAccount, fromAccount,
toAccount, toAccount,
depositReserveAddress, depositReserve.pubkey,
depositReserve.collateralSupply, depositReserve.info.collateralSupply,
borrowReserveAddress, borrowReserve.pubkey,
borrowReserve.liquiditySupply, borrowReserve.info.liquiditySupply,
obligation, obligation,
obligationMint, obligationMint,

View File

@ -1,6 +1,6 @@
import React, { useCallback, useMemo, useState } from "react"; import React, { useCallback, useMemo, useState } from "react";
import { useTokenName, useUserBalance } from "../../hooks"; import { useTokenName, useUserBalance } from "../../hooks";
import { LendingReserve, LendingReserveParser } from "../../models"; import { BorrowAmountType, LendingReserve, LendingReserveParser } from "../../models";
import { TokenIcon } from "../TokenIcon"; import { TokenIcon } from "../TokenIcon";
import { Button, Card } from "antd"; import { Button, Card } from "antd";
import { cache, ParsedAccount } from "../../contexts/accounts"; import { cache, ParsedAccount } from "../../contexts/accounts";
@ -15,15 +15,13 @@ import { LABELS } from "../../constants";
export const BorrowInput = (props: { export const BorrowInput = (props: {
className?: string; className?: string;
reserve: LendingReserve; reserve: ParsedAccount<LendingReserve>;
address: PublicKey;
}) => { }) => {
const connection = useConnection(); const connection = useConnection();
const { wallet } = useWallet(); const { wallet } = useWallet();
const [value, setValue] = useState(""); const [value, setValue] = useState("");
const borrowReserve = props.reserve; const borrowReserve = props.reserve;
const borrowReserveAddress = props.address;
const [collateralReserveMint, setCollateralReserveMint] = useState<string>(); const [collateralReserveMint, setCollateralReserveMint] = useState<string>();
@ -36,7 +34,7 @@ export const BorrowInput = (props: {
return cache.get(id) as ParsedAccount<LendingReserve>; return cache.get(id) as ParsedAccount<LendingReserve>;
}, [collateralReserveMint]); }, [collateralReserveMint]);
const name = useTokenName(borrowReserve?.liquidityMint); const name = useTokenName(borrowReserve?.info.liquidityMint);
const { accounts: fromAccounts } = useUserBalance( const { accounts: fromAccounts } = useUserBalance(
collateralReserve?.info.collateralMint collateralReserve?.info.collateralMint
); );
@ -50,10 +48,11 @@ export const BorrowInput = (props: {
borrow( borrow(
fromAccounts[0], fromAccounts[0],
parseFloat(value), parseFloat(value),
// TODO: switch to collateral when user is using slider
BorrowAmountType.LiquidityBorrowAmount,
borrowReserve, borrowReserve,
borrowReserveAddress, collateralReserve,
collateralReserve.info, undefined,
collateralReserve.pubkey,
connection, connection,
wallet wallet
); );
@ -64,7 +63,6 @@ export const BorrowInput = (props: {
collateralReserve, collateralReserve,
borrowReserve, borrowReserve,
fromAccounts, fromAccounts,
borrowReserveAddress,
]); ]);
const bodyStyle: React.CSSProperties = { const bodyStyle: React.CSSProperties = {
@ -86,7 +84,7 @@ export const BorrowInput = (props: {
> >
<div className="borrow-input-title">{LABELS.BORROW_QUESTION}</div> <div className="borrow-input-title">{LABELS.BORROW_QUESTION}</div>
<div className="token-input"> <div className="token-input">
<TokenIcon mintAddress={borrowReserve?.liquidityMint} /> <TokenIcon mintAddress={borrowReserve?.info.liquidityMint} />
<NumericInput <NumericInput
value={value} value={value}
onChange={(val: any) => { onChange={(val: any) => {
@ -105,7 +103,7 @@ export const BorrowInput = (props: {
</div> </div>
<div className="borrow-input-title">{LABELS.SELECT_COLLATERAL}</div> <div className="borrow-input-title">{LABELS.SELECT_COLLATERAL}</div>
<CollateralSelector <CollateralSelector
reserve={borrowReserve} reserve={borrowReserve.info}
mint={collateralReserveMint} mint={collateralReserveMint}
onMintChange={setCollateralReserveMint} onMintChange={setCollateralReserveMint}
/> />

View File

@ -20,13 +20,13 @@ export const ReserveUtilizationChart = (props: {
const liquidityMint = useMint(props.reserve.liquidityMint); const liquidityMint = useMint(props.reserve.liquidityMint);
const avilableLiquidity = fromLamports( const avilableLiquidity = fromLamports(
props.reserve.totalLiquidity.toNumber(), props.reserve.availableLiqudity.toNumber(),
liquidityMint liquidityMint
); );
const totalBorrows = useMemo( const totalBorrows = useMemo(
() => () =>
fromLamports(wadToLamports(props.reserve.totalBorrowsWad), liquidityMint), fromLamports(wadToLamports(props.reserve.borrowedLiquidityWad), liquidityMint),
[props.reserve, liquidityMint] [props.reserve, liquidityMint]
); );

View File

@ -4,7 +4,7 @@ import { LendingReserve } from "../../models/lending";
import { TokenIcon } from "../../components/TokenIcon"; import { TokenIcon } from "../../components/TokenIcon";
import { formatNumber, formatPct, fromLamports } from "../../utils/utils"; import { formatNumber, formatPct, fromLamports } from "../../utils/utils";
import { Card, Typography } from "antd"; import { Card, Typography } from "antd";
import { useMint } from "../../contexts/accounts"; import { ParsedAccount, useMint } from "../../contexts/accounts";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
@ -17,17 +17,16 @@ export enum SideReserveOverviewMode {
export const SideReserveOverview = (props: { export const SideReserveOverview = (props: {
className?: string; className?: string;
reserve: LendingReserve; reserve: ParsedAccount<LendingReserve>;
address: PublicKey;
mode: SideReserveOverviewMode; mode: SideReserveOverviewMode;
}) => { }) => {
const reserve = props.reserve; const reserve = props.reserve.info;
const mode = props.mode; const mode = props.mode;
const name = useTokenName(reserve?.liquidityMint); const name = useTokenName(reserve?.liquidityMint);
const liquidityMint = useMint(props.reserve.liquidityMint); const liquidityMint = useMint(reserve.liquidityMint);
const totalLiquidity = fromLamports( const availableLiqudity = fromLamports(
props.reserve.totalLiquidity.toNumber(), reserve.availableLiqudity.toNumber(),
liquidityMint liquidityMint
); );
@ -102,7 +101,7 @@ export const SideReserveOverview = (props: {
justifyContent: "center", justifyContent: "center",
}} }}
> >
<Link to={`/reserve/${props.address}`}> <Link to={`/reserve/${props.reserve.pubkey}`}>
<TokenIcon <TokenIcon
mintAddress={reserve?.liquidityMint} mintAddress={reserve?.liquidityMint}
style={{ width: 30, height: 30 }} style={{ width: 30, height: 30 }}
@ -124,7 +123,7 @@ export const SideReserveOverview = (props: {
Available liquidity: Available liquidity:
</Text> </Text>
<div className="card-cell "> <div className="card-cell ">
{formatNumber.format(totalLiquidity)} {name} {formatNumber.format(availableLiqudity)} {name}
</div> </div>
</div> </div>

View File

@ -21,9 +21,14 @@ export const LABELS = {
BORROW_QUESTION: "How much would you like to borrow?", BORROW_QUESTION: "How much would you like to borrow?",
BORROW_ACTION: "Borrow", BORROW_ACTION: "Borrow",
TABLE_TITLE_ASSET: "Asset", 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_APY: "APY",
TABLE_TITLE_APR: "APR", 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_ACTION: "Action",
TABLE_TITLE_MAX_BORROW: "Available fro you", TABLE_TITLE_MAX_BORROW: "Available fro you",
DASHBOARD_TITLE_LOANS: "Loans", DASHBOARD_TITLE_LOANS: "Loans",

View File

@ -8,3 +8,4 @@ export * from "./useLendingObligations";
export * from "./useUserObligations"; export * from "./useUserObligations";
export * from "./useUserObligationByReserve"; export * from "./useUserObligationByReserve";
export * from "./useBorrowedAmount"; export * from "./useBorrowedAmount";
export * from "./useUserDeposits";

View File

@ -1,14 +1,15 @@
import { PublicKey } from "@solana/web3.js";
import { useMint } from "../contexts/accounts"; import { useMint } from "../contexts/accounts";
import { LendingReserve } from "../models/lending"; import { LendingReserve } from "../models/lending";
import { fromLamports } from "../utils/utils"; import { fromLamports } from "../utils/utils";
import { useUserBalance } from "./useUserBalance"; import { useUserBalance } from "./useUserBalance";
export function useCollateralBalance(reserve?: LendingReserve) { export function useCollateralBalance(reserve?: LendingReserve, account?: PublicKey) {
const mint = useMint(reserve?.collateralMint); const mint = useMint(reserve?.collateralMint);
const { balanceLamports, accounts } = useUserBalance(reserve?.collateralMint); const { balanceLamports, accounts } = useUserBalance(reserve?.collateralMint, account);
const collateralRatioLamports = const collateralRatioLamports =
(reserve?.totalLiquidity.toNumber() || 0) * (reserve?.availableLiqudity.toNumber() || 0) *
(balanceLamports / (reserve?.collateralMintSupply.toNumber() || 1)); (balanceLamports / (reserve?.collateralMintSupply.toNumber() || 1));
return { return {

View File

@ -4,12 +4,12 @@ import { useMint } from "../contexts/accounts";
import { fromLamports } from "../utils/utils"; import { fromLamports } from "../utils/utils";
import { useUserAccounts } from "./useUserAccounts"; import { useUserAccounts } from "./useUserAccounts";
export function useUserBalance(mint?: PublicKey) { export function useUserBalance(mint?: PublicKey, account?: PublicKey) {
const { userAccounts } = useUserAccounts(); const { userAccounts } = useUserAccounts();
const mintInfo = useMint(mint); const mintInfo = useMint(mint);
const accounts = useMemo(() => { const accounts = useMemo(() => {
return userAccounts 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()); .sort((a, b) => b.info.amount.sub(a.info.amount).toNumber());
}, [userAccounts, mint]); }, [userAccounts, mint]);

View File

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

View File

@ -12,6 +12,11 @@ import * as Layout from "./../../utils/layout";
import { LendingInstruction } from "./lending"; import { LendingInstruction } from "./lending";
import { LendingReserve } from "./reserve"; 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 /// Borrow tokens from a reserve by depositing collateral tokens. The number of borrowed tokens
/// is calculated by market price. The debt obligation is tokenized. /// is calculated by market price. The debt obligation is tokenized.
/// ///
@ -33,7 +38,8 @@ import { LendingReserve } from "./reserve";
/// 15 `[]` Rent sysvar /// 15 `[]` Rent sysvar
/// 16 '[]` Token program id /// 16 '[]` Token program id
export const borrowInstruction = ( export const borrowInstruction = (
collateralAmount: number | BN, amount: number | BN,
amountType: BorrowAmountType,
from: PublicKey, // Collateral input SPL Token account. $authority can transfer $collateralAmount from: PublicKey, // Collateral input SPL Token account. $authority can transfer $collateralAmount
to: PublicKey, // Liquidity output SPL Token account, to: PublicKey, // Liquidity output SPL Token account,
depositReserve: PublicKey, depositReserve: PublicKey,
@ -55,17 +61,20 @@ export const borrowInstruction = (
): TransactionInstruction => { ): TransactionInstruction => {
const dataLayout = BufferLayout.struct([ const dataLayout = BufferLayout.struct([
BufferLayout.u8("instruction"), BufferLayout.u8("instruction"),
Layout.uint64("collateralAmount"), Layout.uint64("amount"),
BufferLayout.u8("amountType"),
]); ]);
const data = Buffer.alloc(dataLayout.span); const data = Buffer.alloc(dataLayout.span);
dataLayout.encode( dataLayout.encode(
{ {
instruction: LendingInstruction.BorrowLiquidity, instruction: LendingInstruction.BorrowLiquidity,
collateralAmount: new BN(collateralAmount), amount: new BN(amount),
amountType,
}, },
data data
); );
debugger;
const keys = [ const keys = [
{ pubkey: from, isSigner: false, isWritable: true }, { pubkey: from, isSigner: false, isWritable: true },
@ -104,9 +113,9 @@ export const borrowInstruction = (
// deposit APY utilization currentUtilizationRate * borrowAPY // deposit APY utilization currentUtilizationRate * borrowAPY
export const calculateBorrowAPY = (reserve: LendingReserve) => { export const calculateBorrowAPY = (reserve: LendingReserve) => {
const totalBorrows = reserve.totalBorrowsWad.div(WAD).toNumber(); const totalBorrows = reserve.borrowedLiquidityWad.div(WAD).toNumber();
const currentUtilization = const currentUtilization =
totalBorrows / (reserve.totalLiquidity.toNumber() + totalBorrows); totalBorrows / (reserve.availableLiqudity.toNumber() + totalBorrows);
const optimalUtilization = reserve.config.optimalUtilizationRate; const optimalUtilization = reserve.config.optimalUtilizationRate;
let borrowAPY; let borrowAPY;
if (currentUtilization < optimalUtilization) { if (currentUtilization < optimalUtilization) {

View File

@ -64,9 +64,9 @@ export const depositInstruction = (
}; };
export const calculateDepositAPY = (reserve: LendingReserve) => { export const calculateDepositAPY = (reserve: LendingReserve) => {
const totalBorrows = reserve.totalBorrowsWad.div(WAD).toNumber(); const totalBorrows = reserve.borrowedLiquidityWad.div(WAD).toNumber();
const currentUtilization = const currentUtilization =
totalBorrows / (reserve.totalLiquidity.toNumber() + totalBorrows); totalBorrows / (reserve.availableLiqudity.toNumber() + totalBorrows);
const borrowAPY = calculateBorrowAPY(reserve); const borrowAPY = calculateBorrowAPY(reserve);
return currentUtilization * borrowAPY; return currentUtilization * borrowAPY;

View File

@ -45,9 +45,9 @@ export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.
), ),
Layout.uint128("cumulativeBorrowRateWad"), Layout.uint128("cumulativeBorrowRateWad"),
Layout.uint128("totalBorrowsWad"), Layout.uint128("borrowedLiquidityWad"),
Layout.uint64("totalLiquidity"), Layout.uint64("availableLiqudity"),
Layout.uint64("collateralMintSupply"), Layout.uint64("collateralMintSupply"),
] ]
); );
@ -83,9 +83,9 @@ export interface LendingReserve {
// collateralFactor: number; // collateralFactor: number;
cumulativeBorrowRateWad: BN; cumulativeBorrowRateWad: BN;
totalBorrowsWad: BN; borrowedLiquidityWad: BN;
totalLiquidity: BN; availableLiqudity: BN;
collateralMintSupply: BN; collateralMintSupply: BN;
// Layout.uint128("cumulative_borrow_rate"), // Layout.uint128("cumulative_borrow_rate"),

View File

@ -32,6 +32,11 @@ export function Routes() {
<AppLayout> <AppLayout>
<Switch> <Switch>
<Route exact path="/" component={() => <HomeView />} /> <Route exact path="/" component={() => <HomeView />} />
<Route
exact
path="/dashboard/deposits"
children={<DashboardView />}
/>
<Route <Route
exact exact
path="/dashboard" path="/dashboard"

View File

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

View File

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

View File

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

View File

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

View File

@ -12,9 +12,8 @@ import {
export const BorrowReserveView = () => { export const BorrowReserveView = () => {
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const lendingReserve = useLendingReserve(id); const lendingReserve = useLendingReserve(id);
const reserve = lendingReserve?.info;
if (!reserve || !lendingReserve) { if (!lendingReserve) {
return null; return null;
} }
@ -23,13 +22,11 @@ export const BorrowReserveView = () => {
<div className="borrow-reserve-container"> <div className="borrow-reserve-container">
<BorrowInput <BorrowInput
className="borrow-reserve-item borrow-reserve-item-left" className="borrow-reserve-item borrow-reserve-item-left"
reserve={reserve} reserve={lendingReserve}
address={lendingReserve.pubkey}
/> />
<SideReserveOverview <SideReserveOverview
className="borrow-reserve-item borrow-reserve-item-right" className="borrow-reserve-item borrow-reserve-item-right"
reserve={reserve} reserve={lendingReserve}
address={lendingReserve.pubkey}
mode={SideReserveOverviewMode.Borrow} mode={SideReserveOverviewMode.Borrow}
/> />
</div> </div>

View File

@ -10,33 +10,34 @@ import { formatNumber } from "../../utils/utils";
import { Button, Card } from "antd"; import { Button, Card } from "antd";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { TokenAccount } from "../../models";
import { ParsedAccount } from "../../contexts/accounts";
export const ReserveItem = (props: { export const DepositItem = (props: {
reserve: LendingReserve; reserve: ParsedAccount<LendingReserve>;
address: PublicKey; account: TokenAccount;
}) => { }) => {
const name = useTokenName(props.reserve.liquidityMint); const account = props.account.info;
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
const { balance: collateralBalance } = useCollateralBalance(props.reserve); const mintAddress = props.reserve.info.liquidityMint;
const name = useTokenName(mintAddress);
const { balance: collateralBalance } = useCollateralBalance(props.reserve.info, props.account.pubkey);
return ( return (
<Link to={`/deposit/${props.address.toBase58()}`}> <Link to={`/withdraw/${props.reserve.pubkey.toBase58()}`}>
<Card> <Card>
<div className="deposit-item"> <div className="deposit-item">
<span style={{ display: "flex" }}> <span style={{ display: "flex" }}>
<TokenIcon mintAddress={props.reserve.liquidityMint} /> <TokenIcon mintAddress={mintAddress} />
{name} {name}
</span> </span>
<div>
{formatNumber.format(tokenBalance)} {name}
</div>
<div> <div>
{formatNumber.format(collateralBalance)} {name} {formatNumber.format(collateralBalance)} {name}
</div> </div>
<div>--</div> <div>--</div>
<div> <div>
<Button> <Button>
<span>Deposit</span> <span>Withdraw</span>
</Button> </Button>
</div> </div>
</div> </div>

View File

@ -1,24 +1,37 @@
import React from "react"; import React from "react";
import { LABELS } from "../../constants"; import { LABELS } from "../../constants";
import { useUserObligations } from "./../../hooks"; import { useUserDeposits, useUserObligations } from "./../../hooks";
import { DepositItem } from "./depositItem";
import { ObligationItem } from "./obligationItem"; import { ObligationItem } from "./obligationItem";
import "./style.less"; import "./style.less";
export const DashboardView = () => { export const DashboardView = () => {
const { userObligations } = useUserObligations(); const { userObligations } = useUserObligations();
const { userDeposits } = useUserDeposits();
return ( return (
<div className="dashboard-container"> <div className="dashboard-container">
<div> <div className="dashboard-left">
<span>{LABELS.DASHBOARD_TITLE_DEPOSITS}</span> <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> <div className="dashboard-right">
<span>{LABELS.DASHBOARD_TITLE_LOANS}</span> <span>{LABELS.DASHBOARD_TITLE_LOANS}</span>
{userObligations.length > 0 && ( {userObligations.length > 0 && (
<div className="dashboard-item dashboard-header"> <div className="dashboard-item dashboard-header">
<div>{LABELS.TABLE_TITLE_ASSET}</div> <div>{LABELS.TABLE_TITLE_ASSET}</div>
<div>{LABELS.TABLE_TITLE_LOAN_BALANCE}</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>{LABELS.TABLE_TITLE_ACTION}</div>
</div> </div>
)} )}

View File

@ -26,4 +26,25 @@
text-align: left; text-align: left;
flex: 80px 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%;
}
}

View File

@ -34,8 +34,7 @@ export const DepositReserveView = () => {
/> />
<SideReserveOverview <SideReserveOverview
className="deposit-reserve-item deposit-reserve-item-right" className="deposit-reserve-item deposit-reserve-item-right"
reserve={reserve} reserve={lendingReserve}
address={lendingReserve.pubkey}
mode={SideReserveOverviewMode.Deposit} mode={SideReserveOverviewMode.Deposit}
/> />
</div> </div>

View File

@ -1,4 +1,5 @@
import React from "react"; import React from "react";
import { LABELS } from "../../constants";
import { useLendingReserves } from "../../hooks"; import { useLendingReserves } from "../../hooks";
import { LendingReserveItem } from "./item"; import { LendingReserveItem } from "./item";
import "./itemStyle.less"; import "./itemStyle.less";
@ -11,11 +12,11 @@ export const HomeView = () => {
return ( return (
<div className="flexColumn"> <div className="flexColumn">
<div className="home-item home-header"> <div className="home-item home-header">
<div>Asset</div> <div>{LABELS.TABLE_TITLE_ASSET}</div>
<div>Market Size</div> <div>{LABELS.TABLE_TITLE_MARKET_SIZE}</div>
<div>Total Borrowed</div> <div>{LABELS.TABLE_TITLE_TOTAL_BORROWED}</div>
<div>Deposit APY</div> <div>{LABELS.TABLE_TITLE_DEPOSIT_APR}</div>
<div>Borrow APY</div> <div>{LABELS.TABLE_TITLE_BORROW_APR}</div>
</div> </div>
{reserveAccounts.map((account) => ( {reserveAccounts.map((account) => (
<LendingReserveItem reserve={account.info} address={account.pubkey} /> <LendingReserveItem reserve={account.info} address={account.pubkey} />

View File

@ -21,14 +21,14 @@ export const LendingReserveItem = (props: {
const liquidityMint = useMint(props.reserve.liquidityMint); const liquidityMint = useMint(props.reserve.liquidityMint);
const totalLiquidity = fromLamports( const availableLiqudity = fromLamports(
props.reserve.totalLiquidity.toNumber(), props.reserve.availableLiqudity.toNumber(),
liquidityMint liquidityMint
); );
const totalBorrows = useMemo( const totalBorrows = useMemo(
() => () =>
fromLamports(wadToLamports(props.reserve.totalBorrowsWad), liquidityMint), fromLamports(wadToLamports(props.reserve.borrowedLiquidityWad), liquidityMint),
[props.reserve, liquidityMint] [props.reserve, liquidityMint]
); );
@ -40,7 +40,7 @@ export const LendingReserveItem = (props: {
props.reserve, props.reserve,
]); ]);
const marketSize = totalLiquidity+totalBorrows; const marketSize = availableLiqudity+totalBorrows;
return ( return (
<Link to={`/reserve/${props.address.toBase58()}`}> <Link to={`/reserve/${props.address.toBase58()}`}>

View File

@ -38,8 +38,7 @@ export const RepayReserveView = () => {
/> />
<SideReserveOverview <SideReserveOverview
className="repay-reserve-item repay-reserve-item-right" className="repay-reserve-item repay-reserve-item-right"
reserve={reserve} reserve={lendingReserve}
address={lendingReserve.pubkey}
mode={SideReserveOverviewMode.Borrow} mode={SideReserveOverviewMode.Borrow}
/> />
</div> </div>

View File

@ -34,8 +34,7 @@ export const WithdrawView = () => {
/> />
<SideReserveOverview <SideReserveOverview
className="deposit-reserve-item deposit-reserve-item-right" className="deposit-reserve-item deposit-reserve-item-right"
reserve={reserve} reserve={lendingReserve}
address={lendingReserve.pubkey}
mode={SideReserveOverviewMode.Deposit} mode={SideReserveOverviewMode.Deposit}
/> />
</div> </div>