lev-stake-sol/components/Positions.tsx

243 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import useMangoGroup from 'hooks/useMangoGroup'
import { useMemo } from 'react'
import { SHOW_INACTIVE_POSITIONS_KEY } from 'utils/constants'
import TokenLogo from './shared/TokenLogo'
import Button from './shared/Button'
import { formatTokenSymbol } from 'utils/tokens'
import mangoStore, { ActiveTab } from '@store/mangoStore'
import Switch from './forms/Switch'
import useLocalStorageState from 'hooks/useLocalStorageState'
import FormatNumericValue from './shared/FormatNumericValue'
import {
Bank,
MangoAccount,
toUiDecimalsForQuote,
} from '@blockworks-foundation/mango-v4'
import useBankRates from 'hooks/useBankRates'
import usePositions from 'hooks/usePositions'
const set = mangoStore.getState().set
type Position = {
borrowBalance: number
stakeBalance: number
pnl: number
bank: Bank
acct: MangoAccount | undefined
}
const getLiquidationRatio = (
borrowBalance: number,
stakeBalance: number,
stakeBank: Bank,
borrowBank: Bank,
) => {
return (
(Math.abs(borrowBalance) * borrowBank.maintLiabWeight.toNumber()) /
(stakeBalance * stakeBank.maintAssetWeight.toNumber())
).toFixed(3)
}
const Positions = ({
setActiveTab,
}: {
setActiveTab: (tab: ActiveTab) => void
}) => {
const [showInactivePositions, setShowInactivePositions] =
useLocalStorageState(SHOW_INACTIVE_POSITIONS_KEY, true)
const { borrowBank, positions } = usePositions(showInactivePositions)
const numberOfPositions = useMemo(() => {
if (!positions.length) return 0
return positions.filter((pos) => pos.stakeBalance > 0).length
}, [positions])
return (
<>
<div className="mb-2 flex items-center justify-between rounded-lg border-2 border-th-fgd-1 bg-th-bkg-1 px-6 py-3.5">
<p className="font-medium">{`You have ${numberOfPositions} active position${
numberOfPositions !== 1 ? 's' : ''
}`}</p>
<Switch
checked={showInactivePositions}
onChange={(checked) => setShowInactivePositions(checked)}
>
Show Inactive
</Switch>
</div>
<div className="grid grid-cols-1 gap-2">
{positions.length ? (
positions.map((position) => {
return position.bank ? (
<PositionItem
key={position.bank.name}
position={position}
setActiveTab={setActiveTab}
borrowBank={borrowBank}
/>
) : null
})
) : (
<div className="flex justify-center rounded-2xl border-2 border-th-fgd-1 bg-th-bkg-1 p-6">
<span>Nothing to see here...</span>
</div>
)}
</div>
</>
)
}
const PositionItem = ({
position,
setActiveTab,
borrowBank,
}: {
position: Position
setActiveTab: (v: ActiveTab) => void
borrowBank: Bank | undefined
}) => {
const { group } = useMangoGroup()
const { stakeBalance, borrowBalance, bank, pnl, acct } = position
const handleAddOrManagePosition = (token: string) => {
setActiveTab('Boost!')
set((state) => {
state.selectedToken = token
})
}
const leverage = useMemo(() => {
if (!group || !acct) return 1
const accountValue = toUiDecimalsForQuote(acct.getEquity(group).toNumber())
const assetsValue = toUiDecimalsForQuote(
acct.getAssetsValue(group).toNumber(),
)
if (isNaN(assetsValue / accountValue)) {
return 0
} else {
return Math.abs(1 - assetsValue / accountValue) + 1
}
}, [group, acct])
const [liqRatio] = useMemo(() => {
if (!borrowBalance || !borrowBank) return ['0.00', '']
const liqRatio = getLiquidationRatio(
borrowBalance,
stakeBalance,
bank,
borrowBank,
)
const currentPriceRatio = bank.uiPrice / borrowBank.uiPrice
const liqPriceChangePercentage =
((parseFloat(liqRatio) - currentPriceRatio) / currentPriceRatio) * 100
return [liqRatio, liqPriceChangePercentage.toFixed(2)]
}, [bank, borrowBalance, borrowBank, stakeBalance])
const { financialMetrics, stakeBankDepositRate } = useBankRates(
bank.name,
leverage,
)
const APY_Daily_Compound = Math.pow(1 + Number(stakeBankDepositRate) / 365, 365) - 1;
const uiRate = bank.name == 'USDC' ? APY_Daily_Compound * 100 : financialMetrics.APY
return (
<div className="rounded-2xl border-2 border-th-fgd-1 bg-th-bkg-1 p-6">
<div className="mb-4 flex flex-col border-b border-th-bkg-3 pb-4 md:flex-row md:items-center md:justify-between">
<div className="mb-4 flex items-center space-x-3 md:mb-0">
<div
className={`inner-shadow-bottom-sm flex h-12 w-12 items-center justify-center rounded-full border border-th-bkg-2 bg-gradient-to-b from-th-bkg-1 to-th-bkg-2`}
>
<TokenLogo bank={bank} size={28} />
</div>
<div>
<h3>{formatTokenSymbol(bank.name)}</h3>
<p>${bank.uiPrice.toFixed(2)}</p>
</div>
</div>
<Button onClick={() => handleAddOrManagePosition(bank.name)}>
<p className="mb-1 text-base tracking-wider text-th-bkg-1">
{stakeBalance ? 'Manage' : 'Add Position'}
</p>
</Button>
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
<p className="mb-1 text-th-fgd-4">Position Size</p>
<span className="text-xl font-bold text-th-fgd-1">
<FormatNumericValue
value={stakeBalance * (bank.name != 'USDC' ? bank?.uiPrice : 1)}
decimals={2}
/>{' '}
{'USDC'}
</span>
{bank.name !== 'USDC' ? (
<p className="text-th-fgd-4">
<FormatNumericValue
roundUp={true}
value={stakeBalance}
decimals={3}
/>{' '}
{formatTokenSymbol(bank.name)}
</p>
) : null}
</div>
<div>
<p className="mb-1 text-th-fgd-4">Est. APY</p>
<span className="text-xl font-bold text-th-fgd-1">
<FormatNumericValue value={Number(uiRate)} decimals={2} />%
</span>
</div>
<div>
<p className="mb-1 text-th-fgd-4">Total Earned</p>
<span
className={`text-xl font-bold ${
!stakeBalance
? 'text-th-fgd-4'
: pnl >= 0
? 'text-th-success'
: 'text-th-error'
}`}
>
{stakeBalance || pnl ? (
<FormatNumericValue value={pnl} decimals={2} isUsd />
) : (
''
)}
</span>
</div>
{position.bank.name == 'USDC' ? null : (
<>
<div>
<p className="mb-1 text-th-fgd-4">Leverage</p>
<span className="text-xl font-bold text-th-fgd-1">
{leverage ? leverage.toFixed(2) : 0.0}x
</span>
</div>
<div>
<p className="mb-1 text-th-fgd-4">Est. Liquidation Price</p>
<div className="flex flex-wrap items-end">
<span className="mr-2 whitespace-nowrap text-xl font-bold text-th-fgd-1">
${liqRatio}
</span>
</div>
{/* {liqPriceChangePercentage ? (
<Tooltip content="Estimated price change required for liquidation.">
<p className="tooltip-underline mb-0.5 text-th-fgd-4">
{liqPriceChangePercentage}%
</p>
</Tooltip>
) : null} */}
</div>
</>
)}
</div>
</div>
)
}
export default Positions