add Borrows page and health ratio

This commit is contained in:
Tyler Shipe 2021-07-05 11:03:57 -04:00
parent 671e50eba0
commit c355f7132e
14 changed files with 357 additions and 457 deletions

View File

@ -1,6 +1,7 @@
import { AccountInfo, PublicKey, Transaction } from '@solana/web3.js'
import { Market, OpenOrders } from '@project-serum/serum'
import { Event } from '@project-serum/serum/lib/queue'
import { I80F48 } from '@blockworks-foundation/mango-client/lib/src/fixednum'
export interface MarketInfo {
address: PublicKey
@ -34,7 +35,7 @@ export interface Trade extends Event {
interface BalancesBase {
key: string
coin: string
symbol: string
wallet?: number | null | undefined
orders?: number | null | undefined
openOrders?: OpenOrders | null | undefined
@ -43,8 +44,8 @@ interface BalancesBase {
export interface Balances extends BalancesBase {
market?: Market | null | undefined
marginDeposits?: number | null | undefined
borrows?: number | null | undefined
marginDeposits?: I80F48 | null | undefined
borrows?: I80F48 | null | undefined
net?: number | null | undefined
}

View File

@ -79,7 +79,7 @@ const BalancesTable = () => {
scope="col"
className={`px-6 py-2 text-left font-normal`}
>
Coin
Asset
</Th>
<Th
scope="col"
@ -124,17 +124,17 @@ const BalancesTable = () => {
<Td
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.coin}
{balance.symbol}
</Td>
<Td
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.marginDeposits}
{balance.marginDeposits.toString()}
</Td>
<Td
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.borrows}
{balance.borrows.toString()}
</Td>
<Td
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1`}

View File

@ -1,12 +1,13 @@
// import { useEffect, useState, useMemo } from 'react'
// import { groupBy } from '../utils'
// import useTradeHistory from '../hooks/useTradeHistory'
import { I80F48 } from '@blockworks-foundation/mango-client/lib/src/fixednum'
import useMangoStore, { mangoClient } from '../stores/useMangoStore'
import { HeartIcon } from '@heroicons/react/outline'
import { ZERO_I80F48 } from '@blockworks-foundation/mango-client/lib/src/fixednum'
import useMangoStore from '../stores/useMangoStore'
import FloatingElement from './FloatingElement'
import Tooltip from './Tooltip'
// import Button from './Button'
// import AlertsModal from './AlertsModal'
let initHealth = ZERO_I80F48
let maintHealth = ZERO_I80F48
let maintHealthRatio = 0
let initHealthRatio = 0
export default function MarginInfo() {
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
@ -16,8 +17,6 @@ export default function MarginInfo() {
)
// TODO move out of component
let initHealth = I80F48.fromString('0')
let maintHealth = I80F48.fromString('0')
if (selectedMangoAccount) {
initHealth = selectedMangoAccount.getHealth(mangoGroup, mangoCache, 'Init')
maintHealth = selectedMangoAccount.getHealth(
@ -25,110 +24,71 @@ export default function MarginInfo() {
mangoCache,
'Maint'
)
}
console.log('init health: ', initHealth.toString())
console.log('maint health: ', maintHealth.toString())
// const tradeHistory = useTradeHistory()
// const tradeHistoryLength = useMemo(() => tradeHistory.length, [tradeHistory])
const initliabsVal = selectedMangoAccount.getNativeLiabsVal(
mangoGroup,
mangoCache,
'Init'
)
const maintliabsVal = selectedMangoAccount.getNativeLiabsVal(
mangoGroup,
mangoCache,
'Init'
)
maintHealthRatio = maintliabsVal.gt(ZERO_I80F48)
? maintHealth.div(maintliabsVal).toNumber() * 100
: 100
initHealthRatio = initliabsVal.gt(ZERO_I80F48)
? initHealth.div(initliabsVal).toNumber() * 100
: 100
console.log('init health ratio: ', `${initHealthRatio.toFixed(1)}%`)
}
return (
<FloatingElement>
<>
<div className={`flex justify-between pt-2 pb-2`}>
<Tooltip content="Account health">
<div
className={`cursor-help font-normal text-th-fgd-4 border-b border-th-fgd-4 border-dashed border-opacity-20 leading-4 default-transition hover:border-th-bkg-2 hover:text-th-fgd-3`}
>
Init Health
</div>
</Tooltip>
<div className={`text-th-fgd-1`}>{initHealth.toFixed(3)}</div>
</div>
<div className={`flex justify-between pt-2 pb-2`}>
<Tooltip content="Account health">
<div
className={`cursor-help font-normal text-th-fgd-4 border-b border-th-fgd-4 border-dashed border-opacity-20 leading-4 default-transition hover:border-th-bkg-2 hover:text-th-fgd-3`}
>
Maint Health
</div>
</Tooltip>
<div className={`text-th-fgd-1`}>{maintHealth.toFixed(3)}</div>
</div>
<div className="flex flex-col h-full justify-between">
{selectedMangoAccount ? (
<>
<div>
<div className={`flex justify-between pt-2 pb-2`}>
<Tooltip content="Account health">
<div
className={`cursor-help font-normal text-th-fgd-4 border-b border-th-fgd-4 border-dashed border-opacity-20 leading-4 default-transition hover:border-th-bkg-2 hover:text-th-fgd-3`}
>
Assets Val
Init Ratio
</div>
</Tooltip>
<div className={`text-th-fgd-1`}>
{selectedMangoAccount
.getAssetsVal(mangoGroup, mangoCache)
.toString()}
{initHealthRatio.toFixed(2)}%
</div>
</div>
<div className={`flex justify-between pt-2 pb-2`}>
<Tooltip content="Account health">
<div
className={`cursor-help font-normal text-th-fgd-4 border-b border-th-fgd-4 border-dashed border-opacity-20 leading-4 default-transition hover:border-th-bkg-2 hover:text-th-fgd-3`}
>
Liabs Val
</div>
</Tooltip>
<div className={`text-th-fgd-1`}>
{selectedMangoAccount
.getLiabsVal(mangoGroup, mangoCache)
.toString()}
</div>
</div>
<div className={`flex justify-between pt-2 pb-2`}>
<Tooltip content="Account health">
<div
className={`cursor-help font-normal text-th-fgd-4 border-b border-th-fgd-4 border-dashed border-opacity-20 leading-4 default-transition hover:border-th-bkg-2 hover:text-th-fgd-3`}
>
Equity
</div>
</Tooltip>
<div className={`text-th-fgd-1`}>
{selectedMangoAccount
.computeValue(mangoGroup, mangoCache)
.toString()}
</div>
</div>
<div className={`flex justify-between pt-2 pb-2`}>
<Tooltip content="Account health">
<div
className={`cursor-help font-normal text-th-fgd-4 border-b border-th-fgd-4 border-dashed border-opacity-20 leading-4 default-transition hover:border-th-bkg-2 hover:text-th-fgd-3`}
>
BTC Price
</div>
</Tooltip>
<div className={`text-th-fgd-1`}>
{mangoGroup.getPrice(0, mangoCache).toString()}
</div>
</div>
</>
</div>
) : null}
{/* <Button
className="mt-4 w-full"
disabled={!connected}
onClick={() => setOpenAlertModal(true)}
>
Create Liquidation Alert
</Button>
{openAlertModal ? (
<AlertsModal
isOpen={openAlertModal}
onClose={() => setOpenAlertModal(false)}
mangoAccount={selectedMangoAccount}
/>
) : null} */}
</>
<div className="bg-th-bkg-1 p-4 rounded">
<div className="flex flex-col">
<div className="flex justify-between">
<div className="flex items-center">
<HeartIcon className="h-5 w-5" aria-hidden="true" />
<span className="ml-2">Health Ratio</span>
</div>
<div className="text-right">{maintHealthRatio.toFixed(1)}%</div>
</div>
<div className="mt-4">
<div className="h-2 flex rounded bg-th-bkg-3">
<div
style={{ width: `${maintHealthRatio.toFixed(1)}%` }}
className="flex rounded bg-th-primary"
></div>
</div>
</div>
</div>
</div>
</div>
</FloatingElement>
)
}

View File

@ -5,10 +5,10 @@ import useInterval from '../hooks/useInterval'
import ChartApi from '../utils/chartDataConnector'
import UiLock from './UiLock'
import ManualRefresh from './ManualRefresh'
// import useOraclePrice from '../hooks/useOraclePrice'
import useOraclePrice from '../hooks/useOraclePrice'
const MarketHeader = () => {
// const oraclePrice = useOraclePrice()
const oraclePrice = useOraclePrice()
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
const baseSymbol = marketConfig.baseSymbol
const selectedMarketName = marketConfig.name
@ -95,7 +95,7 @@ const MarketHeader = () => {
<div className="pr-4 sm:pr-0 sm:w-24">
<div className="text-th-fgd-4 text-xs">Oracle price</div>
<div className="font-semibold mt-0.5">
{/* {oraclePrice ? oraclePrice.toFixed(2) : '--'} */}
{oraclePrice ? oraclePrice.toFixed(2) : '--'}
</div>
</div>
<div className="pr-4 sm:pr-0 sm:w-24">

View File

@ -2,20 +2,24 @@ import { useCallback, useEffect, useState } from 'react'
import { getDecimalCount } from '../utils'
import { ChartTradeType } from '../@types/types'
import FloatingElement from './FloatingElement'
import useMarket from '../hooks/useMarket'
import useInterval from '../hooks/useInterval'
import ChartApi from '../utils/chartDataConnector'
import { ElementTitle } from './styles'
import { isEqual } from '../utils/index'
import useMangoStore from '../stores/useMangoStore'
export default function PublicTrades() {
const { baseCurrency, quoteCurrency, market, marketAddress } = useMarket()
export default function RecentMarketTrades() {
const mangoConfig = useMangoStore((s) => s.selectedMangoGroup.config)
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
const market = useMangoStore((s) => s.selectedMarket.current)
const [trades, setTrades] = useState([])
const fetchTradesForChart = useCallback(async () => {
if (!marketAddress) return
if (!marketConfig) return
const newTrades = await ChartApi.getRecentTrades(marketAddress)
const newTrades = await ChartApi.getRecentTrades(
marketConfig.publicKey.toString()
)
if (!newTrades) return null
if (newTrades.length && trades.length === 0) {
setTrades(newTrades)
@ -25,7 +29,7 @@ export default function PublicTrades() {
) {
setTrades(newTrades)
}
}, [marketAddress, trades])
}, [marketConfig, trades])
useEffect(() => {
fetchTradesForChart()
@ -39,8 +43,8 @@ export default function PublicTrades() {
<FloatingElement>
<ElementTitle>Recent Trades</ElementTitle>
<div className={`grid grid-cols-3 text-th-fgd-4 mb-2 text-xs`}>
<div>Price ({quoteCurrency}) </div>
<div className={`text-right`}>Size ({baseCurrency})</div>
<div>Price ({mangoConfig.quoteSymbol}) </div>
<div className={`text-right`}>Size ({marketConfig.baseSymbol})</div>
<div className={`text-right`}>Time</div>
</div>
{!!trades.length && (

View File

@ -28,9 +28,9 @@ const TopBar = () => {
className={`hidden md:flex md:items-center md:space-x-6 md:ml-4 py-2`}
>
<MenuItem href="/">Trade</MenuItem>
{/* <MenuItem href="/account">Account</MenuItem>
{/* <MenuItem href="/account">Account</MenuItem> */}
<MenuItem href="/borrow">Borrow</MenuItem>
<MenuItem href="/alerts">Alerts</MenuItem>
{/* <MenuItem href="/alerts">Alerts</MenuItem>
<MenuItem href="/stats">Stats</MenuItem> */}
<MenuItem href="https://docs.mango.markets/">Learn</MenuItem>
</div>

View File

@ -2,9 +2,9 @@ import { useState } from 'react'
import FloatingElement from './FloatingElement'
import OpenOrdersTable from './OpenOrdersTable'
import BalancesTable from './BalancesTable'
// import PositionsTable from './PositionsTable'
import PositionsTable from './PositionsTable'
import TradeHistoryTable from './TradeHistoryTable'
import { Position } from '../public/charting_library/charting_library'
// import { Position } from '../public/charting_library/charting_library'
// import FeeDiscountsTable from './FeeDiscountsTable'
const TABS = [

View File

@ -54,8 +54,6 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
const actions = useMangoStore((s) => s.actions)
const selectedMangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
console.log('mg: ', selectedMangoGroup)
const selectedMangoAccount = useMangoStore(
(s) => s.selectedMangoAccount.current
)
@ -69,8 +67,6 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
)
const tokenIndex = selectedMangoGroup.getTokenIndex(token.mintKey)
console.log('mango Acc', selectedMangoAccount)
useEffect(() => {
if (!selectedMangoGroup || !selectedMangoAccount || !withdrawTokenSymbol)
return
@ -92,33 +88,18 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
)
const currentAssetsVal = selectedMangoAccount
.getAssetsVal(selectedMangoGroup, mangoCache)
.getAssetsVal(selectedMangoGroup, mangoCache, 'Init')
.sub(maxValForSelectedAsset)
const currentLiabsVal = selectedMangoAccount.getLiabsVal(
selectedMangoGroup,
mangoCache
mangoCache,
'Init'
)
const liabsAvail = currentAssetsVal.sub(currentLiabsVal)
// calculate max withdraw amount
console.log('currentAssetsVal', currentAssetsVal.toString())
// console.log(
// 'current assets / 1.2 == ',
// currentAssetsVal.div(I80F48.fromNumber(1.19)).toString()
// )
console.log('currentLiabsVal', currentLiabsVal.toString())
console.log('========================================')
console.log('liabsAvail', liabsAvail.toString())
console.log(
'selected asset price',
selectedMangoGroup.getPrice(tokenIndex, mangoCache)?.toString() ||
ONE_I80F48.toString()
)
const amountToWithdraw = includeBorrow
? liabsAvail
.div(
@ -127,13 +108,6 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
.add(getDepositsForSelectedAsset())
: getDepositsForSelectedAsset()
// liabsVal = equity * 5
// 73550 =
// health
// 0 = quoteDeposits - quoteBorrows + spotHealth
console.log('amount to withdraw', amountToWithdraw.toString())
if (amountToWithdraw.gt(I80F48.fromNumber(0))) {
setMaxAmount(amountToWithdraw.toNumber())
} else {
@ -164,9 +138,16 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
.div(I80F48.fromNumber(Math.pow(10, mintDecimals)))
.div(mangoCache.rootBankCache[tokenIndex].borrowIndex)
const equity = simulation.computeValue(selectedMangoGroup, mangoCache)
const assetsVal = simulation.getAssetsVal(selectedMangoGroup, mangoCache)
const liabsVal = simulation.getLiabsVal(selectedMangoGroup, mangoCache)
const assetsVal = simulation.getAssetsVal(
selectedMangoGroup,
mangoCache,
'Init'
)
const liabsVal = simulation.getLiabsVal(
selectedMangoGroup,
mangoCache,
'Init'
)
// const collateralRatio = simulation.getCollateralRatio(
// selectedMangoGroup,
// prices
@ -174,7 +155,6 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
// const leverage = 1 / Math.max(0, collateralRatio - 1)
setSimulation({
equity,
assetsVal,
liabsVal,
})

View File

@ -1,80 +1,33 @@
import { useCallback, useState } from 'react'
import { Table, Thead, Tbody, Tr, Th, Td } from 'react-super-responsive-table'
import useConnection from '../../hooks/useConnection'
import {
getTokenBySymbol,
ZERO_I80F48,
} from '@blockworks-foundation/mango-client'
import useMangoStore from '../../stores/useMangoStore'
import useMarketList from '../../hooks/useMarketList'
import { useBalances } from '../../hooks/useBalances'
import { notify } from '../../utils/notifications'
import { sleep } from '../../utils'
import { PublicKey } from '@solana/web3.js'
import { tokenPrecision } from '../../utils/index'
// import { settleBorrow } from '../../utils/mango'
import BorrowModal from '../BorrowModal'
import Button from '../Button'
import DepositModal from '../DepositModal'
import { QUOTE_INDEX } from '@blockworks-foundation/mango-client/lib/src/MangoGroup'
export default function AccountBorrows() {
const balances = useBalances()
const { programId, connection } = useConnection()
const actions = useMangoStore((s) => s.actions)
const selectedMangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const selectedMangoAccount = useMangoStore(
(s) => s.selectedMangoAccount.current
)
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache)
const mangoConfig = useMangoStore((s) => s.selectedMangoGroup.config)
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
const loadingMangoAccount = useMangoStore(
(s) => s.selectedMangoAccount.initialLoad
)
const connected = useMangoStore((s) => s.wallet.connected)
const { symbols } = useMarketList()
const prices = useMangoStore((s) => s.selectedMangoGroup.prices)
const [borrowSymbol, setBorrowSymbol] = useState('')
const [depositToSettle, setDepositToSettle] = useState(null)
const [showBorrowModal, setShowBorrowModal] = useState(false)
const [showDepositModal, setShowDepositModal] = useState(false)
async function handleSettleBorrow(token, borrowQuantity, depositBalance) {
const mangoAccount = useMangoStore.getState().selectedMangoAccount.current
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
const wallet = useMangoStore.getState().wallet.current
if (borrowQuantity > depositBalance) {
const deficit = borrowQuantity - depositBalance
handleShowDeposit(token, deficit)
return
}
try {
await settleBorrow(
connection,
new PublicKey(programId),
mangoGroup,
mangoAccount,
wallet,
new PublicKey(symbols[token]),
Number(borrowQuantity)
)
await sleep(250)
actions.fetchMangoAccounts()
} catch (e) {
console.warn('Error settling all:', e)
if (e.message === 'No unsettled borrows') {
notify({
message: 'There are no unsettled borrows',
type: 'error',
})
} else {
notify({
message: 'Error settling borrows',
description: e.message,
txid: e.txid,
type: 'error',
})
}
}
}
const handleCloseWithdraw = useCallback(() => {
setShowBorrowModal(false)
}, [])
@ -89,12 +42,8 @@ export default function AccountBorrows() {
setShowBorrowModal(true)
}
const handleShowDeposit = (symbol, deficit) => {
setDepositToSettle({ symbol, deficit })
setShowDepositModal(true)
}
console.log(depositToSettle)
// console.log(depositToSettle)
console.log('balances', balances)
return (
<>
@ -103,15 +52,12 @@ export default function AccountBorrows() {
<div className="border border-th-red flex items-center justify-between p-2 rounded">
<div className="pr-4 text-xs text-th-fgd-3">Total Borrow Value:</div>
<span>
$
{balances
.reduce((acc, d, i) => acc + d.borrows * prices[i], 0)
.toFixed(2)}
${mangoAccount.getLiabsVal(mangoGroup, mangoCache).toFixed(2)}
</span>
</div>
</div>
{selectedMangoGroup ? (
balances.find((b) => b.borrows > 0) ? (
{mangoGroup ? (
balances.find((b) => b.borrows.gt(ZERO_I80F48)) ? (
<Table className="min-w-full divide-y divide-th-bkg-2">
<Thead>
<Tr className="text-th-fgd-3 text-xs">
@ -131,94 +77,73 @@ export default function AccountBorrows() {
</Thead>
<Tbody>
{balances
.filter((assets) => assets.borrows > 0)
.map((asset, i) => (
<Tr
key={`${i}`}
className={`border-b border-th-bkg-3
.filter((assets) => assets.borrows.gt(ZERO_I80F48))
.map((asset, i) => {
const token = getTokenBySymbol(mangoConfig, asset.symbol)
console.log('token', mangoConfig, asset.symbol)
const tokenIndex = mangoGroup.getTokenIndex(token.mintKey)
return (
<Tr
key={tokenIndex}
className={`border-b border-th-bkg-3
${i % 2 === 0 ? `bg-th-bkg-3` : `bg-th-bkg-2`}
`}
>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<div className="flex items-center">
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${asset.coin.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
<div>{asset.coin}</div>
</div>
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
{asset.borrows.toFixed(tokenPrecision[asset.coin])}
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
$
{(
asset.borrows *
prices[
Object.keys(symbols).findIndex(
(key) => key === asset.coin
)
]
).toFixed(tokenPrecision[asset.coin])}
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<span className={`text-th-red`}>
{(
selectedMangoGroup
.getBorrowRate(
Object.keys(symbols).findIndex(
(key) => key === asset.coin
)
)
.toNumber() * 100
).toFixed(2)}
%
</span>
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<div className={`flex justify-end`}>
<Button
onClick={() =>
handleSettleBorrow(
asset.coin,
asset.borrows,
asset.marginDeposits
)
}
className="text-xs pt-0 pb-0 h-8 pl-3 pr-3"
disabled={
!connected ||
!selectedMangoAccount ||
loadingMangoAccount
}
>
Settle
</Button>
<Button
onClick={() => handleShowBorrow(asset.coin)}
className="ml-3 text-xs pt-0 pb-0 h-8 pl-3 pr-3"
disabled={!connected || loadingMangoAccount}
>
Borrow
</Button>
</div>
</Td>
</Tr>
))}
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<div className="flex items-center">
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${asset.symbol.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
<div>{asset.symbol}</div>
</div>
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
{asset.borrows.toFixed(tokenPrecision[asset.symbol])}
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
$
{asset.borrows
.mul(mangoGroup.getPrice(tokenIndex, mangoCache))
.toFixed(tokenPrecision[asset.symbol])}
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<span className={`text-th-red`}>
{(
mangoGroup.getBorrowRate(tokenIndex).toNumber() *
100
).toFixed(2)}
%
</span>
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<div className={`flex justify-end`}>
<Button
onClick={() => handleShowBorrow(asset.symbol)}
className="ml-3 text-xs pt-0 pb-0 h-8 pl-3 pr-3"
disabled={!connected || loadingMangoAccount}
>
Borrow
</Button>
</div>
</Td>
</Tr>
)
})}
</Tbody>
</Table>
) : (
@ -248,62 +173,66 @@ export default function AccountBorrows() {
</Tr>
</Thead>
<Tbody>
{Object.entries(symbols).map(([asset], i) => (
<Tr
key={`${i}`}
className={`border-b border-th-bkg-3
{mangoConfig.tokens.map((token, i) => {
const tokenIndex = mangoGroup.getTokenIndex(token.mintKey)
return (
<Tr
key={`${i}`}
className={`border-b border-th-bkg-3
${i % 2 === 0 ? `bg-th-bkg-3` : `bg-th-bkg-2`}
`}
>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<div className="flex items-center">
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${asset.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
<div>{asset}</div>
</div>
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
${prices[i]}
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<span className={`text-th-red`}>
{(selectedMangoGroup.getBorrowRate(i) * 100).toFixed(2)}%
</span>
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
{(
selectedMangoGroup.getUiTotalDeposit(i) -
selectedMangoGroup.getUiTotalBorrow(i)
).toLocaleString()}
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<div className={`flex justify-end`}>
<Button
onClick={() => handleShowBorrow(asset)}
className="text-xs pt-0 pb-0 h-8 pl-3 pr-3"
disabled={!connected || loadingMangoAccount}
>
Borrow
</Button>
</div>
</Td>
</Tr>
))}
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<div className="flex items-center">
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${token.symbol.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
<div>{token.symbol}</div>
</div>
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
${mangoGroup.getPrice(tokenIndex, mangoCache).toFixed(2)}
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<span className={`text-th-red`}>
{mangoGroup.getBorrowRate(tokenIndex).toFixed(2)}%
</span>
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
$
{mangoGroup
.getUiTotalDeposit(tokenIndex)
.sub(mangoGroup.getUiTotalBorrow(tokenIndex))
.toFixed(2)}
</Td>
<Td
className={`px-6 py-3 whitespace-nowrap text-sm text-th-fgd-1`}
>
<div className={`flex justify-end`}>
<Button
onClick={() => handleShowBorrow(token.symbol)}
className="text-xs pt-0 pb-0 h-8 pl-3 pr-3"
disabled={!connected || loadingMangoAccount}
>
Borrow
</Button>
</div>
</Td>
</Tr>
)
})}
</Tbody>
</Table>
{showBorrowModal && (

View File

@ -1,13 +1,6 @@
import { Balances } from '../@types/types'
import { nativeToUi } from '@blockworks-foundation/mango-client'
import useMarketList from './useMarketList'
import useMangoStore from '../stores/useMangoStore'
import {
// displayBorrowsForMangoAccount,
// displayDepositsForMangoAccount,
floorToDecimal,
} from '../utils'
import useAllMarkets from './useAllMarkets'
import { sumBy } from 'lodash'
import { QUOTE_INDEX } from '@blockworks-foundation/mango-client/lib/src/MangoGroup'
import { I80F48 } from '@blockworks-foundation/mango-client/lib/src/fixednum'
@ -71,21 +64,17 @@ export function useBalances(): Balances[] {
{
market: null,
key: `${baseSymbol}${name}`,
coin: baseSymbol,
marginDeposits: mangoAccount
.getUiDeposit(
mangoCache.rootBankCache[tokenIndex],
mangoGroup,
tokenIndex
)
.toString(),
borrows: mangoAccount
.getUiBorrow(
mangoCache.rootBankCache[tokenIndex],
mangoGroup,
tokenIndex
)
.toString(),
symbol: baseSymbol,
marginDeposits: mangoAccount.getUiDeposit(
mangoCache.rootBankCache[tokenIndex],
mangoGroup,
tokenIndex
),
borrows: mangoAccount.getUiBorrow(
mangoCache.rootBankCache[tokenIndex],
mangoGroup,
tokenIndex
),
orders: nativeToUi(
nativeBaseLocked,
mangoGroup.tokens[tokenIndex].decimals
@ -99,21 +88,17 @@ export function useBalances(): Balances[] {
{
market: null,
key: `${name}`,
coin: mangoGroupConfig.quoteSymbol,
marginDeposits: mangoAccount
.getUiDeposit(
mangoCache.rootBankCache[quoteCurrencyIndex],
mangoGroup,
quoteCurrencyIndex
)
.toString(),
borrows: mangoAccount
.getUiBorrow(
mangoCache.rootBankCache[tokenIndex],
mangoGroup,
quoteCurrencyIndex
)
.toString(),
symbol: mangoGroupConfig.quoteSymbol,
marginDeposits: mangoAccount.getUiDeposit(
mangoCache.rootBankCache[quoteCurrencyIndex],
mangoGroup,
quoteCurrencyIndex
),
borrows: mangoAccount.getUiBorrow(
mangoCache.rootBankCache[tokenIndex],
mangoGroup,
quoteCurrencyIndex
),
orders: nativeToUi(
nativeQuoteLocked,
mangoGroup.tokens[quoteCurrencyIndex].decimals
@ -129,25 +114,27 @@ export function useBalances(): Balances[] {
}
const baseBalances = balances.map((b) => b[0])
// const quoteBalances = balances.map((b) => b[1])
// const quoteMeta = quoteBalances[0]
// const quoteInOrders = sumBy(quoteBalances, 'orders')
// const unsettled = sumBy(quoteBalances, 'unsettled')
// const net =
// quoteMeta.marginDeposits + unsettled - quoteMeta.borrows - quoteInOrders
const quoteBalances = balances.map((b) => b[1])
const quoteMeta = quoteBalances[0]
const quoteInOrders = sumBy(quoteBalances, 'orders')
const unsettled = sumBy(quoteBalances, 'unsettled')
const net = quoteMeta.marginDeposits
.add(I80F48.fromNumber(unsettled))
.sub(quoteMeta.borrows)
.sub(I80F48.fromNumber(quoteInOrders))
// return baseBalances.concat([
// {
// market: null,
// key: `${quoteMeta.coin}${quoteMeta.coin}`,
// coin: quoteMeta.coin,
// marginDeposits: quoteMeta.marginDeposits,
// borrows: quoteMeta.borrows,
// orders: quoteInOrders,
// unsettled,
// net: floorToDecimal(net, mangoGroup.tokens[QUOTE_INDEX].decimals),
// },
// ])
return baseBalances.concat([
{
market: null,
key: `${quoteMeta.symbol}${quoteMeta.symbol}`,
symbol: quoteMeta.symbol,
marginDeposits: quoteMeta.marginDeposits,
borrows: quoteMeta.borrows,
orders: quoteInOrders,
unsettled,
net: net.toString(),
},
])
return baseBalances
}

View File

@ -1,37 +1,28 @@
import { useCallback, useEffect, useState } from 'react'
import useMangoStore from '../stores/useMangoStore'
import useConnection from './useConnection'
import useInterval from './useInterval'
import useMarket from './useMarket'
import useMarketList from './useMarketList'
const SECONDS = 1000
export default function useOraclePrice() {
const selectedMangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const { connection } = useConnection()
const { marketAddress } = useMarket()
const { getMarketIndex } = useMarketList()
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache)
const selectedMarket = useMangoStore((s) => s.selectedMarket.config)
const [oraclePrice, setOraclePrice] = useState(null)
const fetchOraclePrice = useCallback(() => {
if (selectedMangoGroup) {
if (mangoGroup) {
setOraclePrice(null)
const marketIndex = getMarketIndex(marketAddress)
selectedMangoGroup.getPrices(connection).then((prices) => {
const oraclePriceForMarket = prices[marketIndex]
setOraclePrice(oraclePriceForMarket)
})
let marketIndex = 0
if (selectedMarket.kind === 'spot') {
marketIndex = mangoGroup.getSpotMarketIndex(selectedMarket.publicKey)
} else {
marketIndex = mangoGroup.getPerpMarketIndex(selectedMarket.publicKey)
}
setOraclePrice(mangoGroup.getPrice(marketIndex, mangoCache))
}
}, [selectedMangoGroup, marketAddress])
}, [mangoGroup, selectedMarket, mangoCache])
useEffect(() => {
fetchOraclePrice()
}, [fetchOraclePrice])
useInterval(() => {
fetchOraclePrice()
}, 20 * SECONDS)
return oraclePrice
}

View File

@ -89,7 +89,7 @@
"postcss": "^8.2.8",
"prettier": "^2.0.2",
"react-is": "^17.0.2",
"tailwindcss": "^2.1.2",
"tailwindcss": "^2.2.4",
"twin.macro": "^2.4.1",
"typescript": "^4.1.3"
}

View File

@ -261,7 +261,7 @@ const useMangoStore = create<MangoStore>((set, get) => ({
.sort((a, b) =>
a.publicKey.toBase58() > b.publicKey.toBase58() ? 1 : -1
)
console.log('margin acc: ', sortedAccounts[0])
console.log('mango acc: ', sortedAccounts[0])
set((state) => {
state.mangoAccounts = sortedAccounts

View File

@ -988,7 +988,7 @@
"@blockworks-foundation/mango-client@git+https://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:x-oauth-basic@github.com/blockworks-foundation/mango-client-v3.git#ts/mangoacc":
version "3.0.1"
resolved "git+https://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:x-oauth-basic@github.com/blockworks-foundation/mango-client-v3.git#4cf0e96e0982363028844592c52c1ce90bd7c70d"
resolved "git+https://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:x-oauth-basic@github.com/blockworks-foundation/mango-client-v3.git#6dae67d3fc6f6c31f5b91618d7bc0565ffc84686"
dependencies:
"@project-serum/serum" "^0.13.38"
"@project-serum/sol-wallet-adapter" "^0.2.0"
@ -1487,12 +1487,12 @@
eventemitter3 "^4.0.4"
"@project-serum/sol-wallet-adapter@^0.2.0":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@project-serum/sol-wallet-adapter/-/sol-wallet-adapter-0.2.4.tgz#205adc303aea38d6c2043cb644fd70ba2fa758e0"
integrity sha512-NlFD5FumLddrHsRB1Dco7adiproUvXFC8kRxNg8ptEMD7k9p5gr8iKf6t06eARZAWQ7r90Bq1gEJc9GHlZ0c4Q==
version "0.2.5"
resolved "https://registry.yarnpkg.com/@project-serum/sol-wallet-adapter/-/sol-wallet-adapter-0.2.5.tgz#d949a8cc4d2f74e31e4f0205989703977689c76b"
integrity sha512-Y0XHe+FXXJ7P8XZtx3luAlatO0ge2LdrZUCmqMSzJf+K+fko+qTYIBSUuWwO7y/O4brIXVReR1mEUvF6QKDF2w==
dependencies:
bs58 "^4.0.1"
eventemitter3 "^4.0.4"
eventemitter3 "^4.0.7"
"@sinonjs/commons@^1.7.0":
version "1.8.3"
@ -1850,9 +1850,9 @@
integrity sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==
"@types/node@*":
version "15.12.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.5.tgz#9a78318a45d75c9523d2396131bd3cca54b2d185"
integrity sha512-se3yX7UHv5Bscf8f1ERKvQOD6sTyycH3hdaoozvaLxgUiY5lIGEeH37AD0G0Qi9kPqihPn0HOfd2yaIEN9VwEg==
version "16.0.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.0.0.tgz#067a6c49dc7a5c2412a505628e26902ae967bf6f"
integrity sha512-TmCW5HoZ2o2/z2EYi109jLqIaPIi9y/lc2LmDCWzuCi35bcaQ+OtUh6nwBiFK7SOu25FAU5+YKdqFZUwtqGSdg==
"@types/node@^12.12.54":
version "12.20.15"
@ -1924,9 +1924,9 @@
integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==
"@types/ws@^7.4.4":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.5.tgz#8ff0f7efcd8fea19f51f9dd66cb8b498d172a752"
integrity sha512-8mbDgtc8xpxDDem5Gwj76stBDJX35KQ3YBoayxlqUQcL5BZUthiqP/VQ4PQnLHqM4PmlbyO74t98eJpURO+gPA==
version "7.4.6"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.6.tgz#c4320845e43d45a7129bb32905e28781c71c1fff"
integrity sha512-ijZ1vzRawI7QoWnTNL8KpHixd2b2XVb9I9HAqI3triPsh1EC0xH0Eg6w2O3TKbDCgiNNlJqfrof6j4T2I+l9vw==
dependencies:
"@types/node" "*"
@ -2872,7 +2872,7 @@ chokidar@3.5.1:
optionalDependencies:
fsevents "~2.3.1"
chokidar@^3.5.1:
chokidar@^3.5.1, chokidar@^3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
@ -3588,9 +3588,9 @@ detective@^5.2.0:
minimist "^1.1.1"
didyoumean@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.1.tgz#e92edfdada6537d484d73c0172fd1eba0c4976ff"
integrity sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=
version "1.2.2"
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==
diff-sequences@^26.6.2:
version "26.6.2"
@ -4172,7 +4172,7 @@ fast-equals@^2.0.0:
resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-2.0.3.tgz#7039b0a039909f345a2ce53f6202a14e5f392efc"
integrity sha512-0EMw4TTUxsMDpDkCg0rXor2gsg+npVrMIHbEhvD0HZyIhUX6AktC/yasm+qKwfyswd06Qy95ZKk8p2crTo0iPA==
fast-glob@^3.1.1, fast-glob@^3.2.5:
fast-glob@^3.1.1:
version "3.2.5"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661"
integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==
@ -4184,6 +4184,17 @@ fast-glob@^3.1.1, fast-glob@^3.2.5:
micromatch "^4.0.2"
picomatch "^2.2.1"
fast-glob@^3.2.5:
version "3.2.6"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.6.tgz#434dd9529845176ea049acc9343e8282765c6e1a"
integrity sha512-GnLuqj/pvQ7pX8/L4J84nijv6sAnlwvSDpMkJi9i7nPmPxGtRPkBSStfvDW5l6nMdX9VWe+pkKWFTgD+vF2QSQ==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
glob-parent "^5.1.2"
merge2 "^1.3.0"
micromatch "^4.0.4"
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
@ -7070,7 +7081,7 @@ postcss-lab-function@^2.0.1:
postcss "^7.0.2"
postcss-values-parser "^2.0.0"
postcss-load-config@^3.0.1:
postcss-load-config@^3.0.1, postcss-load-config@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.0.tgz#d39c47091c4aec37f50272373a6a648ef5e97829"
integrity sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==
@ -8611,7 +8622,7 @@ table@^6.0.9:
string-width "^4.2.0"
strip-ansi "^6.0.0"
tailwindcss@^2.1.0, tailwindcss@^2.1.2:
tailwindcss@^2.1.0:
version "2.2.2"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.2.2.tgz#28a99c87b5a6b2bf6298a77d88dc0590e84fa8ee"
integrity sha512-OzFWhlnfrO3JXZKHQiqZcb0Wwl3oJSmQ7PvT2jdIgCjV5iUoAyql9bb9ZLCSBI5TYXmawujXAoNxXVfP5Auy/Q==
@ -8648,6 +8659,43 @@ tailwindcss@^2.1.0, tailwindcss@^2.1.2:
resolve "^1.20.0"
tmp "^0.2.1"
tailwindcss@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.2.4.tgz#6a2e259b1e26125aeaa7cdc479963fd217c308b0"
integrity sha512-OdBCPgazNNsknSP+JfrPzkay9aqKjhKtFhbhgxHgvEFdHy/GuRPo2SCJ4w1SFTN8H6FPI4m6qD/Jj20NWY1GkA==
dependencies:
"@fullhuman/postcss-purgecss" "^4.0.3"
arg "^5.0.0"
bytes "^3.0.0"
chalk "^4.1.1"
chokidar "^3.5.2"
color "^3.1.3"
cosmiconfig "^7.0.0"
detective "^5.2.0"
didyoumean "^1.2.1"
dlv "^1.1.3"
fast-glob "^3.2.5"
fs-extra "^10.0.0"
glob-parent "^6.0.0"
html-tags "^3.1.0"
is-glob "^4.0.1"
lodash "^4.17.21"
lodash.topath "^4.5.2"
modern-normalize "^1.1.0"
node-emoji "^1.8.1"
normalize-path "^3.0.0"
object-hash "^2.2.0"
postcss-js "^3.0.3"
postcss-load-config "^3.1.0"
postcss-nested "5.0.5"
postcss-selector-parser "^6.0.6"
postcss-value-parser "^4.1.0"
pretty-hrtime "^1.0.3"
quick-lru "^5.1.1"
reduce-css-calc "^2.1.8"
resolve "^1.20.0"
tmp "^0.2.1"
terminal-link@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994"
@ -9309,9 +9357,9 @@ ws@^7.0.0, ws@^7.3.1:
integrity sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==
ws@^7.4.5:
version "7.5.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.1.tgz#44fc000d87edb1d9c53e51fbc69a0ac1f6871d66"
integrity sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==
version "7.5.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.2.tgz#09cc8fea3bec1bc5ed44ef51b42f945be36900f6"
integrity sha512-lkF7AWRicoB9mAgjeKbGqVUekLnSNO4VjKVnuPHpQeOxZOErX6BPXwJk70nFslRCEEA8EVW7ZjKwXaP9N+1sKQ==
xml-name-validator@^3.0.0:
version "3.0.0"