wire up positions, add estimated net apr

This commit is contained in:
tjs 2023-09-20 17:41:45 -04:00
parent 4e979dae49
commit 32016c233f
9 changed files with 108 additions and 57 deletions

View File

@ -9,6 +9,8 @@ import Switch from './forms/Switch'
import useLocalStorageState from 'hooks/useLocalStorageState'
import useStakeRates from 'hooks/useStakeRates'
import SheenLoader from './shared/SheenLoader'
import useStakeAccounts from 'hooks/useStakeAccounts'
import FormatNumericValue from './shared/FormatNumericValue'
const set = mangoStore.getState().set
@ -21,6 +23,7 @@ const Positions = ({
const { data: stakeRates, isLoading: loadingRates } = useStakeRates()
const [showInactivePositions, setShowInactivePositions] =
useLocalStorageState(SHOW_INACTIVE_POSITIONS_KEY, true)
const { stakeAccounts } = useStakeAccounts()
const banks = useMemo(() => {
if (!group) return []
@ -33,20 +36,21 @@ const Positions = ({
}, [group])
const positions = useMemo(() => {
if (!banks.length) return []
if (!banks.length || !stakeAccounts?.length) return []
const positions = []
for (const bank of banks) {
let balance = 0
if (bank?.name === 'JitoSOL') {
balance = 100
}
if (!bank) continue
const acct = stakeAccounts.find((acc) => acc.getTokenBalanceUi(bank) > 0)
const balance = acct ? acct.getTokenBalanceUi(bank) : 0
positions.push({ balance, bank })
}
const sortedPositions = positions.sort((a, b) => b.balance - a.balance)
return showInactivePositions
? sortedPositions
: sortedPositions.filter((pos) => pos.balance > 0)
}, [banks, showInactivePositions])
}, [banks, showInactivePositions, stakeAccounts])
console.log('positions', positions)
const numberOfPositions = useMemo(() => {
if (!positions.length) return 0
@ -106,7 +110,8 @@ const Positions = ({
<div>
<p className="mb-1">Position Size</p>
<span className="text-xl font-bold">
{balance} {formatTokenSymbol(bank.name)}
<FormatNumericValue value={balance} decimals={6} />{' '}
{formatTokenSymbol(bank.name)}
</span>
</div>
<div>

View File

@ -36,6 +36,7 @@ import FormatNumericValue from './shared/FormatNumericValue'
import { stakeAndCreate } from 'utils/transactions'
import { MangoAccount } from '@blockworks-foundation/mango-v4'
import { AnchorProvider } from '@project-serum/anchor'
import useStakeRates from 'hooks/useStakeRates'
const set = mangoStore.getState().set
@ -92,12 +93,13 @@ function DepositForm({ onSuccess, token: selectedToken }: DepositFormProps) {
// const banks = useBanksWithBalances('walletBalance')
const { usedTokens, totalTokens } = useMangoAccountAccounts()
const { group } = useMangoGroup()
const { data: stakeRates } = useStakeRates()
const stakeBank = useMemo(() => {
return group?.banksMapByName.get(selectedToken)?.[0]
}, [selectedToken, group])
const solBank = useMemo(() => {
const borrowBank = useMemo(() => {
return group?.banksMapByName.get('SOL')?.[0]
}, [group])
@ -126,8 +128,8 @@ function DepositForm({ onSuccess, token: selectedToken }: DepositFormProps) {
// setShowTokenList(false)
// }
const solAmountToBorrow = useMemo(() => {
const solPrice = solBank?.uiPrice
const amountToBorrow = useMemo(() => {
const solPrice = borrowBank?.uiPrice
const stakePrice = stakeBank?.uiPrice
if (!solPrice || !stakePrice || !Number(inputAmount)) return 0
const priceDifference = (stakePrice - solPrice) / solPrice
@ -135,7 +137,7 @@ function DepositForm({ onSuccess, token: selectedToken }: DepositFormProps) {
(1 + priceDifference) * Number(inputAmount) * Math.min(leverage - 1, 1)
return borrowAmount
}, [leverage, solBank, stakeBank, inputAmount])
}, [leverage, borrowBank, stakeBank, inputAmount])
const handleRefreshWalletBalances = useCallback(async () => {
if (!publicKey) return
@ -157,14 +159,14 @@ function DepositForm({ onSuccess, token: selectedToken }: DepositFormProps) {
setSubmitting(true)
try {
console.log('starting deposit')
console.log('solAmountToBorrow', solAmountToBorrow)
console.log('amountToBorrow', amountToBorrow)
const newAccountNum = getNextAccountNumber(mangoAccounts)
const { signature: tx, slot } = await stakeAndCreate(
client,
group,
mangoAccount,
solAmountToBorrow,
amountToBorrow,
stakeBank.mint,
parseFloat(inputAmount),
newAccountNum + 300,
@ -195,7 +197,7 @@ function DepositForm({ onSuccess, token: selectedToken }: DepositFormProps) {
type: 'error',
})
}
}, [stakeBank, publicKey, inputAmount, solAmountToBorrow, onSuccess])
}, [stakeBank, publicKey, inputAmount, amountToBorrow, onSuccess])
const showInsufficientBalance =
tokenMax.maxAmount < Number(inputAmount) ||
@ -212,6 +214,28 @@ function DepositForm({ onSuccess, token: selectedToken }: DepositFormProps) {
})
}, [selectedToken])
const stakeBankDepositRate = useMemo(() => {
return stakeBank ? stakeBank.getDepositRateUi() : 0
}, [stakeBank])
const borrowBankBorrowRate = useMemo(() => {
return borrowBank ? borrowBank.getBorrowRateUi() : 0
}, [borrowBank])
const borrowBankStakeRate = useMemo(() => {
return stakeRates ? stakeRates[selectedToken.toLowerCase()] * 100 : 0
}, [stakeRates, selectedToken])
const leveragedAPY = useMemo(() => {
return borrowBankStakeRate ? borrowBankStakeRate * leverage : 0
}, [borrowBankStakeRate, leverage])
const estimatedNetAPY = useMemo(() => {
return (
borrowBankStakeRate * leverage - borrowBankBorrowRate * (leverage - 1)
)
}, [borrowBankStakeRate, leverage, borrowBankBorrowRate])
return (
<>
<EnterBottomExitBottom
@ -315,7 +339,7 @@ function DepositForm({ onSuccess, token: selectedToken }: DepositFormProps) {
step={0.1}
/>
</div>
{stakeBank && solBank ? (
{stakeBank && borrowBank ? (
<>
<div className="mt-2 space-y-1.5 px-2 py-4">
<div className="flex justify-between">
@ -327,10 +351,10 @@ function DepositForm({ onSuccess, token: selectedToken }: DepositFormProps) {
</div>
<div className="flex justify-between">
<p>SOL Borrowed</p>
{solBank ? (
{borrowBank ? (
<span className="font-mono text-th-fgd-1">
<FormatNumericValue
value={solAmountToBorrow}
value={amountToBorrow}
decimals={3}
/>
</span>
@ -339,20 +363,26 @@ function DepositForm({ onSuccess, token: selectedToken }: DepositFormProps) {
</div>
<div className="space-y-1.5 border-t border-th-bkg-3 px-2 pt-4">
<div className="flex justify-between">
<p>{formatTokenSymbol(selectedToken)} Leveraged APY</p>
<span className="font-mono text-th-fgd-1">
<p className="font-bold">Estimated Net APY</p>
<span className="font-mono text-green-600">
<FormatNumericValue
value={7.28 * leverage}
value={estimatedNetAPY}
decimals={2}
/>
%
</span>
</div>
<div className="flex justify-between">
<p>{formatTokenSymbol(selectedToken)} Leveraged APY</p>
<span className="font-mono text-green-600">
<FormatNumericValue value={leveragedAPY} decimals={2} />%
</span>
</div>
<div className="flex justify-between">
<p>{formatTokenSymbol(selectedToken)} Deposit Rate</p>
<span className="font-mono text-th-fgd-1">
<FormatNumericValue
value={stakeBank.getDepositRateUi()}
value={stakeBankDepositRate}
decimals={2}
/>
%
@ -360,11 +390,9 @@ function DepositForm({ onSuccess, token: selectedToken }: DepositFormProps) {
</div>
<div className="flex justify-between">
<p>SOL Borrow Rate</p>
<span className="font-mono text-th-fgd-1">
<span className="font-mono text-red-600">
<FormatNumericValue
value={
solBank.getDepositRateUi() * Math.min(leverage - 1, 1)
}
value={borrowBankBorrowRate}
decimals={2}
/>
%

View File

@ -12,13 +12,7 @@ const TokenButton = ({
selectedToken: string
handleTokenSelect: (v: string) => void
}) => {
const {
data: stakeRates,
isLoading: loadingStakeRates,
isFetching: fetchingStakeRates,
} = useStakeRates()
const loadingRates = loadingStakeRates || fetchingStakeRates
const { data: stakeRates, isLoading } = useStakeRates()
return (
<button
@ -38,7 +32,7 @@ const TokenButton = ({
{formatTokenSymbol(tokenName)}
</span>
<span className="font-mono">
{loadingRates ? (
{isLoading ? (
<SheenLoader className="mt-0.5">
<div className="h-5 w-10 bg-th-bkg-3" />
</SheenLoader>

20
hooks/useStakeAccounts.ts Normal file
View File

@ -0,0 +1,20 @@
import { MangoAccount } from '@blockworks-foundation/mango-v4'
import mangoStore from '@store/mangoStore'
import { useMemo } from 'react'
import { BOOST_ACCOUNT_PREFIX } from 'utils/constants'
export default function useStakeAccounts(): {
stakeAccounts: MangoAccount[] | undefined
} {
const mangoAccounts = mangoStore((s) => s.mangoAccounts)
const stakeAccounts = useMemo(() => {
return mangoAccounts.filter((ma) =>
ma.name.includes(`${BOOST_ACCOUNT_PREFIX}`),
)
}, [mangoAccounts])
return {
stakeAccounts,
}
}

View File

@ -17,10 +17,10 @@ const fetchRates = async () => {
console.log('jitosol', jitoPrices)
// may be null if the price range cannot be calculated
const msolRange = getPriceRangeFromPeriod(msolPrices, PERIOD.DAYS_7)
const jitoRange = getPriceRangeFromPeriod(jitoPrices, PERIOD.DAYS_7)
const bsolRange = getPriceRangeFromPeriod(bsolPrices, PERIOD.DAYS_7)
const lidoRange = getPriceRangeFromPeriod(lidoPrices, PERIOD.DAYS_7)
const msolRange = getPriceRangeFromPeriod(msolPrices, PERIOD.DAYS_30)
const jitoRange = getPriceRangeFromPeriod(jitoPrices, PERIOD.DAYS_30)
const bsolRange = getPriceRangeFromPeriod(bsolPrices, PERIOD.DAYS_30)
const lidoRange = getPriceRangeFromPeriod(lidoPrices, PERIOD.DAYS_30)
console.log('msol prices', msolPrices)
const rateData: Record<string, number> = {}
@ -51,5 +51,8 @@ export default function useStakeRates() {
refetchOnWindowFocus: true,
})
return response
return {
data: response.data,
isLoading: response.isFetching || response.isLoading,
}
}

View File

@ -5,7 +5,7 @@ import mangoStore from '@store/mangoStore'
import type { NextPage } from 'next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { useEffect, useState } from 'react'
import { ACCOUNT_PREFIX } from 'utils/transactions'
import { BOOST_ACCOUNT_PREFIX } from 'utils/constants'
const set = mangoStore.getState().set
@ -24,7 +24,7 @@ const Index: NextPage = () => {
useEffect(() => {
const mangoAccounts = mangoStore.getState().mangoAccounts
const selectedMangoAccount = mangoAccounts.find(
(ma) => ma.name === `${ACCOUNT_PREFIX}${selectedToken}`,
(ma) => ma.name === `${BOOST_ACCOUNT_PREFIX}${selectedToken}`,
)
console.log('selectedMangoAccount', selectedMangoAccount)

View File

@ -34,6 +34,7 @@ import {
TokenAccount,
} from '../utils/tokens'
import {
BOOST_ACCOUNT_PREFIX,
CONNECTION_COMMITMENT,
DEFAULT_MARKET_NAME,
INPUT_TOKEN_DEFAULT,
@ -80,7 +81,6 @@ import groupBy from 'lodash/groupBy'
import sampleSize from 'lodash/sampleSize'
import { Token } from 'types/jupiter'
import { sleep } from 'utils'
import { ACCOUNT_PREFIX } from 'utils/transactions'
const GROUP = new PublicKey('78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX')
@ -655,7 +655,7 @@ const mangoStore = create<MangoStore>()(
if (!selectedMangoAccount || !selectedAccountIsNotInAccountsList) {
try {
newSelectedMangoAccount = mangoAccounts.find(
(m) => m.name.toString() === `${ACCOUNT_PREFIX}MSOL`,
(m) => m.name.toString() === `${BOOST_ACCOUNT_PREFIX}MSOL`,
)
} catch (e) {
console.error('Error parsing last account', e)

View File

@ -8,6 +8,8 @@ export const SHOW_INACTIVE_POSITIONS_KEY = 'showInactivePositions-0.1'
export const LAST_ACCOUNT_KEY = 'mangoAccount-0.4'
export const BOOST_ACCOUNT_PREFIX = 'Leverage Stake'
export const CLIENT_TX_TIMEOUT = 90000
export const SECONDS = 1000

View File

@ -31,8 +31,7 @@ import {
VersionedTransaction,
} from '@solana/web3.js'
import { floorToDecimal } from './numbers'
export const ACCOUNT_PREFIX = 'Leverage Stake '
import { BOOST_ACCOUNT_PREFIX } from './constants'
export const unstakeAndClose = async (
client: MangoClient,
@ -130,18 +129,18 @@ export const unstakeAndClose = async (
)
instructions.push(...withdrawIx)
if (withdrawMax) {
const closeIx = await client.program.methods
.accountClose(false)
.accounts({
group: group.publicKey,
account: mangoAccount.publicKey,
owner: (client.program.provider as AnchorProvider).wallet.publicKey,
solDestination: mangoAccount.owner,
})
.instruction()
instructions.push(closeIx)
}
// if (withdrawMax) {
// const closeIx = await client.program.methods
// .accountClose(false)
// .accounts({
// group: group.publicKey,
// account: mangoAccount.publicKey,
// owner: (client.program.provider as AnchorProvider).wallet.publicKey,
// solDestination: mangoAccount.owner,
// })
// .instruction()
// instructions.push(closeIx)
// }
return await client.sendAndConfirmTransactionForGroup(group, instructions, {
alts: [...group.addressLookupTablesList, ...swapAlts],
@ -177,7 +176,7 @@ export const stakeAndCreate = async (
0, // serum
0, // perp
0, // perp OO
name ?? `${ACCOUNT_PREFIX}${stakeBank.name}`,
name ?? `${BOOST_ACCOUNT_PREFIX}${stakeBank.name}`,
)
.accounts({
group: group.publicKey,