From 49324447ba3f70e45db6e138a954d7617a79d327 Mon Sep 17 00:00:00 2001 From: bartosz-lipinski <264380+bartosz-lipinski@users.noreply.github.com> Date: Wed, 25 Nov 2020 00:16:37 -0600 Subject: [PATCH] feat: reserve utilization --- src/actions/deposit.tsx | 2 +- src/actions/withdraw.tsx | 2 +- src/components/ReserveStatus/index.tsx | 109 +--------------- .../ReserveUtilizationChart/index.tsx | 108 ++++++++++++++++ src/components/SideReserveOverview/index.tsx | 13 +- src/constants/labels.ts | 2 + src/contexts/lending.tsx | 20 ++- src/contexts/market.tsx | 22 +++- src/models/lending/borrow.ts | 29 +++++ src/models/lending/deposit.ts | 73 +++++++++++ src/models/lending/index.ts | 2 + src/models/lending/reserve.ts | 117 ------------------ src/models/lending/withdraw.ts | 50 ++++++++ src/views/borrow/index.tsx | 9 +- src/views/home/item.tsx | 22 +++- 15 files changed, 327 insertions(+), 253 deletions(-) create mode 100644 src/components/ReserveUtilizationChart/index.tsx create mode 100644 src/models/lending/deposit.ts create mode 100644 src/models/lending/withdraw.ts diff --git a/src/actions/deposit.tsx b/src/actions/deposit.tsx index 5b623bd..b30a17b 100644 --- a/src/actions/deposit.tsx +++ b/src/actions/deposit.tsx @@ -10,7 +10,7 @@ import { depositInstruction, initReserveInstruction, LendingReserve, -} from "./../models/lending/reserve"; +} from "./../models/lending"; import { AccountLayout, MintInfo, Token } from "@solana/spl-token"; import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids"; import { diff --git a/src/actions/withdraw.tsx b/src/actions/withdraw.tsx index 85b5fac..d33b20f 100644 --- a/src/actions/withdraw.tsx +++ b/src/actions/withdraw.tsx @@ -9,7 +9,7 @@ import { notify } from "../utils/notifications"; import { LendingReserve, withdrawInstruction, -} from "./../models/lending/reserve"; +} from "./../models/lending"; import { AccountLayout, Token } from "@solana/spl-token"; import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids"; import { findOrCreateAccountByMint } from "./account"; diff --git a/src/components/ReserveStatus/index.tsx b/src/components/ReserveStatus/index.tsx index 0a8fa46..bd0761e 100644 --- a/src/components/ReserveStatus/index.tsx +++ b/src/components/ReserveStatus/index.tsx @@ -1,115 +1,10 @@ -import React, { useEffect, useMemo, useRef } from "react"; +import React, { } from "react"; import { LendingReserve } from "../../models/lending"; import { Card } from "antd"; import { PublicKey } from "@solana/web3.js"; import "./style.less"; import { LABELS } from "../../constants"; -import echarts from "echarts"; -import { formatNumber, formatUSD, fromLamports, wadToLamports } from "../../utils/utils"; -import { useMint } from "../../contexts/accounts"; - -export const ReserveUtilizationChart = (props: { - reserve: LendingReserve; -}) => { - const chartDiv = useRef(null); - - // dispose chart - useEffect(() => { - const div = chartDiv.current; - return () => { - let instance = div && echarts.getInstanceByDom(div); - instance && instance.dispose(); - }; - }, []); - - const liquidityMint = useMint(props.reserve.liquidityMint); - const avilableLiquidity = fromLamports( - props.reserve.totalLiquidity.toNumber(), - liquidityMint - ); - - const totalBorrows = useMemo( - () => - fromLamports(wadToLamports(props.reserve.totalBorrowsWad), liquidityMint), - [props.reserve, liquidityMint] - ); - - useEffect(() => { - if (!chartDiv.current) { - return; - } - - let instance = echarts.getInstanceByDom(chartDiv.current); - if (!instance) { - instance = echarts.init(chartDiv.current as any); - } - - const data = [ - { - name: 'Available Liquidity', - value: avilableLiquidity, - tokens: avilableLiquidity, - }, - { - name: 'Total Borrowed', - value: totalBorrows, - tokens: totalBorrows, - }, - ]; - - instance.setOption({ - tooltip: { - trigger: "item", - formatter: function (params: any) { - var val = formatUSD.format(params.value); - var tokenAmount = formatNumber.format(params.data.tokens); - return `${params.name}: \n${val}\n(${tokenAmount})`; - }, - }, - series: [ - { - name: "Liquidity", - type: "pie", - radius: ['50%', '70%'], - top: 0, - bottom: 0, - left: 0, - right: 0, - animation: false, - label: { - fontSize: 14, - show: true, - formatter: function (params: any) { - var val = formatUSD.format(params.value); - var tokenAmount = formatNumber.format(params.data.tokens); - return `{c|${params.name}}\n{r|${tokenAmount}}\n{r|${val}}`; - }, - rich: { - c: { - color: "#999", - lineHeight: 22, - align: "center", - }, - r: { - color: "#999", - align: "right", - }, - }, - color: "rgba(255, 255, 255, 0.5)", - }, - itemStyle: { - normal: { - borderColor: "#000", - }, - }, - data, - }, - ], - }); - }, [totalBorrows, avilableLiquidity]); - - return
-} +import { ReserveUtilizationChart } from "./../../components/ReserveUtilizationChart"; export const ReserveStatus = (props: { className?: string; diff --git a/src/components/ReserveUtilizationChart/index.tsx b/src/components/ReserveUtilizationChart/index.tsx new file mode 100644 index 0000000..b7ada0a --- /dev/null +++ b/src/components/ReserveUtilizationChart/index.tsx @@ -0,0 +1,108 @@ +import React, { useEffect, useMemo, useRef } from "react"; +import { LendingReserve } from "../../models/lending"; +import echarts from "echarts"; +import { formatNumber, formatUSD, fromLamports, wadToLamports } from "../../utils/utils"; +import { useMint } from "../../contexts/accounts"; + +export const ReserveUtilizationChart = (props: { + reserve: LendingReserve; +}) => { + const chartDiv = useRef(null); + + // dispose chart + useEffect(() => { + const div = chartDiv.current; + return () => { + let instance = div && echarts.getInstanceByDom(div); + instance && instance.dispose(); + }; + }, []); + + const liquidityMint = useMint(props.reserve.liquidityMint); + const avilableLiquidity = fromLamports( + props.reserve.totalLiquidity.toNumber(), + liquidityMint + ); + + const totalBorrows = useMemo( + () => + fromLamports(wadToLamports(props.reserve.totalBorrowsWad), liquidityMint), + [props.reserve, liquidityMint] + ); + + useEffect(() => { + if (!chartDiv.current) { + return; + } + + let instance = echarts.getInstanceByDom(chartDiv.current); + if (!instance) { + instance = echarts.init(chartDiv.current as any); + } + + const data = [ + { + name: 'Available Liquidity', + value: avilableLiquidity, + tokens: avilableLiquidity, + }, + { + name: 'Total Borrowed', + value: totalBorrows, + tokens: totalBorrows, + }, + ]; + + instance.setOption({ + tooltip: { + trigger: "item", + formatter: function (params: any) { + var val = formatUSD.format(params.value); + var tokenAmount = formatNumber.format(params.data.tokens); + return `${params.name}: \n${val}\n(${tokenAmount})`; + }, + }, + series: [ + { + name: "Liquidity", + type: "pie", + radius: ['50%', '70%'], + top: 0, + bottom: 0, + left: 0, + right: 0, + animation: false, + label: { + fontSize: 14, + show: true, + formatter: function (params: any) { + var val = formatUSD.format(params.value); + var tokenAmount = formatNumber.format(params.data.tokens); + return `{c|${params.name}}\n{r|${tokenAmount}}\n{r|${val}}`; + }, + rich: { + c: { + color: "#999", + lineHeight: 22, + align: "center", + }, + r: { + color: "#999", + align: "right", + }, + }, + color: "rgba(255, 255, 255, 0.5)", + }, + itemStyle: { + normal: { + borderColor: "#000", + }, + }, + data, + }, + ], + }); + }, [totalBorrows, avilableLiquidity]); + + return
+} diff --git a/src/components/SideReserveOverview/index.tsx b/src/components/SideReserveOverview/index.tsx index ab104f4..fa6a849 100644 --- a/src/components/SideReserveOverview/index.tsx +++ b/src/components/SideReserveOverview/index.tsx @@ -6,6 +6,7 @@ import { formatNumber, formatPct, fromLamports } from "../../utils/utils"; import { Card, Typography } from "antd"; import { useMint } from "../../contexts/accounts"; import { PublicKey } from "@solana/web3.js"; +import { Link } from "react-router-dom"; const { Text } = Typography; @@ -101,11 +102,13 @@ export const SideReserveOverview = (props: { justifyContent: "center", }} > - {" "} - {name} Reserve Overview + + {" "} + {name} Reserve Overview +
} > diff --git a/src/constants/labels.ts b/src/constants/labels.ts index 7222cff..83a4e97 100644 --- a/src/constants/labels.ts +++ b/src/constants/labels.ts @@ -23,7 +23,9 @@ export const LABELS = { TABLE_TITLE_ASSET: "Asset", TABLE_TITLE_LOAN_BALANCE: "Your loan balan", TABLE_TITLE_APY: "APY", + TABLE_TITLE_APR: "APR", TABLE_TITLE_ACTION: "Action", + TABLE_TITLE_MAX_BORROW: "Available fro you", DASHBOARD_TITLE_LOANS: "Loans", DASHBOARD_TITLE_DEPOSITS: "Deposts", WITHDRAW_ACTION: "Withdraw", diff --git a/src/contexts/lending.tsx b/src/contexts/lending.tsx index a5c4c3f..1c55998 100644 --- a/src/contexts/lending.tsx +++ b/src/contexts/lending.tsx @@ -18,6 +18,7 @@ import { } from "./accounts"; import { PublicKey } from "@solana/web3.js"; import { DexMarketParser } from "../models/dex"; +import { usePrecacheMarket } from "./market"; export interface LendingContextState {} @@ -39,14 +40,19 @@ export function LendingProvider({ children = null as any }) { export const useLending = () => { const connection = useConnection(); const [lendingAccounts, setLendingAccounts] = useState([]); + const precacheMarkets = usePrecacheMarket() + + // TODO: query for all the dex from reserves const processAccount = useCallback((item) => { if (isLendingReserve(item.account)) { - return cache.add( + const reserve = cache.add( item.pubkey.toBase58(), item.account, LendingReserveParser ); + + return reserve; } else if (isLendingMarket(item.account)) { return cache.add( item.pubkey.toBase58(), @@ -71,12 +77,14 @@ export const useLending = () => { .map(processAccount) .filter((item) => item !== undefined); + const lendingReserves = accounts + .filter( + (acc) => (acc?.info as LendingReserve).lendingMarket !== undefined + ) + .map((acc) => acc as ParsedAccount); + const toQuery = [ - ...accounts - .filter( - (acc) => (acc?.info as LendingReserve).lendingMarket !== undefined - ) - .map((acc) => acc as ParsedAccount) + ...lendingReserves .map((acc) => { const result = [ cache.registerParser( diff --git a/src/contexts/market.tsx b/src/contexts/market.tsx index 31cd01f..a28da5f 100644 --- a/src/contexts/market.tsx +++ b/src/contexts/market.tsx @@ -17,6 +17,8 @@ export interface MarketsContextState { marketByMint: Map; subscribeToMarket: (mint: string) => () => void; + + precacheMarkets: (mints: string[]) => void; } const REFRESH_INTERVAL = 30_000; @@ -28,17 +30,15 @@ const marketEmitter = new EventEmitter(); export function MarketProvider({ children = null as any }) { const { endpoint } = useConnectionConfig(); const accountsToObserve = useMemo(() => new Map(), []); + const [marketMints, setMarketMints] = useState(); const connection = useMemo(() => new Connection(endpoint, "recent"), [ endpoint, ]); - // TODO: identify which markets to query ... - const mints = useMemo(() => [] as PublicKey[], []); - const marketByMint = useMemo(() => { - return [...new Set(mints).values()].reduce((acc, key) => { - const mintAddress = key.toBase58(); + return [...new Set(marketMints).values()].reduce((acc, key) => { + const mintAddress = key; const SERUM_TOKEN = TOKEN_MINTS.find( (a) => a.address.toBase58() === mintAddress @@ -58,7 +58,7 @@ export function MarketProvider({ children = null as any }) { return acc; }, new Map()) as Map; - }, [mints]); + }, [marketMints]); useEffect(() => { let timer = 0; @@ -185,6 +185,10 @@ export function MarketProvider({ children = null as any }) { [marketByMint, accountsToObserve] ); + const precacheMarkets = useCallback((mints: string[]) => { + setMarketMints([...new Set([...marketMints, ...mints]).values()]) + }, [setMarketMints, marketMints]); + return ( {children} @@ -231,6 +236,11 @@ export const useMidPriceInUSD = (mint: string) => { return { price, isBase: price === 1.0 }; }; +export const usePrecacheMarket = () => { + const context = useMarkets(); + return context.precacheMarkets; +} + const getMidPrice = (marketAddress?: string, mintAddress?: string) => { const SERUM_TOKEN = TOKEN_MINTS.find( (a) => a.address.toBase58() === mintAddress diff --git a/src/models/lending/borrow.ts b/src/models/lending/borrow.ts index 7b36a57..163c750 100644 --- a/src/models/lending/borrow.ts +++ b/src/models/lending/borrow.ts @@ -7,8 +7,10 @@ import { import BN from "bn.js"; import * as BufferLayout from "buffer-layout"; import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids"; +import { WAD } from "../../constants/math"; import * as Layout from "./../../utils/layout"; import { LendingInstruction } from "./lending"; +import { LendingReserve } from "./reserve"; /// Borrow tokens from a reserve by depositing collateral tokens. The number of borrowed tokens /// is calculated by market price. The debt obligation is tokenized. @@ -98,3 +100,30 @@ export const borrowInstruction = ( data, }); }; + +// deposit APY utilization currentUtilizationRate * borrowAPY + +export const calculateBorrowAPY = (reserve: LendingReserve) => { + const totalBorrows = reserve.totalBorrowsWad.div(WAD).toNumber(); + const currentUtilization = + totalBorrows / (reserve.totalLiquidity.toNumber() + totalBorrows); + const optimalUtilization = reserve.config.optimalUtilizationRate; + let borrowAPY; + if (currentUtilization < optimalUtilization) { + const normalized_factor = currentUtilization / optimalUtilization; + const optimalBorrowRate = reserve.config.optimalBorrowRate / 100; + const minBorrowRate = reserve.config.minBorrowRate / 100; + borrowAPY = + normalized_factor * (optimalBorrowRate - minBorrowRate) + minBorrowRate; + } else { + const normalized_factor = + (currentUtilization - optimalUtilization) / (100 - optimalUtilization); + const optimalBorrowRate = reserve.config.optimalBorrowRate / 100; + const maxBorrowRate = reserve.config.maxBorrowRate / 100; + borrowAPY = + normalized_factor * (maxBorrowRate - optimalBorrowRate) + + optimalBorrowRate; + } + + return borrowAPY; +}; diff --git a/src/models/lending/deposit.ts b/src/models/lending/deposit.ts new file mode 100644 index 0000000..07e45ec --- /dev/null +++ b/src/models/lending/deposit.ts @@ -0,0 +1,73 @@ +import { + PublicKey, + SYSVAR_CLOCK_PUBKEY, + TransactionInstruction, +} from "@solana/web3.js"; +import BN from "bn.js"; +import * as BufferLayout from "buffer-layout"; +import { WAD } from "../../constants"; +import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids"; +import * as Layout from "./../../utils/layout"; +import { calculateBorrowAPY } from "./borrow"; +import { LendingInstruction } from "./lending"; +import { LendingReserve } from "./reserve"; + +/// Deposit liquidity into a reserve. The output is a collateral token representing ownership +/// of the reserve liquidity pool. +/// +/// 0. `[writable]` Liquidity input SPL Token account. $authority can transfer $liquidity_amount +/// 1. `[writable]` Collateral output SPL Token account, +/// 2. `[writable]` Reserve account. +/// 3. `[writable]` Reserve liquidity supply SPL Token account. +/// 4. `[writable]` Reserve collateral SPL Token mint. +/// 5. `[]` Derived lending market authority ($authority). +/// 6. `[]` Clock sysvar +/// 7. '[]` Token program id +export const depositInstruction = ( + liquidityAmount: number | BN, + from: PublicKey, // Liquidity input SPL Token account. $authority can transfer $liquidity_amount + to: PublicKey, // Collateral output SPL Token account, + reserveAuthority: PublicKey, + reserveAccount: PublicKey, + reserveSupply: PublicKey, + collateralMint: PublicKey +): TransactionInstruction => { + const dataLayout = BufferLayout.struct([ + BufferLayout.u8("instruction"), + Layout.uint64("liquidityAmount"), + ]); + + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: LendingInstruction.DepositReserveLiquidity, + liquidityAmount: new BN(liquidityAmount), + }, + data + ); + + const keys = [ + { pubkey: from, isSigner: false, isWritable: true }, + { pubkey: to, isSigner: false, isWritable: true }, + { pubkey: reserveAccount, isSigner: false, isWritable: true }, + { pubkey: reserveSupply, isSigner: false, isWritable: true }, + { pubkey: collateralMint, isSigner: false, isWritable: true }, + { pubkey: reserveAuthority, isSigner: false, isWritable: false }, + { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false }, + { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, + ]; + return new TransactionInstruction({ + keys, + programId: LENDING_PROGRAM_ID, + data, + }); +}; + +export const calculateDepositAPY = (reserve: LendingReserve) => { + const totalBorrows = reserve.totalBorrowsWad.div(WAD).toNumber(); + const currentUtilization = + totalBorrows / (reserve.totalLiquidity.toNumber() + totalBorrows); + + const borrowAPY = calculateBorrowAPY(reserve); + return currentUtilization * borrowAPY; +}; diff --git a/src/models/lending/index.ts b/src/models/lending/index.ts index 6da1134..a364a13 100644 --- a/src/models/lending/index.ts +++ b/src/models/lending/index.ts @@ -3,3 +3,5 @@ export * from "./reserve"; export * from "./obligation"; export * from "./lending"; export * from "./borrow"; +export * from "./deposit"; +export * from "./withdraw"; \ No newline at end of file diff --git a/src/models/lending/reserve.ts b/src/models/lending/reserve.ts index aea1e48..662e33c 100644 --- a/src/models/lending/reserve.ts +++ b/src/models/lending/reserve.ts @@ -7,7 +7,6 @@ import { } from "@solana/web3.js"; import BN from "bn.js"; import * as BufferLayout from "buffer-layout"; -import { WAD } from "../../constants"; import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids"; import * as Layout from "./../../utils/layout"; import { LendingInstruction } from "./lending"; @@ -169,119 +168,3 @@ export const initReserveInstruction = ( data, }); }; - -/// Deposit liquidity into a reserve. The output is a collateral token representing ownership -/// of the reserve liquidity pool. -/// -/// 0. `[writable]` Liquidity input SPL Token account. $authority can transfer $liquidity_amount -/// 1. `[writable]` Collateral output SPL Token account, -/// 2. `[writable]` Reserve account. -/// 3. `[writable]` Reserve liquidity supply SPL Token account. -/// 4. `[writable]` Reserve collateral SPL Token mint. -/// 5. `[]` Derived lending market authority ($authority). -/// 6. `[]` Clock sysvar -/// 7. '[]` Token program id -export const depositInstruction = ( - liquidityAmount: number | BN, - from: PublicKey, // Liquidity input SPL Token account. $authority can transfer $liquidity_amount - to: PublicKey, // Collateral output SPL Token account, - reserveAuthority: PublicKey, - reserveAccount: PublicKey, - reserveSupply: PublicKey, - collateralMint: PublicKey -): TransactionInstruction => { - const dataLayout = BufferLayout.struct([ - BufferLayout.u8("instruction"), - Layout.uint64("liquidityAmount"), - ]); - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: LendingInstruction.DepositReserveLiquidity, - liquidityAmount: new BN(liquidityAmount), - }, - data - ); - - const keys = [ - { pubkey: from, isSigner: false, isWritable: true }, - { pubkey: to, isSigner: false, isWritable: true }, - { pubkey: reserveAccount, isSigner: false, isWritable: true }, - { pubkey: reserveSupply, isSigner: false, isWritable: true }, - { pubkey: collateralMint, isSigner: false, isWritable: true }, - { pubkey: reserveAuthority, isSigner: false, isWritable: false }, - { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false }, - { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, - ]; - return new TransactionInstruction({ - keys, - programId: LENDING_PROGRAM_ID, - data, - }); -}; - -export const withdrawInstruction = ( - collateralAmount: number | BN, - from: PublicKey, // Collateral input SPL Token account. $authority can transfer $liquidity_amount - to: PublicKey, // Liquidity output SPL Token account, - reserveAccount: PublicKey, - collateralMint: PublicKey, - reserveSupply: PublicKey, - authority: PublicKey -): TransactionInstruction => { - const dataLayout = BufferLayout.struct([ - BufferLayout.u8("instruction"), - Layout.uint64("collateralAmount"), - ]); - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: LendingInstruction.WithdrawReserveLiquidity, - collateralAmount: new BN(collateralAmount), - }, - data - ); - - const keys = [ - { pubkey: from, isSigner: false, isWritable: true }, - { pubkey: to, isSigner: false, isWritable: true }, - { pubkey: reserveAccount, isSigner: false, isWritable: true }, - { pubkey: collateralMint, isSigner: false, isWritable: true }, - { pubkey: reserveSupply, isSigner: false, isWritable: true }, - { pubkey: authority, isSigner: false, isWritable: false }, - { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false }, - { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, - ]; - return new TransactionInstruction({ - keys, - programId: LENDING_PROGRAM_ID, - data, - }); -}; - -export const calculateBorrowAPY = (reserve: LendingReserve) => { - const totalBorrows = reserve.totalBorrowsWad.div(WAD).toNumber(); - const currentUtilization = - totalBorrows / (reserve.totalLiquidity.toNumber() + totalBorrows); - const optimalUtilization = reserve.config.optimalUtilizationRate; - let borrowAPY; - if (currentUtilization < optimalUtilization) { - const normalized_factor = currentUtilization / optimalUtilization; - const optimalBorrowRate = reserve.config.optimalBorrowRate / 100; - const minBorrowRate = reserve.config.minBorrowRate / 100; - borrowAPY = - normalized_factor * (optimalBorrowRate - minBorrowRate) + minBorrowRate; - } else { - const normalized_factor = - (currentUtilization - optimalUtilization) / (100 - optimalUtilization); - const optimalBorrowRate = reserve.config.optimalBorrowRate / 100; - const maxBorrowRate = reserve.config.maxBorrowRate / 100; - borrowAPY = - normalized_factor * (maxBorrowRate - optimalBorrowRate) + - optimalBorrowRate; - } - - return borrowAPY; -}; diff --git a/src/models/lending/withdraw.ts b/src/models/lending/withdraw.ts new file mode 100644 index 0000000..434c073 --- /dev/null +++ b/src/models/lending/withdraw.ts @@ -0,0 +1,50 @@ +import { + PublicKey, + SYSVAR_CLOCK_PUBKEY, + TransactionInstruction, +} from "@solana/web3.js"; +import BN from "bn.js"; +import * as BufferLayout from "buffer-layout"; +import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids"; +import * as Layout from "./../../utils/layout"; +import { LendingInstruction } from "./lending"; + +export const withdrawInstruction = ( + collateralAmount: number | BN, + from: PublicKey, // Collateral input SPL Token account. $authority can transfer $liquidity_amount + to: PublicKey, // Liquidity output SPL Token account, + reserveAccount: PublicKey, + collateralMint: PublicKey, + reserveSupply: PublicKey, + authority: PublicKey +): TransactionInstruction => { + const dataLayout = BufferLayout.struct([ + BufferLayout.u8("instruction"), + Layout.uint64("collateralAmount"), + ]); + + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: LendingInstruction.WithdrawReserveLiquidity, + collateralAmount: new BN(collateralAmount), + }, + data + ); + + const keys = [ + { pubkey: from, isSigner: false, isWritable: true }, + { pubkey: to, isSigner: false, isWritable: true }, + { pubkey: reserveAccount, isSigner: false, isWritable: true }, + { pubkey: collateralMint, isSigner: false, isWritable: true }, + { pubkey: reserveSupply, isSigner: false, isWritable: true }, + { pubkey: authority, isSigner: false, isWritable: false }, + { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false }, + { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, + ]; + return new TransactionInstruction({ + keys, + programId: LENDING_PROGRAM_ID, + data, + }); +}; \ No newline at end of file diff --git a/src/views/borrow/index.tsx b/src/views/borrow/index.tsx index a4599fe..a78846a 100644 --- a/src/views/borrow/index.tsx +++ b/src/views/borrow/index.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { LABELS } from "../../constants"; import { useLendingReserves } from "../../hooks"; import { BorrowItem } from "./item"; import "./itemStyle.less"; @@ -8,10 +9,10 @@ export const BorrowView = () => { return (
-
Asset
-
Available fro you
-
APY
-
Action
+
{LABELS.TABLE_TITLE_ASSET}
+
{LABELS.TABLE_TITLE_MAX_BORROW}
+
{LABELS.TABLE_TITLE_APR}
+
{LABELS.TABLE_TITLE_ACTION}
{reserveAccounts.map((account) => ( diff --git a/src/views/home/item.tsx b/src/views/home/item.tsx index 350d611..b61cd71 100644 --- a/src/views/home/item.tsx +++ b/src/views/home/item.tsx @@ -1,6 +1,6 @@ import React, { useMemo } from "react"; import { useTokenName } from "../../hooks"; -import { calculateBorrowAPY, LendingReserve } from "../../models/lending"; +import { calculateBorrowAPY, calculateDepositAPY, LendingReserve } from "../../models/lending"; import { TokenIcon } from "../../components/TokenIcon"; import { wadToLamports, @@ -36,6 +36,12 @@ export const LendingReserveItem = (props: { props.reserve, ]); + const depositAPY = useMemo(() => calculateDepositAPY(props.reserve), [ + props.reserve, + ]); + + const marketSize = totalLiquidity+totalBorrows; + return ( @@ -44,14 +50,18 @@ export const LendingReserveItem = (props: { {name} -
- {formatNumber.format(totalLiquidity+totalBorrows)} {name} +
+ {formatNumber.format(marketSize)} {name}
-
+
{formatNumber.format(totalBorrows)} {name}
-
--
-
{formatPct.format(borrowAPY)}
+
+ {formatPct.format(depositAPY)} +
+
+ {formatPct.format(borrowAPY)} +