-
+
+
+
+
+
+
+
+ {formatTokenSymbol(tokenName)}
+
+
+ {!groupLoaded ? (
+
+
+
+ ) : !UiRate || isNaN(UiRate) ? (
+ 'Rate Unavailable'
+ ) : tokenName === 'USDC' ? (
+ `${UiRate.toFixed(2)}% APY`
+ ) : (
+ `Up to ${UiRate.toFixed(2)}% APY`
+ )}
+
+
-
- {formatTokenSymbol(tokenName)}
-
-
- {!groupLoaded ? (
-
-
-
- ) : !UiRate || isNaN(UiRate) ? (
- 'Rate Unavailable'
- ) : tokenName === 'USDC' ? (
- `${UiRate.toFixed(2)}% APY`
- ) : (
- `Up to ${UiRate.toFixed(2)}% APY`
- )}
-
+
)
diff --git a/components/TokenSelect.tsx b/components/TokenSelect.tsx
new file mode 100644
index 0000000..d77ae68
--- /dev/null
+++ b/components/TokenSelect.tsx
@@ -0,0 +1,71 @@
+import Image from 'next/image'
+import { formatTokenSymbol } from 'utils/tokens'
+import useBankRates from 'hooks/useBankRates'
+import useLeverageMax from 'hooks/useLeverageMax'
+import mangoStore from '@store/mangoStore'
+import SheenLoader from './shared/SheenLoader'
+
+const TokenSelect = ({
+ onClick,
+ tokenName,
+}: {
+ tokenName: string
+ onClick: () => void
+}) => {
+ const leverage = useLeverageMax(tokenName) * 0.9
+ const groupLoaded = mangoStore((s) => s.groupLoaded)
+
+ const { stakeBankDepositRate, financialMetrics } = useBankRates(
+ tokenName,
+ leverage,
+ )
+
+ const { financialMetrics: estimatedNetAPYFor1xLev } = useBankRates(
+ tokenName,
+ 1,
+ )
+
+ const APY_Daily_Compound =
+ Math.pow(1 + Number(stakeBankDepositRate) / 365, 365) - 1
+ const UiRate =
+ tokenName === 'USDC'
+ ? APY_Daily_Compound * 100
+ : Math.max(estimatedNetAPYFor1xLev.APY, financialMetrics.APY)
+
+ return (
+
+ )
+}
+
+export default TokenSelect
diff --git a/components/UnstakeForm.tsx b/components/UnstakeForm.tsx
index 0e836a8..e7e558d 100644
--- a/components/UnstakeForm.tsx
+++ b/components/UnstakeForm.tsx
@@ -9,7 +9,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'
import NumberFormat, { NumberFormatValues } from 'react-number-format'
import mangoStore from '@store/mangoStore'
import { notify } from '../utils/notifications'
-import { TokenAccount, formatTokenSymbol } from '../utils/tokens'
+import { formatTokenSymbol } from '../utils/tokens'
// import ActionTokenList from './account/ActionTokenList'
import Label from './forms/Label'
import Button, { IconButton } from './shared/Button'
@@ -39,6 +39,7 @@ import { Disclosure } from '@headlessui/react'
import { sleep } from 'utils'
import useIpAddress from 'hooks/useIpAddress'
import { AnchorProvider } from '@project-serum/anchor'
+import { JLP_BORROW_TOKEN, LST_BORROW_TOKEN } from 'utils/constants'
const set = mangoStore.getState().set
@@ -46,27 +47,6 @@ interface UnstakeFormProps {
token: string
}
-export const walletBalanceForToken = (
- walletTokens: TokenAccount[],
- token: string,
-): { maxAmount: number; maxDecimals: number } => {
- const group = mangoStore.getState().group
- const bank = group?.banksMapByName.get(token)?.[0]
-
- let walletToken
- if (bank) {
- const tokenMint = bank?.mint
- walletToken = tokenMint
- ? walletTokens.find((t) => t.mint.toString() === tokenMint.toString())
- : null
- }
-
- return {
- maxAmount: walletToken ? walletToken.uiAmount : 0,
- maxDecimals: bank?.mintDecimals || 6,
- }
-}
-
function UnstakeForm({ token: selectedToken }: UnstakeFormProps) {
const { t } = useTranslation(['common', 'account'])
const [inputAmount, setInputAmount] = useState('')
@@ -79,17 +59,28 @@ function UnstakeForm({ token: selectedToken }: UnstakeFormProps) {
const { maxSolDeposit } = useSolBalance()
// const banks = useBanksWithBalances('walletBalance')
const { usedTokens, totalTokens } = useMangoAccountAccounts()
- const { group } = useMangoGroup()
+ const { jlpGroup, lstGroup } = useMangoGroup()
const { mangoAccount } = useMangoAccount()
const { ipAllowed } = useIpAddress()
- const stakeBank = useMemo(() => {
- return group?.banksMapByName.get(selectedToken)?.[0]
- }, [selectedToken, group])
+ const [stakeBank, borrowBank] = useMemo(() => {
+ const isJlpGroup = selectedToken === 'JLP' || selectedToken === 'USDC'
+ const stakeBank = isJlpGroup
+ ? jlpGroup?.banksMapByName.get(selectedToken)?.[0]
+ : lstGroup?.banksMapByName.get(selectedToken)?.[0]
+ const borrowBank = isJlpGroup
+ ? jlpGroup?.banksMapByName.get(JLP_BORROW_TOKEN)?.[0]
+ : lstGroup?.banksMapByName.get(LST_BORROW_TOKEN)?.[0]
+ return [stakeBank, borrowBank]
+ }, [selectedToken, jlpGroup, lstGroup])
- const borrowBank = useMemo(() => {
- return group?.banksMapByName.get('USDC')?.[0]
- }, [group])
+ // const stakeBank = useMemo(() => {
+ // return group?.banksMapByName.get(selectedToken)?.[0]
+ // }, [selectedToken, group])
+
+ // const borrowBank = useMemo(() => {
+ // return group?.banksMapByName.get('USDC')?.[0]
+ // }, [group])
const tokenPositionsFull = useMemo(() => {
if (!stakeBank || !usedTokens.length || !totalTokens.length) return false
@@ -176,16 +167,18 @@ function UnstakeForm({ token: selectedToken }: UnstakeFormProps) {
}, [borrowBank, mangoAccount])
const handleWithdraw = useCallback(async () => {
- if (!ipAllowed) {
+ if (!ipAllowed || !stakeBank || !borrowBank || !publicKey) {
return
}
const client = mangoStore.getState().client
- const group = mangoStore.getState().group
+ const jlpGroup = mangoStore.getState().group.jlpGroup
+ const lstGroup = mangoStore.getState().group.lstGroup
+ const isJlpGroup = stakeBank.name === 'JLP' || stakeBank.name === 'USDC'
+ const group = isJlpGroup ? jlpGroup : lstGroup
const actions = mangoStore.getState().actions
let mangoAccount = mangoStore.getState().mangoAccount.current
- if (!group || !stakeBank || !borrowBank || !publicKey || !mangoAccount)
- return
+ if (!group || !mangoAccount) return
setSubmitting(true)
try {
@@ -265,10 +258,16 @@ function UnstakeForm({ token: selectedToken }: UnstakeFormProps) {
}
}, [ipAllowed, stakeBank, borrowBank, publicKey, inputAmount, leverage])
- const maxWithdraw =
- group && mangoAccount && stakeBank
- ? mangoAccount.getMaxWithdrawWithBorrowForTokenUi(group, stakeBank.mint)
- : 0
+ const maxWithdraw = useMemo(() => {
+ if (!mangoAccount || !stakeBank) return 0
+ const isJlpGroup = stakeBank.name === 'JLP' || stakeBank.name === 'USDC'
+ const group = isJlpGroup ? jlpGroup : lstGroup
+ if (!group) return 0
+ return mangoAccount.getMaxWithdrawWithBorrowForTokenUi(
+ group,
+ stakeBank.mint,
+ )
+ }, [jlpGroup, lstGroup, mangoAccount, stakeBank])
const showInsufficientBalance =
tokenMax.maxAmount < Number(inputAmount) ||
@@ -280,7 +279,10 @@ function UnstakeForm({ token: selectedToken }: UnstakeFormProps) {
Number(inputAmount) > maxWithdraw
useEffect(() => {
- const group = mangoStore.getState().group
+ const jlpGroup = mangoStore.getState().group.jlpGroup
+ const lstGroup = mangoStore.getState().group.lstGroup
+ const isJlpGroup = selectedToken === 'JLP' || selectedToken === 'USDC'
+ const group = isJlpGroup ? jlpGroup : lstGroup
set((state) => {
state.swap.outputBank = group?.banksMapByName.get(selectedToken)?.[0]
})
diff --git a/components/WithdrawForm.tsx b/components/WithdrawForm.tsx
deleted file mode 100644
index bb9b793..0000000
--- a/components/WithdrawForm.tsx
+++ /dev/null
@@ -1,407 +0,0 @@
-import {
- ArrowPathIcon,
- ChevronDownIcon,
- ExclamationCircleIcon,
-} from '@heroicons/react/20/solid'
-import { useWallet } from '@solana/wallet-adapter-react'
-import { useTranslation } from 'next-i18next'
-import React, { useCallback, useEffect, useMemo, useState } from 'react'
-import NumberFormat, { NumberFormatValues } from 'react-number-format'
-import mangoStore from '@store/mangoStore'
-import { notify } from '../utils/notifications'
-import { TokenAccount, formatTokenSymbol } from '../utils/tokens'
-// import ActionTokenList from './account/ActionTokenList'
-import Label from './forms/Label'
-import Button, { IconButton } from './shared/Button'
-import Loading from './shared/Loading'
-import MaxAmountButton from '@components/shared/MaxAmountButton'
-import Tooltip from '@components/shared/Tooltip'
-import SolBalanceWarnings from '@components/shared/SolBalanceWarnings'
-import useSolBalance from 'hooks/useSolBalance'
-import { floorToDecimal, withValueLimit } from 'utils/numbers'
-import BankAmountWithValue from './shared/BankAmountWithValue'
-// import useBanksWithBalances from 'hooks/useBanksWithBalances'
-import { isMangoError } from 'types'
-// import TokenListButton from './shared/TokenListButton'
-import TokenLogo from './shared/TokenLogo'
-import SecondaryConnectButton from './shared/SecondaryConnectButton'
-import useMangoAccountAccounts from 'hooks/useMangoAccountAccounts'
-import InlineNotification from './shared/InlineNotification'
-import Link from 'next/link'
-import useMangoGroup from 'hooks/useMangoGroup'
-import FormatNumericValue from './shared/FormatNumericValue'
-import useMangoAccount from 'hooks/useMangoAccount'
-import { unstakeAndSwap, withdrawAndClose } from 'utils/transactions'
-import { NUMBERFORMAT_CLASSES } from './StakeForm'
-import ButtonGroup from './forms/ButtonGroup'
-import Decimal from 'decimal.js'
-import { Disclosure } from '@headlessui/react'
-import { sleep } from 'utils'
-import useIpAddress from 'hooks/useIpAddress'
-
-const set = mangoStore.getState().set
-
-interface UnstakeFormProps {
- token: string
-}
-
-export const walletBalanceForToken = (
- walletTokens: TokenAccount[],
- token: string,
-): { maxAmount: number; maxDecimals: number } => {
- const group = mangoStore.getState().group
- const bank = group?.banksMapByName.get(token)?.[0]
-
- let walletToken
- if (bank) {
- const tokenMint = bank?.mint
- walletToken = tokenMint
- ? walletTokens.find((t) => t.mint.toString() === tokenMint.toString())
- : null
- }
-
- return {
- maxAmount: walletToken ? walletToken.uiAmount : 0,
- maxDecimals: bank?.mintDecimals || 6,
- }
-}
-
-function WithdrawForm({ token: selectedToken }: UnstakeFormProps) {
- const { t } = useTranslation(['common', 'account'])
- const [inputAmount, setInputAmount] = useState('')
- const [submitting, setSubmitting] = useState(false)
- const { ipAllowed } = useIpAddress()
- // const [selectedToken, setSelectedToken] = useState(
- // token || INPUT_TOKEN_DEFAULT,
- // )
- const [refreshingWalletTokens, setRefreshingWalletTokens] = useState(false)
- const [sizePercentage, setSizePercentage] = useState('')
- const { maxSolDeposit } = useSolBalance()
- // const banks = useBanksWithBalances('walletBalance')
- const { usedTokens, totalTokens } = useMangoAccountAccounts()
- const { group } = useMangoGroup()
- const { mangoAccount } = useMangoAccount()
-
- const stakeBank = useMemo(() => {
- return group?.banksMapByName.get(selectedToken)?.[0]
- }, [selectedToken, group])
-
- const borrowBank = useMemo(() => {
- return group?.banksMapByName.get('USDC')?.[0]
- }, [group])
-
- const tokenPositionsFull = useMemo(() => {
- if (!stakeBank || !usedTokens.length || !totalTokens.length) return false
- const hasTokenPosition = usedTokens.find(
- (token) => token.tokenIndex === stakeBank.tokenIndex,
- )
- return hasTokenPosition ? false : usedTokens.length >= totalTokens.length
- }, [stakeBank, usedTokens, totalTokens])
-
- const { connected, publicKey } = useWallet()
-
- const tokenMax = useMemo(() => {
- if (!stakeBank || !mangoAccount) return { maxAmount: 0.0, maxDecimals: 6 }
- return {
- maxAmount: mangoAccount.getTokenBalanceUi(stakeBank),
- maxDecimals: stakeBank.mintDecimals,
- }
- }, [stakeBank, mangoAccount])
-
- const setMax = useCallback(() => {
- const max = floorToDecimal(tokenMax.maxAmount, tokenMax.maxDecimals)
- setInputAmount(max.toFixed())
- }, [tokenMax])
-
- const handleSizePercentage = useCallback(
- (percentage: string) => {
- if (!stakeBank) return
- setSizePercentage(percentage)
- const amount = floorToDecimal(
- new Decimal(percentage).div(100).mul(tokenMax.maxAmount),
- stakeBank.mintDecimals,
- )
- setInputAmount(amount.toFixed())
- },
- [tokenMax, stakeBank],
- )
-
- // const handleSelectToken = (token: string) => {
- // setSelectedToken(token)
- // setShowTokenList(false)
- // }
-
- const handleRefreshWalletBalances = useCallback(async () => {
- if (!publicKey) return
- const actions = mangoStore.getState().actions
- setRefreshingWalletTokens(true)
- await actions.fetchMangoAccounts(publicKey)
- setRefreshingWalletTokens(false)
- }, [publicKey])
-
- const borrowed = useMemo(() => {
- if (!borrowBank || !mangoAccount) return 0.0
- return mangoAccount.getTokenBalanceUi(borrowBank)
- }, [borrowBank, mangoAccount])
-
- const handleWithdraw = useCallback(async () => {
- if (!ipAllowed) {
- return
- }
- const client = mangoStore.getState().client
- const group = mangoStore.getState().group
- const actions = mangoStore.getState().actions
- let mangoAccount = mangoStore.getState().mangoAccount.current
-
- if (!group || !stakeBank || !borrowBank || !publicKey || !mangoAccount)
- return
-
- setSubmitting(true)
- try {
- if (mangoAccount.getTokenBalanceUi(borrowBank) < 0) {
- notify({
- title: 'Sending transaction 1 of 2',
- type: 'info',
- })
- console.log(
- 'unstake and swap',
- mangoAccount.getTokenBalanceUi(borrowBank),
- )
-
- const { signature: tx } = await unstakeAndSwap(
- client,
- group,
- mangoAccount,
- stakeBank.mint,
- )
- notify({
- title: 'Swap Transaction confirmed.',
- type: 'success',
- txid: tx,
- })
- await sleep(300)
- await actions.fetchMangoAccounts(mangoAccount.owner)
- await actions.fetchWalletTokens(publicKey)
- mangoAccount = mangoStore.getState().mangoAccount.current
- notify({
- title: 'Sending transaction 2 of 2',
- type: 'info',
- })
- }
- if (!mangoAccount) return
- const { signature: tx2 } = await withdrawAndClose(
- client,
- group,
- mangoAccount,
- stakeBank.mint,
- Number(inputAmount),
- )
- notify({
- title: 'Withdraw transaction confirmed.',
- type: 'success',
- txid: tx2,
- })
- setSubmitting(false)
- setInputAmount('')
- await sleep(500)
- await actions.fetchMangoAccounts(mangoAccount.owner)
- await actions.fetchWalletTokens(publicKey)
- } catch (e) {
- console.error('Error depositing:', e)
- setSubmitting(false)
- if (!isMangoError(e)) return
- notify({
- title: 'Transaction failed',
- description: e.message,
- txid: e?.txid,
- type: 'error',
- })
- }
- }, [stakeBank, publicKey, inputAmount])
-
- const showInsufficientBalance =
- tokenMax.maxAmount < Number(inputAmount) ||
- (selectedToken === 'SOL' && maxSolDeposit <= 0)
-
- useEffect(() => {
- const group = mangoStore.getState().group
- set((state) => {
- state.swap.outputBank = group?.banksMapByName.get(selectedToken)?.[0]
- })
- }, [selectedToken])
-
- return (
- <>
-
-
-
-
-
-
-
-
{
- setInputAmount(
- !Number.isNaN(Number(e.value)) ? e.value : '',
- )
- }}
- isAllowed={withValueLimit}
- />
-
-
-
-
-
- {formatTokenSymbol(selectedToken)}
-
-
-
-
-
- handleSizePercentage(p)}
- values={['10', '25', '50', '75', '100']}
- unit="%"
- />
-
-
- {stakeBank && borrowBank ? (
-
-
- {({ open }) => (
- <>
-
-
-
Staked Amount
-
-
-
-
-
-
-
-
-
-
-
-
USDC borrowed
- {borrowBank ? (
-
0.001
- ? 'text-th-fgd-1'
- : 'text-th-bkg-4'
- }`}
- >
-
-
- ) : null}
-
-
- >
- )}
-
-
- ) : null}
-
- {connected ? (
-
- ) : (
-
- )}
- {tokenPositionsFull ? (
-
- {t('error-token-positions-full')}{' '}
-
- {t('manage')}
-
- >
- }
- />
- ) : null}
-
- >
- )
-}
-
-export default WithdrawForm
diff --git a/components/shared/TokenLogo.tsx b/components/shared/TokenLogo.tsx
index 1911de5..7756aac 100644
--- a/components/shared/TokenLogo.tsx
+++ b/components/shared/TokenLogo.tsx
@@ -13,7 +13,6 @@ const TokenLogo = ({
size?: number
}) => {
const { mangoTokens } = useJupiterMints()
-
const logoUri = useMemo(() => {
if (!bank) return ''
const tokenSymbol = bank.name.toLowerCase()
diff --git a/components/stats/HistoricalStats.tsx b/components/stats/HistoricalStats.tsx
index 6328fe4..4a57483 100644
--- a/components/stats/HistoricalStats.tsx
+++ b/components/stats/HistoricalStats.tsx
@@ -5,18 +5,18 @@ import { fetchTokenStatsData } from 'utils/stats'
import TokenRatesChart from './TokenRatesChart'
const HistoricalStats = () => {
- const { group } = useMangoGroup()
+ const { jlpGroup } = useMangoGroup()
const [depositDaysToShow, setDepositDaysToShow] = useState('30')
const { data: historicalStats, isLoading: loadingHistoricalStats } = useQuery(
['historical-stats'],
- () => fetchTokenStatsData(group),
+ () => fetchTokenStatsData(jlpGroup),
{
cacheTime: 1000 * 60 * 10,
staleTime: 1000 * 60,
retry: 3,
refetchOnWindowFocus: false,
- enabled: !!group,
+ enabled: !!jlpGroup,
},
)
diff --git a/components/stats/StatsPage.tsx b/components/stats/StatsPage.tsx
index 3613390..182e2db 100644
--- a/components/stats/StatsPage.tsx
+++ b/components/stats/StatsPage.tsx
@@ -11,21 +11,23 @@ import { formatTokenSymbol } from 'utils/tokens'
import HistoricalStats from './HistoricalStats'
const StatsPage = () => {
- const { group } = useMangoGroup()
+ const { jlpGroup, lstGroup } = useMangoGroup()
const { t } = useTranslation('common')
const { isMobile } = useViewport()
const banks = useMemo(() => {
- if (!group) return []
- const positionBanks = []
+ const statsBanks = []
for (const token of STAKEABLE_TOKENS) {
- const bank = group.banksMapByName.get(token)?.[0]
+ const isJlpGroup = token === 'JLP' || token === 'USDC'
+ const bank = isJlpGroup
+ ? jlpGroup?.banksMapByName.get(token)?.[0]
+ : lstGroup?.banksMapByName.get(token)?.[0]
if (bank !== undefined) {
- positionBanks.push(bank)
+ statsBanks.push(bank)
}
}
- return positionBanks
- }, [group])
+ return statsBanks
+ }, [jlpGroup, lstGroup])
return (
diff --git a/hooks/useAccountHistory.ts b/hooks/useAccountHistory.ts
index a1731e2..0eea3cd 100644
--- a/hooks/useAccountHistory.ts
+++ b/hooks/useAccountHistory.ts
@@ -40,16 +40,18 @@ const accountNums = STAKEABLE_TOKENS_DATA.map((d) => d.id)
export default function useAccountHistory() {
const { stakeAccounts } = useStakeAccounts()
- const { group } = useMangoGroup()
+ const { jlpGroup, lstGroup } = useMangoGroup()
const { wallet } = useWallet()
// const accountPks = stakeAccounts?.map((acc) => acc.publicKey.toString()) || []
const accountPks = useMemo(() => {
const client = mangoStore.getState().client
const payer = wallet?.adapter.publicKey?.toBuffer()
- if (!group || !payer) return []
+ if (!jlpGroup || !lstGroup || !payer) return []
const x = accountNums.map((n) => {
+ const isJlpGroup = n === 0 || n === 1
+ const group = isJlpGroup ? jlpGroup : lstGroup
const acctNumBuffer = Buffer.alloc(4)
acctNumBuffer.writeUInt32LE(n)
const [mangoAccountPda] = PublicKey.findProgramAddressSync(
@@ -64,7 +66,7 @@ export default function useAccountHistory() {
return mangoAccountPda.toString()
})
return x
- }, [group, wallet])
+ }, [jlpGroup, lstGroup, wallet])
const activeStakeAccts =
stakeAccounts?.map((acc) => acc.publicKey.toString()) ?? []
diff --git a/hooks/useBankRates.ts b/hooks/useBankRates.ts
index 416dce8..24929ce 100644
--- a/hooks/useBankRates.ts
+++ b/hooks/useBankRates.ts
@@ -3,23 +3,27 @@ import useStakeRates from './useStakeRates'
import useMangoGroup from './useMangoGroup'
// import mangoStore from '@store/mangoStore'
import useLeverageMax from './useLeverageMax'
+import { JLP_BORROW_TOKEN, LST_BORROW_TOKEN } from 'utils/constants'
// const set = mangoStore.getState().set
export default function useBankRates(selectedToken: string, leverage: number) {
const { data: stakeRates } = useStakeRates()
- const { group } = useMangoGroup()
+ const { jlpGroup, lstGroup } = useMangoGroup()
// const estimatedMaxAPY = mangoStore((s) => s.estimatedMaxAPY.current)
const leverageMax = useLeverageMax(selectedToken)
- const stakeBank = useMemo(() => {
- return group?.banksMapByName.get(selectedToken)?.[0]
- }, [selectedToken, group])
-
- const borrowBank = useMemo(() => {
- return group?.banksMapByName.get('USDC')?.[0]
- }, [group])
+ const [stakeBank, borrowBank] = useMemo(() => {
+ const isJlpGroup = selectedToken === 'JLP' || selectedToken === 'USDC'
+ const stakeBank = isJlpGroup
+ ? jlpGroup?.banksMapByName.get(selectedToken)?.[0]
+ : lstGroup?.banksMapByName.get(selectedToken)?.[0]
+ const borrowBank = isJlpGroup
+ ? jlpGroup?.banksMapByName.get(JLP_BORROW_TOKEN)?.[0]
+ : lstGroup?.banksMapByName.get(LST_BORROW_TOKEN)?.[0]
+ return [stakeBank, borrowBank]
+ }, [selectedToken, jlpGroup, lstGroup])
const stakeBankDepositRate = useMemo(() => {
return stakeBank ? stakeBank.getDepositRate() : 0
diff --git a/hooks/useJupiterMints.ts b/hooks/useJupiterMints.ts
index 4c812bf..e869eaf 100644
--- a/hooks/useJupiterMints.ts
+++ b/hooks/useJupiterMints.ts
@@ -1,19 +1,25 @@
-import { Group } from '@blockworks-foundation/mango-v4'
-import { CLUSTER } from '@store/mangoStore'
+import mangoStore, { CLUSTER } from '@store/mangoStore'
import { useQuery } from '@tanstack/react-query'
import useMangoGroup from 'hooks/useMangoGroup'
import { Token } from 'types/jupiter'
import { JUPITER_API_DEVNET, JUPITER_API_MAINNET } from 'utils/constants'
-const fetchJupiterTokens = async (group: Group) => {
+const fetchJupiterTokens = async () => {
+ const { jlpGroup, lstGroup } = mangoStore.getState().group
+ if (!jlpGroup || !lstGroup) return
const url = CLUSTER === 'devnet' ? JUPITER_API_DEVNET : JUPITER_API_MAINNET
const response = await fetch(url)
const data: Token[] = await response.json()
- const bankMints = Array.from(group.banksMapByName.values()).map((b) =>
+ const jlpBankMints = Array.from(jlpGroup.banksMapByName.values()).map((b) =>
b[0].mint.toString(),
)
- const mangoTokens = data.filter((t) => bankMints.includes(t.address))
+ const lstBankMints = Array.from(lstGroup.banksMapByName.values()).map((b) =>
+ b[0].mint.toString(),
+ )
+ const mangoTokens = data.filter(
+ (t) => jlpBankMints.includes(t.address) || lstBankMints.includes(t.address),
+ )
return {
mangoTokens,
@@ -26,19 +32,15 @@ const useJupiterMints = (): {
jupiterTokens: Token[]
isFetching: boolean
} => {
- const { group } = useMangoGroup()
+ const { jlpGroup, lstGroup } = useMangoGroup()
- const res = useQuery(
- ['jupiter-mango-tokens'],
- () => fetchJupiterTokens(group!),
- {
- cacheTime: 1000 * 60 * 10,
- staleTime: 1000 * 60 * 10,
- retry: 3,
- enabled: !!group,
- refetchOnWindowFocus: false,
- },
- )
+ const res = useQuery(['jupiter-mango-tokens'], () => fetchJupiterTokens(), {
+ cacheTime: 1000 * 60 * 10,
+ staleTime: 1000 * 60 * 10,
+ retry: 3,
+ enabled: !!(jlpGroup && lstGroup),
+ refetchOnWindowFocus: false,
+ })
return {
mangoTokens: res?.data?.mangoTokens || [],
diff --git a/hooks/useLeverageMax.ts b/hooks/useLeverageMax.ts
index 78041c1..f9fcd62 100644
--- a/hooks/useLeverageMax.ts
+++ b/hooks/useLeverageMax.ts
@@ -1,24 +1,28 @@
import { useMemo } from 'react'
import useMangoGroup from './useMangoGroup'
import { floorToDecimal } from 'utils/numbers'
+import { JLP_BORROW_TOKEN, LST_BORROW_TOKEN } from 'utils/constants'
export default function useLeverageMax(selectedToken: string) {
- const { group } = useMangoGroup()
+ const { jlpGroup, lstGroup } = useMangoGroup()
- const stakeBank = useMemo(() => {
- return group?.banksMapByName.get(selectedToken)?.[0]
- }, [selectedToken, group])
-
- const borrowBank = useMemo(() => {
- return group?.banksMapByName.get('USDC')?.[0]
- }, [group])
+ const [stakeBank, borrowBank] = useMemo(() => {
+ const isJlpGroup = selectedToken === 'JLP' || selectedToken === 'USDC'
+ const stakeBank = isJlpGroup
+ ? jlpGroup?.banksMapByName.get(selectedToken)?.[0]
+ : lstGroup?.banksMapByName.get(selectedToken)?.[0]
+ const borrowBank = isJlpGroup
+ ? jlpGroup?.banksMapByName.get(JLP_BORROW_TOKEN)?.[0]
+ : lstGroup?.banksMapByName.get(LST_BORROW_TOKEN)?.[0]
+ return [stakeBank, borrowBank]
+ }, [selectedToken, jlpGroup, lstGroup])
const leverageMax = useMemo(() => {
if (!stakeBank || !borrowBank) return 0
const borrowInitLiabWeight = borrowBank.initLiabWeight
const stakeInitAssetWeight = stakeBank.initAssetWeight
-
+
if (!borrowInitLiabWeight || !stakeInitAssetWeight) return 1
const x = stakeInitAssetWeight.toNumber() / borrowInitLiabWeight.toNumber()
diff --git a/hooks/useMangoGroup.ts b/hooks/useMangoGroup.ts
index 502b5ca..f103186 100644
--- a/hooks/useMangoGroup.ts
+++ b/hooks/useMangoGroup.ts
@@ -2,9 +2,11 @@ import { Group } from '@blockworks-foundation/mango-v4'
import mangoStore from '@store/mangoStore'
export default function useMangoGroup(): {
- group: Group | undefined
+ jlpGroup: Group | undefined
+ lstGroup: Group | undefined
} {
- const group = mangoStore((s) => s.group)
+ const jlpGroup = mangoStore((s) => s.group.jlpGroup)
+ const lstGroup = mangoStore((s) => s.group.lstGroup)
- return { group }
+ return { jlpGroup, lstGroup }
}
diff --git a/hooks/usePositions.ts b/hooks/usePositions.ts
index c80d559..80f338e 100644
--- a/hooks/usePositions.ts
+++ b/hooks/usePositions.ts
@@ -1,38 +1,51 @@
import { useMemo } from 'react'
-import { BORROW_TOKEN, STAKEABLE_TOKENS } from 'utils/constants'
+import {
+ JLP_BORROW_TOKEN,
+ LST_BORROW_TOKEN,
+ STAKEABLE_TOKENS,
+} from 'utils/constants'
import useStakeAccounts from './useStakeAccounts'
import useMangoGroup from './useMangoGroup'
-import {
- toUiDecimalsForQuote,
-} from '@blockworks-foundation/mango-v4'
+import { toUiDecimalsForQuote } from '@blockworks-foundation/mango-v4'
export default function usePositions(showInactive = false) {
const { stakeAccounts } = useStakeAccounts()
- const { group } = useMangoGroup()
+ const { jlpGroup, lstGroup } = useMangoGroup()
- const borrowBank = useMemo(() => {
- return group?.banksMapByName.get(BORROW_TOKEN)?.[0]
- }, [group])
+ const jlpBorrowBank = useMemo(() => {
+ return jlpGroup?.banksMapByName.get(JLP_BORROW_TOKEN)?.[0]
+ }, [jlpGroup])
+
+ const lstBorrowBank = useMemo(() => {
+ return lstGroup?.banksMapByName.get(LST_BORROW_TOKEN)?.[0]
+ }, [lstGroup])
const banks = useMemo(() => {
- if (!group) return []
+ if (!jlpGroup || !lstGroup) return []
const positionBanks = []
for (const token of STAKEABLE_TOKENS) {
- const bank = group.banksMapByName.get(token)?.[0]
+ const isJlpGroup = token === 'JLP' || token === 'USDC'
+ const bank = isJlpGroup
+ ? jlpGroup.banksMapByName.get(token)?.[0]
+ : lstGroup.banksMapByName.get(token)?.[0]
positionBanks.push(bank)
}
return positionBanks
- }, [group])
+ }, [jlpGroup, lstGroup])
const positions = useMemo(() => {
const positions = []
for (const bank of banks) {
- if (!bank || !group) continue
+ if (!bank || !jlpGroup || !lstGroup) continue
+ const isJlpGroup = bank.name === 'JLP' || bank.name === 'USDC'
+ const group = isJlpGroup ? jlpGroup : lstGroup
+ const borrowBank = isJlpGroup ? jlpBorrowBank : lstBorrowBank
const acct = stakeAccounts?.find((acc) => acc.getTokenBalanceUi(bank) > 0)
const stakeBalance = acct ? acct.getTokenBalanceUi(bank) : 0
const pnl = acct ? toUiDecimalsForQuote(acct.getPnl(group).toNumber()) : 0
- const borrowBalance = acct && borrowBank ? acct.getTokenBalanceUi(borrowBank) : 0
+ const borrowBalance =
+ acct && borrowBank ? acct.getTokenBalanceUi(borrowBank) : 0
positions.push({ borrowBalance, stakeBalance, bank, pnl, acct })
}
const sortedPositions = positions.sort(
@@ -41,7 +54,15 @@ export default function usePositions(showInactive = false) {
return showInactive
? sortedPositions
: sortedPositions.filter((pos) => pos.stakeBalance > 0)
- }, [banks, showInactive, stakeAccounts, group, borrowBank])
+ }, [
+ banks,
+ showInactive,
+ stakeAccounts,
+ jlpGroup,
+ lstGroup,
+ jlpBorrowBank,
+ lstBorrowBank,
+ ])
- return { borrowBank, positions }
+ return { jlpBorrowBank, lstBorrowBank, positions }
}
diff --git a/hooks/useSelectedMarket.ts b/hooks/useSelectedMarket.ts
deleted file mode 100644
index c89e275..0000000
--- a/hooks/useSelectedMarket.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-import { Serum3Market } from '@blockworks-foundation/mango-v4'
-import mangoStore from '@store/mangoStore'
-import { useMemo } from 'react'
-import { floorToDecimal, getDecimalCount } from 'utils/numbers'
-import useJupiterMints from './useJupiterMints'
-import useMangoGroup from './useMangoGroup'
-import { CUSTOM_TOKEN_ICONS } from 'utils/constants'
-
-export default function useSelectedMarket() {
- const { group } = useMangoGroup()
- const selectedMarket = mangoStore((s) => s.selectedMarket.current)
- const { mangoTokens } = useJupiterMints()
-
- const marketAddress = useMemo(() => {
- return selectedMarket?.publicKey.toString()
- }, [selectedMarket])
-
- const price: number = useMemo(() => {
- if (!group) return 0
- if (selectedMarket instanceof Serum3Market) {
- const baseBank = group.getFirstBankByTokenIndex(
- selectedMarket.baseTokenIndex,
- )
- const quoteBank = group.getFirstBankByTokenIndex(
- selectedMarket.quoteTokenIndex,
- )
- const market = group.getSerum3ExternalMarket(
- selectedMarket.serumMarketExternal,
- )
-
- return floorToDecimal(
- baseBank.uiPrice / quoteBank.uiPrice,
- getDecimalCount(market.tickSize),
- ).toNumber()
- } else if (selectedMarket) {
- return selectedMarket._uiPrice
- } else return 0
- }, [selectedMarket, group])
-
- const serumOrPerpMarket = useMemo(() => {
- const group = mangoStore.getState().group
- if (!group || !selectedMarket) return
-
- if (selectedMarket instanceof Serum3Market) {
- return group?.getSerum3ExternalMarket(selectedMarket.serumMarketExternal)
- } else {
- return selectedMarket
- }
- }, [selectedMarket])
-
- const baseSymbol = useMemo(() => {
- return selectedMarket?.name.split(/-|\//)[0]
- }, [selectedMarket])
-
- const baseLogoURI = useMemo(() => {
- if (!baseSymbol || !mangoTokens.length) return ''
- const lowerCaseBaseSymbol = baseSymbol.toLowerCase()
- const hasCustomIcon = CUSTOM_TOKEN_ICONS[lowerCaseBaseSymbol]
- if (hasCustomIcon) {
- return `/icons/${lowerCaseBaseSymbol}.svg`
- } else {
- const token =
- mangoTokens.find(
- (t) => t.symbol.toLowerCase() === lowerCaseBaseSymbol,
- ) ||
- mangoTokens.find(
- (t) => t.symbol.toLowerCase()?.includes(lowerCaseBaseSymbol),
- )
- if (token) {
- return token.logoURI
- }
- }
- }, [baseSymbol, mangoTokens])
-
- const quoteBank = useMemo(() => {
- const group = mangoStore.getState().group
- if (!group || !selectedMarket) return
- const tokenIdx =
- selectedMarket instanceof Serum3Market
- ? selectedMarket.quoteTokenIndex
- : selectedMarket?.settleTokenIndex
- return group?.getFirstBankByTokenIndex(tokenIdx)
- }, [selectedMarket])
-
- const quoteSymbol = useMemo(() => {
- return quoteBank?.name
- }, [quoteBank])
-
- const quoteLogoURI = useMemo(() => {
- if (!quoteSymbol || !mangoTokens.length) return ''
- const lowerCaseQuoteSymbol = quoteSymbol.toLowerCase()
- const hasCustomIcon = CUSTOM_TOKEN_ICONS[lowerCaseQuoteSymbol]
- if (hasCustomIcon) {
- return `/icons/${lowerCaseQuoteSymbol}.svg`
- } else {
- const token = mangoTokens.find(
- (t) => t.symbol.toLowerCase() === lowerCaseQuoteSymbol,
- )
- if (token) {
- return token.logoURI
- }
- }
- }, [quoteSymbol, mangoTokens])
-
- return {
- selectedMarket,
- selectedMarketAddress: marketAddress,
- price,
- serumOrPerpMarket,
- baseSymbol,
- quoteBank,
- quoteSymbol,
- baseLogoURI,
- quoteLogoURI,
- }
-}
diff --git a/hooks/useStakeRates.ts b/hooks/useStakeRates.ts
index 4998071..ca0499c 100644
--- a/hooks/useStakeRates.ts
+++ b/hooks/useStakeRates.ts
@@ -1,12 +1,18 @@
import { useQuery } from '@tanstack/react-query'
import { fetchSwapChartPrices } from 'apis/birdeye/helpers'
-import { STAKEABLE_TOKENS_DATA } from 'utils/constants'
+import { SOL_MINT, STAKEABLE_TOKENS_DATA, USDC_MINT } from 'utils/constants'
const fetchRates = async () => {
try {
- const [jlpPrices] = await Promise.all([
- fetchSwapChartPrices(STAKEABLE_TOKENS_DATA[0]?.mint_address, STAKEABLE_TOKENS_DATA[1]?.mint_address, '30')
- ])
+ const promises = STAKEABLE_TOKENS_DATA.filter(
+ (token) => token.mint_address !== USDC_MINT,
+ ).map((t) => {
+ const isUsdcBorrow = t.name === 'JLP' || t.name === 'USDC'
+ const outputMint = isUsdcBorrow ? USDC_MINT : SOL_MINT
+ return fetchSwapChartPrices(t.mint_address, outputMint, '30')
+ })
+ const [jlpPrices, msolPrices, jitoPrices, bsolPrices] =
+ await Promise.all(promises)
// may be null if the price range cannot be calculated
/*
@@ -19,10 +25,26 @@ const fetchRates = async () => {
*/
const rateData: Record = {}
- rateData.jlp =
- (12 * (jlpPrices[jlpPrices.length - 2].price - jlpPrices[0].price)) /
- jlpPrices[0].price
-
+ if (jlpPrices && jlpPrices?.length > 1) {
+ rateData.jlp =
+ (12 * (jlpPrices[jlpPrices.length - 2].price - jlpPrices[0].price)) /
+ jlpPrices[0].price
+ }
+ if (msolPrices && msolPrices?.length > 1) {
+ rateData.msol =
+ (12 * (msolPrices[msolPrices.length - 2].price - msolPrices[0].price)) /
+ msolPrices[0].price
+ }
+ if (jitoPrices && jitoPrices?.length > 1) {
+ rateData.jito =
+ (12 * (jitoPrices[jitoPrices.length - 2].price - jitoPrices[0].price)) /
+ jitoPrices[0].price
+ }
+ if (bsolPrices && bsolPrices?.length > 1) {
+ rateData.bsol =
+ (12 * (bsolPrices[bsolPrices.length - 2].price - bsolPrices[0].price)) /
+ bsolPrices[0].price
+ }
/*
@@ -40,7 +62,7 @@ const fetchRates = async () => {
}
*/
-
+
return rateData
} catch (e) {
return {}
diff --git a/store/mangoStore.ts b/store/mangoStore.ts
index 1fa40d6..8b6489b 100644
--- a/store/mangoStore.ts
+++ b/store/mangoStore.ts
@@ -35,12 +35,9 @@ import {
BOOST_ACCOUNT_PREFIX,
BOOST_DATA_API_URL,
CONNECTION_COMMITMENT,
- DEFAULT_MARKET_NAME,
FALLBACK_ORACLES,
- INPUT_TOKEN_DEFAULT,
MANGO_DATA_API_URL,
MAX_PRIORITY_FEE_KEYS,
- OUTPUT_TOKEN_DEFAULT,
PAGINATION_PAGE_LENGTH,
RPC_PROVIDER_KEY,
STAKEABLE_TOKENS,
@@ -66,9 +63,7 @@ import {
PositionStat,
OrderbookTooltip,
} from 'types'
-import spotBalancesUpdater from './spotBalancesUpdater'
import { PerpMarket } from '@blockworks-foundation/mango-v4/'
-import perpPositionsUpdater from './perpPositionsUpdater'
import {
DEFAULT_PRIORITY_FEE,
TRITON_DEDICATED_URL,
@@ -84,7 +79,9 @@ import { sleep } from 'utils'
const MANGO_BOOST_ID = new PublicKey(
'zF2vSz6V9g1YHGmfrzsY497NJzbRr84QUrPry4bLQ25',
)
-const GROUP = new PublicKey('AKeMSYiJekyKfwCc3CUfVNDVAiqk9FfbQVMY3G7RUZUf')
+
+const GROUP_JLP = new PublicKey('AKeMSYiJekyKfwCc3CUfVNDVAiqk9FfbQVMY3G7RUZUf')
+const GROUP_V1 = new PublicKey('78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX')
const ENDPOINTS = [
{
@@ -162,7 +159,7 @@ export type MangoStore = {
}
connected: boolean
connection: Connection
- group: Group | undefined
+ group: { jlpGroup: Group | undefined; lstGroup: Group | undefined }
groupLoaded: boolean
client: MangoClient
showUserSetup: boolean
@@ -327,7 +324,7 @@ const mangoStore = create()(
},
connected: false,
connection,
- group: undefined,
+ group: { jlpGroup: undefined, lstGroup: undefined },
groupLoaded: false,
client,
showUserSetup: false,
@@ -531,61 +528,17 @@ const mangoStore = create()(
try {
const set = get().set
const client = get().client
- const group = await client.getGroup(GROUP)
- let selectedMarketName = get().selectedMarket.name
-
- if (!selectedMarketName) {
- selectedMarketName = DEFAULT_MARKET_NAME
- }
-
- const inputBank =
- group?.banksMapByName.get(INPUT_TOKEN_DEFAULT)?.[0]
- const outputBank =
- group?.banksMapByName.get(OUTPUT_TOKEN_DEFAULT)?.[0]
- const serumMarkets = Array.from(
- group.serum3MarketsMapByExternal.values(),
- ).map((m) => {
- // remove this when market name is updated
- if (m.name === 'MSOL/SOL') {
- m.name = 'mSOL/SOL'
- }
- return m
- })
-
- const perpMarkets = Array.from(group.perpMarketsMapByName.values())
- .filter(
- (p) =>
- p.publicKey.toString() !==
- '9Y8paZ5wUpzLFfQuHz8j2RtPrKsDtHx9sbgFmWb5abCw',
- )
- .sort((a, b) => a.name.localeCompare(b.name))
-
- const selectedMarket =
- serumMarkets.find((m) => m.name === selectedMarketName) ||
- perpMarkets.find((m) => m.name === selectedMarketName) ||
- serumMarkets[0]
+ const jlpGroup = await client.getGroup(GROUP_JLP)
+ const lstGroup = await client.getGroup(GROUP_V1)
set((state) => {
- state.group = group
+ state.group.jlpGroup = jlpGroup
+ state.group.lstGroup = lstGroup
state.groupLoaded = true
- state.serumMarkets = serumMarkets
- state.perpMarkets = perpMarkets
- state.selectedMarket.current = selectedMarket
- if (!state.swap.inputBank && !state.swap.outputBank) {
- state.swap.inputBank = inputBank
- state.swap.outputBank = outputBank
- } else {
- state.swap.inputBank = group.getFirstBankByMint(
- state.swap.inputBank!.mint,
- )
- state.swap.outputBank = group.getFirstBankByMint(
- state.swap.outputBank!.mint,
- )
- }
})
} catch (e) {
notify({ type: 'info', title: 'Unable to refresh data' })
- console.error('Error fetching group', e)
+ console.error('Error fetching groups', e)
}
},
reloadMangoAccount: async (confirmationSlot) => {
@@ -632,20 +585,33 @@ const mangoStore = create()(
},
fetchMangoAccounts: async (ownerPk: PublicKey) => {
const set = get().set
- // const actions = get().actions
try {
- const group = get().group
+ const jlpGroup = get().group.jlpGroup
+ const lstGroup = get().group.lstGroup
const client = get().client
const selectedMangoAccount = get().mangoAccount.current
const selectedToken = get().selectedToken
- if (!group) throw new Error('Group not loaded')
+ if (!jlpGroup) throw new Error('JLP group not loaded')
+ if (!lstGroup) throw new Error('LST group not loaded')
if (!client) throw new Error('Client not loaded')
- const [ownerMangoAccounts, delegateAccounts] = await Promise.all([
- client.getMangoAccountsForOwner(group, ownerPk),
- client.getMangoAccountsForDelegate(group, ownerPk),
+ const [
+ jlpOwnerMangoAccounts,
+ lstOwnerMangoAccounts,
+ jlpDelegateAccounts,
+ lstDelegateAccounts,
+ ] = await Promise.all([
+ client.getMangoAccountsForOwner(jlpGroup, ownerPk),
+ client.getMangoAccountsForOwner(lstGroup, ownerPk),
+ client.getMangoAccountsForDelegate(jlpGroup, ownerPk),
+ client.getMangoAccountsForDelegate(lstGroup, ownerPk),
])
- const mangoAccounts = [...ownerMangoAccounts, ...delegateAccounts]
+ const mangoAccounts = [
+ ...jlpOwnerMangoAccounts,
+ ...lstOwnerMangoAccounts,
+ ...jlpDelegateAccounts,
+ ...lstDelegateAccounts,
+ ]
console.log('mango accounts: ', mangoAccounts)
const selectedAccountIsNotInAccountsList = mangoAccounts.find(
(x) =>
@@ -675,16 +641,10 @@ const mangoStore = create()(
}
console.log('newSelectedMangoAccount', newSelectedMangoAccount)
- // await newSelectedMangoAccount.reloadSerum3OpenOrders(client)
set((state) => {
state.mangoAccount.current = newSelectedMangoAccount
state.mangoAccount.initialLoad = false
})
- // actions.fetchOpenOrders()
-
- // await Promise.all(
- // mangoAccounts.map((ma) => ma.reloadSerum3OpenOrders(client)),
- // )
set((state) => {
state.mangoAccounts = mangoAccounts
@@ -902,14 +862,4 @@ const mangoStore = create()(
}),
)
-mangoStore.subscribe((state) => state.mangoAccount.current, spotBalancesUpdater)
-mangoStore.subscribe(
- (state) => state.mangoAccount.openOrderAccounts,
- spotBalancesUpdater,
-)
-mangoStore.subscribe(
- (state) => state.mangoAccount.current,
- perpPositionsUpdater,
-)
-
export default mangoStore
diff --git a/store/perpPositionsUpdater.ts b/store/perpPositionsUpdater.ts
deleted file mode 100644
index 2add065..0000000
--- a/store/perpPositionsUpdater.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { PerpPosition } from '@blockworks-foundation/mango-v4'
-import mangoStore from './mangoStore'
-
-const perpPositionsUpdater = () => {
- const mangoAccount = mangoStore.getState().mangoAccount.current
- const group = mangoStore.getState().group
- const set = mangoStore.getState().set
-
- if (!mangoAccount || !group) return
-
- const positions: PerpPosition[] = []
-
- for (const perpMarket of mangoAccount.perpActive()) {
- const position = mangoAccount.getPerpPosition(perpMarket.marketIndex)
- if (position) {
- positions.push(position)
- }
- }
-
- set((s) => {
- s.mangoAccount.perpPositions = positions
- })
-}
-
-export default perpPositionsUpdater
diff --git a/store/spotBalancesUpdater.ts b/store/spotBalancesUpdater.ts
deleted file mode 100644
index d29c672..0000000
--- a/store/spotBalancesUpdater.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-import { toUiDecimals } from '@blockworks-foundation/mango-v4'
-import { SpotBalances } from 'types'
-import mangoStore from './mangoStore'
-
-const spotBalancesUpdater = () => {
- const mangoAccount = mangoStore.getState().mangoAccount.current
- const group = mangoStore.getState().group
- const openOrdersAccounts =
- mangoStore.getState().mangoAccount.openOrderAccounts
- const set = mangoStore.getState().set
-
- if (!mangoAccount || !group) return
-
- const balances: SpotBalances = {}
-
- for (const serumMarket of mangoAccount.serum3Active()) {
- const market = group.getSerum3MarketByMarketIndex(serumMarket.marketIndex)
- if (!market) continue
- const openOrdersAccForMkt = openOrdersAccounts.find((oo) =>
- oo.market.equals(market.serumMarketExternal),
- )
-
- let baseTokenUnsettled = 0
- let quoteTokenUnsettled = 0
- let baseTokenLockedInOrder = 0
- let quoteTokenLockedInOrder = 0
- if (openOrdersAccForMkt) {
- baseTokenUnsettled = toUiDecimals(
- openOrdersAccForMkt.baseTokenFree.toNumber(),
- group.getFirstBankByTokenIndex(serumMarket.baseTokenIndex).mintDecimals,
- )
- quoteTokenUnsettled = toUiDecimals(
- openOrdersAccForMkt.quoteTokenFree
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- .add((openOrdersAccForMkt as any)['referrerRebatesAccrued'])
- .toNumber(),
- group.getFirstBankByTokenIndex(serumMarket.quoteTokenIndex)
- .mintDecimals,
- )
- baseTokenLockedInOrder = toUiDecimals(
- openOrdersAccForMkt.baseTokenTotal
- .sub(openOrdersAccForMkt.baseTokenFree)
- .toNumber(),
- group.getFirstBankByTokenIndex(serumMarket.baseTokenIndex).mintDecimals,
- )
- quoteTokenLockedInOrder = toUiDecimals(
- openOrdersAccForMkt.quoteTokenTotal
- .sub(openOrdersAccForMkt.quoteTokenFree)
- .toNumber(),
- group.getFirstBankByTokenIndex(serumMarket.quoteTokenIndex)
- .mintDecimals,
- )
- }
-
- let quoteBalances =
- balances[
- group
- .getSerum3ExternalMarket(market.serumMarketExternal)
- .quoteMintAddress.toString()
- ]
- if (!quoteBalances) {
- quoteBalances = balances[
- group
- .getSerum3ExternalMarket(market.serumMarketExternal)
- .quoteMintAddress.toString()
- ] = { inOrders: 0, unsettled: 0 }
- }
- quoteBalances.inOrders += quoteTokenLockedInOrder || 0
- quoteBalances.unsettled += quoteTokenUnsettled
-
- let baseBalances =
- balances[
- group
- .getSerum3ExternalMarket(market.serumMarketExternal)
- .baseMintAddress.toString()
- ]
- if (!baseBalances) {
- baseBalances = balances[
- group
- .getSerum3ExternalMarket(market.serumMarketExternal)
- .baseMintAddress.toString()
- ] = { inOrders: 0, unsettled: 0 }
- }
- baseBalances.inOrders += baseTokenLockedInOrder
- baseBalances.unsettled += baseTokenUnsettled
- }
-
- set((s) => {
- s.mangoAccount.spotBalances = balances
- })
-}
-
-export default spotBalancesUpdater
diff --git a/utils/constants.ts b/utils/constants.ts
index d023231..15fc358 100644
--- a/utils/constants.ts
+++ b/utils/constants.ts
@@ -1,11 +1,40 @@
import { PublicKey } from '@solana/web3.js'
// lev stake
-export const BORROW_TOKEN = 'USDC'
+export const JLP_BORROW_TOKEN = 'USDC'
+export const LST_BORROW_TOKEN = 'SOL'
export const STAKEABLE_TOKENS_DATA = [
- { name: 'JLP', id: 1, active: true, mint_address: '27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4' },
- { name: 'USDC', id: 0, active: true, mint_address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' },
+ {
+ name: 'JLP',
+ id: 1,
+ active: true,
+ mint_address: '27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4',
+ },
+ {
+ name: 'USDC',
+ id: 0,
+ active: true,
+ mint_address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
+ },
+ {
+ name: 'MSOL',
+ id: 521,
+ active: true,
+ mint_address: 'mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So',
+ },
+ {
+ name: 'JitoSOL',
+ id: 621,
+ active: true,
+ mint_address: 'J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn',
+ },
+ {
+ name: 'bSOL',
+ id: 721,
+ active: true,
+ mint_address: 'bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1',
+ },
]
export const STAKEABLE_TOKENS = STAKEABLE_TOKENS_DATA.filter(
(d) => d.active,
@@ -27,6 +56,7 @@ export const SECONDS = 1000
export const INPUT_TOKEN_DEFAULT = 'USDC'
export const MANGO_MINT = 'MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac'
export const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
+export const SOL_MINT = 'So11111111111111111111111111111111111111112'
export const OUTPUT_TOKEN_DEFAULT = 'JLP'
export const JUPITER_V4_PROGRAM_ID =
@@ -152,6 +182,7 @@ export const CUSTOM_TOKEN_ICONS: { [key: string]: boolean } = {
'eth (portal)': true,
hnt: true,
jitosol: true,
+ jlp: true,
kin: true,
ldo: true,
mngo: true,
diff --git a/utils/orderbook.ts b/utils/orderbook.ts
deleted file mode 100644
index 60c8a1f..0000000
--- a/utils/orderbook.ts
+++ /dev/null
@@ -1,168 +0,0 @@
-import {
- BookSide,
- BookSideType,
- MangoClient,
- PerpMarket,
-} from '@blockworks-foundation/mango-v4'
-import { Market, Orderbook as SpotOrderBook } from '@project-serum/serum'
-import { AccountInfo } from '@solana/web3.js'
-import mangoStore from '@store/mangoStore'
-import Big from 'big.js'
-import { cumOrderbookSide } from 'types'
-import { getDecimalCount } from './numbers'
-
-export const getMarket = () => {
- const group = mangoStore.getState().group
- const selectedMarket = mangoStore.getState().selectedMarket.current
- if (!group || !selectedMarket) return
- return selectedMarket instanceof PerpMarket
- ? selectedMarket
- : group?.getSerum3ExternalMarket(selectedMarket.serumMarketExternal)
-}
-
-export const decodeBookL2 = (book: SpotOrderBook | BookSide): number[][] => {
- const depth = 300
- if (book instanceof SpotOrderBook) {
- return book.getL2(depth).map(([price, size]) => [price, size])
- } else if (book instanceof BookSide) {
- return book.getL2Ui(depth)
- }
- return []
-}
-
-export function decodeBook(
- client: MangoClient,
- market: Market | PerpMarket,
- accInfo: AccountInfo,
- side: 'bids' | 'asks',
-): SpotOrderBook | BookSide {
- if (market instanceof Market) {
- const book = SpotOrderBook.decode(market, accInfo.data)
- return book
- } else {
- const decodedAcc = client.program.coder.accounts.decode(
- 'bookSide',
- accInfo.data,
- )
- const book = BookSide.from(
- client,
- market,
- side === 'bids' ? BookSideType.bids : BookSideType.asks,
- decodedAcc,
- )
- return book
- }
-}
-
-export const updatePerpMarketOnGroup = (
- book: BookSide,
- side: 'bids' | 'asks',
-) => {
- const group = mangoStore.getState().group
- const perpMarket = group?.getPerpMarketByMarketIndex(
- book.perpMarket.perpMarketIndex,
- )
- if (perpMarket) {
- perpMarket[`_${side}`] = book
- // mangoStore.getState().actions.fetchOpenOrders()
- }
-}
-
-export const hasOpenOrderForPriceGroup = (
- openOrderPrices: number[],
- price: number,
- grouping: number,
- isGrouped: boolean,
-) => {
- if (!isGrouped) {
- return !!openOrderPrices.find((ooPrice) => {
- return ooPrice === price
- })
- }
- return !!openOrderPrices.find((ooPrice) => {
- return ooPrice >= price - grouping && ooPrice <= price + grouping
- })
-}
-
-export const getCumulativeOrderbookSide = (
- orders: number[][],
- totalSize: number,
- maxSize: number,
- depth: number,
- usersOpenOrderPrices: number[],
- grouping: number,
- isGrouped: boolean,
-): cumOrderbookSide[] => {
- let cumulativeSize = 0
- let cumulativeValue = 0
- return orders.slice(0, depth).map(([price, size]) => {
- cumulativeSize += size
- cumulativeValue += price * size
- return {
- price: Number(price),
- size,
- averagePrice: cumulativeValue / cumulativeSize,
- cumulativeValue: cumulativeValue,
- cumulativeSize,
- sizePercent: Math.round((cumulativeSize / (totalSize || 1)) * 100),
- cumulativeSizePercent: Math.round((size / (cumulativeSize || 1)) * 100),
- maxSizePercent: Math.round((size / (maxSize || 1)) * 100),
- isUsersOrder: hasOpenOrderForPriceGroup(
- usersOpenOrderPrices,
- price,
- grouping,
- isGrouped,
- ),
- }
- })
-}
-
-export const groupBy = (
- ordersArray: number[][],
- market: PerpMarket | Market,
- grouping: number,
- isBids: boolean,
-) => {
- if (!ordersArray || !market || !grouping || grouping == market?.tickSize) {
- return ordersArray || []
- }
- const groupFloors: Record = {}
- for (let i = 0; i < ordersArray.length; i++) {
- if (typeof ordersArray[i] == 'undefined') {
- break
- }
- const bigGrouping = Big(grouping)
- const bigOrder = Big(ordersArray[i][0])
-
- const floor = isBids
- ? bigOrder
- .div(bigGrouping)
- .round(0, Big.roundDown)
- .times(bigGrouping)
- .toNumber()
- : bigOrder
- .div(bigGrouping)
- .round(0, Big.roundUp)
- .times(bigGrouping)
- .toNumber()
- if (typeof groupFloors[floor] == 'undefined') {
- groupFloors[floor] = ordersArray[i][1]
- } else {
- groupFloors[floor] = ordersArray[i][1] + groupFloors[floor]
- }
- }
- const sortedGroups = Object.entries(groupFloors)
- .map((entry) => {
- return [
- +parseFloat(entry[0]).toFixed(getDecimalCount(grouping)),
- entry[1],
- ]
- })
- .sort((a: number[], b: number[]) => {
- if (!a || !b) {
- return -1
- }
- return isBids ? b[0] - a[0] : a[0] - b[0]
- })
- return sortedGroups
-}