Feature/dashboard improvements (#389)

* suggested and current tier

* switchboard icon
This commit is contained in:
Adrian Brzeziński 2024-02-16 18:23:52 +01:00 committed by GitHub
parent a872420b19
commit 39367f1d22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 161 additions and 103 deletions

View File

@ -0,0 +1,38 @@
const SwitchboardIcon = ({ className }: { className?: string }) => (
<svg
className={`${className}`}
width="50"
height="50"
viewBox="0 0 77 59"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
>
<g id="Logo-/-Branding" stroke="none" fill="none">
<g
id="Artboard-Copy-13"
transform="translate(-190.000000, -216.000000)"
fill="#4c6fff"
>
<g id="Group-2" transform="translate(190.073984, 216.936658)">
<g id="Group-2-Copy" transform="translate(7.793510, 0.000000)">
<path
d="M0,20.5618692 C0,8.37745584 8.46049721,0.23471226 21.504123,0 L68.949953,0 C66.1199986,6.2431525 63.8418731,9.40858447 62.1155763,9.49629592 L21.0826801,9.49629592 C13.9789978,9.51541386 9.70259597,13.6455584 9.70259597,20.5618692 C9.70259597,27.4032667 13.9656204,31.8262383 20.8431235,31.8537325 L39.0136632,31.8262382 C41.6527581,31.8537324 43.9144823,34.1278864 43.9144823,36.8658935 C43.9144823,39.6039006 41.8553038,41.1874861 39.2658587,41.3190533 L21.3712044,41.3397319 C8.62248533,41.3397319 0.195863032,32.9187591 0.00336072172,20.9793638 L0,20.5618692 Z"
id="Combined-Shape-Copy-6"
></path>
</g>
<g
id="Group-2-Copy"
transform="translate(34.474977, 36.656819) scale(-1, -1) translate(-34.474977, -36.656819) translate(0.000000, 15.986953)"
>
<path
d="M0,20.5618692 C0,8.37745584 8.46049721,0.23471226 21.504123,0 L68.949953,0 C66.1199986,6.2431525 63.8418731,9.40858447 62.1155763,9.49629592 L21.0826801,9.49629592 C13.9789978,9.51541386 9.70259597,13.6455584 9.70259597,20.5618692 C9.70259597,27.4032667 13.9656204,31.8262383 20.8431235,31.8537325 L39.0136632,31.8262382 C41.6527581,31.8537324 43.9144823,34.1278864 43.9144823,36.8658935 C43.9144823,39.6039006 41.8553038,41.1874861 39.2658587,41.3190533 L21.3712044,41.3397319 C8.62248533,41.3397319 0.195863032,32.9187591 0.00336072172,20.9793638 L0,20.5618692 Z"
id="Combined-Shape-Copy-5"
></path>
</g>
</g>
</g>
</g>
</svg>
)
export default SwitchboardIcon

View File

@ -1,4 +1,4 @@
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
import { ReactNode, useCallback, useEffect, useState } from 'react'
import { ModalProps } from '../../types/modal'
import Modal from '../shared/Modal'
import mangoStore from '@store/mangoStore'
@ -6,13 +6,13 @@ import { useWallet } from '@solana/wallet-adapter-react'
import GovernanceStore from '@store/governanceStore'
import {
formatSuggestedValues,
getApiTokenName,
getFormattedBankValues,
} from 'utils/governance/listingTools'
import {
Bank,
Group,
OracleProvider,
PriceImpact,
toUiDecimals,
} from '@blockworks-foundation/mango-v4'
import { AccountMeta, Transaction } from '@solana/web3.js'
@ -34,10 +34,8 @@ import {
LISTING_PRESETS,
LISTING_PRESETS_KEY,
MidPriceImpact,
getMidPriceImpacts,
getPresetWithAdjustedDepositLimit,
getPresetWithAdjustedNetBorrows,
getProposedKey,
getPythPresets,
getSwitchBoardPresets,
} from '@blockworks-foundation/mango-v4-settings/lib/helpers/listingTools'
@ -52,11 +50,15 @@ const DashboardSuggestedValues = ({
onClose,
bank,
group,
priceImpacts,
suggestedTierKey,
currentTier,
midPriceImp,
}: ModalProps & {
bank: Bank
group: Group
priceImpacts: PriceImpact[]
suggestedTierKey: LISTING_PRESETS_KEY
currentTier: LISTING_PRESET | undefined
midPriceImp: MidPriceImpact[]
}) => {
const client = mangoStore((s) => s.client)
//do not deconstruct wallet is used for anchor to sign
@ -74,63 +76,13 @@ const DashboardSuggestedValues = ({
: getSwitchBoardPresets(LISTING_PRESETS)
const [proposedTier, setProposedTier] =
useState<LISTING_PRESETS_KEY>('liab_1')
const [suggestedTier, setSuggestedTier] =
useState<LISTING_PRESETS_KEY>('liab_1')
useState<LISTING_PRESETS_KEY>(suggestedTierKey)
const [proposing, setProposing] = useState(false)
useEffect(() => {
setForcePythOracle(bank?.oracleProvider === OracleProvider.Pyth)
}, [bank.oracleProvider])
const getApiTokenName = (bankName: string) => {
if (bankName === 'ETH (Portal)') {
return 'ETH'
}
return bankName
}
const priceImpactsFiltered = useMemo(
() =>
getMidPriceImpacts(priceImpacts.length ? priceImpacts : []).filter(
(x) => x.symbol === getApiTokenName(bank.name),
),
[priceImpacts, bank.name],
)
const currentTier = useMemo(() => {
return Object.values(LISTING_PRESETS).find((x) =>
x.initLiabWeight.toFixed(1) === '1.8'
? x.initLiabWeight.toFixed(1) ===
bank?.initLiabWeight.toNumber().toFixed(1) &&
x.reduceOnly === bank.reduceOnly
: x.initLiabWeight.toFixed(1) ===
bank?.initLiabWeight.toNumber().toFixed(1),
)
}, [bank.initLiabWeight])
const getSuggestedTierForListedTokens = useCallback(async () => {
const filteredResp = priceImpactsFiltered
.filter((x) => x.avg_price_impact_percent < 1)
.reduce((acc: { [key: string]: MidPriceImpact }, val: MidPriceImpact) => {
if (
!acc[val.symbol] ||
val.target_amount > acc[val.symbol].target_amount
) {
acc[val.symbol] = val
}
return acc
}, {})
const priceImpact = filteredResp[getApiTokenName(bank.name)]
const suggestedTier = getProposedKey(
priceImpact?.target_amount,
bank.oracleProvider === OracleProvider.Pyth,
)
setProposedTier(suggestedTier)
setSuggestedTier(suggestedTier)
}, [bank.name, bank.oracleProvider, priceImpactsFiltered.toString()])
const proposeNewSuggestedValues = useCallback(
async (
bank: Bank,
@ -148,6 +100,7 @@ const DashboardSuggestedValues = ({
bank.uiPrice,
bank.mintDecimals,
)
console.log(preset)
const fieldsToChange = invalidFieldsKeys.reduce(
(obj, key) => ({ ...obj, [key]: preset[key as keyof typeof preset] }),
@ -314,6 +267,7 @@ const DashboardSuggestedValues = ({
connection,
fee,
group,
oracle,
proposals,
voter.tokenOwnerRecord,
vsrClient,
@ -321,10 +275,6 @@ const DashboardSuggestedValues = ({
],
)
useEffect(() => {
getSuggestedTierForListedTokens()
}, [getSuggestedTierForListedTokens])
const mintInfo = group.mintInfosMapByMint.get(bank.mint.toString())
const formattedBankValues = getFormattedBankValues(group, bank)
@ -376,8 +326,8 @@ const DashboardSuggestedValues = ({
>
<h3 className="mb-6">
<span>
{bank.name} - Suggested tier: {PRESETS[suggestedTier].preset_name}{' '}
Current tier: ~{currentTier?.preset_name}
{bank.name} - Suggested tier: {PRESETS[suggestedTierKey].preset_name}{' '}
Current tier: ~ {currentTier?.preset_name}
</span>
<div className="py-4">
<p className="mb-2">
@ -398,7 +348,7 @@ const DashboardSuggestedValues = ({
<div className="flex w-full items-center justify-between">
{PRESETS[name as LISTING_PRESETS_KEY].preset_name}{' '}
{`{${PRESETS[name as LISTING_PRESETS_KEY].preset_key}}`}
{name === suggestedTier ? '- suggested' : ''}
{name === suggestedTierKey ? ' - suggested' : ''}
</div>
</Select.Option>
))}
@ -668,18 +618,20 @@ const DashboardSuggestedValues = ({
/>
<div>
<h3 className="mb-4 pl-6">Price impacts</h3>
{priceImpactsFiltered.map((x) => (
<div className="flex pl-6" key={x.target_amount}>
<p className="mr-4 w-[150px] space-x-4">
<span>Amount:</span>
<span>${x.target_amount}</span>
</p>
<p className="space-x-4">
<span>Price impact:</span>{' '}
<span>{x.avg_price_impact_percent.toFixed(3)}%</span>
</p>
</div>
))}
{midPriceImp
.filter((x) => x.symbol === getApiTokenName(bank.name))
.map((x) => (
<div className="flex pl-6" key={x.target_amount}>
<p className="mr-4 w-[150px] space-x-4">
<span>Amount:</span>
<span>${x.target_amount}</span>
</p>
<p className="space-x-4">
<span>Price impact:</span>{' '}
<span>{x.avg_price_impact_percent.toFixed(3)}%</span>
</p>
</div>
))}
</div>
</Disclosure.Panel>

View File

@ -1,5 +1,6 @@
import { Bank } from '@blockworks-foundation/mango-v4'
import PythIcon from '@components/icons/PythIcon'
import SwitchboardIcon from '@components/icons/SwitchboardIcon'
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/20/solid'
import useOracleProvider from 'hooks/useOracleProvider'
@ -16,6 +17,9 @@ const OracleProvider = ({ bank }: { bank?: Bank }) => {
{oracleProvider === 'Pyth' ? (
<PythIcon className="mr-1.5 h-4 w-4" />
) : null}
{oracleProvider === 'Switchboard' ? (
<SwitchboardIcon className="mr-1.5 h-4 w-4" />
) : null}
<span className="mr-1.5">{oracleProvider}</span>
<ArrowTopRightOnSquareIcon className="h-4 w-4" />
</a>

View File

@ -10,7 +10,7 @@ import { coder } from '@project-serum/anchor/dist/cjs/spl/token'
import mangoStore from '@store/mangoStore'
import useMangoGroup from 'hooks/useMangoGroup'
import type { NextPage } from 'next'
import { ReactNode, useCallback, useEffect, useState } from 'react'
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import {
ArrowTopRightOnSquareIcon,
@ -23,8 +23,8 @@ import BN from 'bn.js'
import { useRouter } from 'next/router'
import Link from 'next/link'
import {
getApiTokenName,
getFormattedBankValues,
getPriceImpacts,
} from 'utils/governance/listingTools'
import GovernancePageWrapper from '@components/governance/GovernancePageWrapper'
import TokenLogo from '@components/shared/TokenLogo'
@ -37,6 +37,12 @@ import { PythHttpClient } from '@pythnetwork/client'
import { MAINNET_PYTH_PROGRAM } from 'utils/governance/constants'
import { PublicKey } from '@solana/web3.js'
import { notify } from 'utils/notifications'
import {
LISTING_PRESETS,
MidPriceImpact,
getMidPriceImpacts,
getProposedKey,
} from '@blockworks-foundation/mango-v4-settings/lib/helpers/listingTools'
dayjs.extend(relativeTime)
@ -63,10 +69,51 @@ const Dashboard: NextPage = () => {
const { group } = useMangoGroup()
const connection = mangoStore((s) => s.connection)
const { banks } = useBanks()
const [isOpenSuggestionModal, setIsOpenSuggestionModal] = useState(false)
const [priceImpacts, setPriceImapcts] = useState<PriceImpact[]>([])
const [openedSuggestedModal, setOpenedSuggestedModal] = useState<
string | null
>(null)
const priceImpacts: PriceImpact[] = group?.pis || []
const [stickyIndex, setStickyIndex] = useState(-1)
const midPriceImp = useMemo(
() => getMidPriceImpacts(priceImpacts.length ? priceImpacts : []),
[priceImpacts],
)
const getSuggestedAndCurrentTier = (bank: Bank) => {
const currentTier = Object.values(LISTING_PRESETS).find((x) => {
return x.initLiabWeight.toFixed(1) === '1.8'
? x.initLiabWeight.toFixed(1) ===
bank?.initLiabWeight.toNumber().toFixed(1) &&
x.reduceOnly === bank.reduceOnly
: x.initLiabWeight.toFixed(1) ===
bank?.initLiabWeight.toNumber().toFixed(1)
})
const filteredResp = midPriceImp
.filter((x) => x.avg_price_impact_percent < 1)
.reduce((acc: { [key: string]: MidPriceImpact }, val: MidPriceImpact) => {
if (
!acc[val.symbol] ||
val.target_amount > acc[val.symbol].target_amount
) {
acc[val.symbol] = val
}
return acc
}, {})
const priceImpact = filteredResp[getApiTokenName(bank.name)]
const suggestedTierKey = getProposedKey(
priceImpact?.target_amount,
bank.oracleProvider === OracleProvider.Pyth,
)
return {
suggestedTierKey,
currentTier,
}
}
const getPythLink = async (pythOraclePk: PublicKey) => {
const pythClient = new PythHttpClient(connection, MAINNET_PYTH_PROGRAM)
const pythAccounts = await pythClient.getData()
@ -121,16 +168,6 @@ const Dashboard: NextPage = () => {
}
}, [banks.length])
useEffect(() => {
const handleGetPriceImapcts = async () => {
const [resp] = await Promise.all([getPriceImpacts()])
setPriceImapcts(resp)
}
if (group) {
handleGetPriceImapcts()
}
}, [connection, group])
return (
<GovernancePageWrapper noStyles={true}>
<div className="col-span-12 lg:col-span-8 lg:col-start-3">
@ -171,7 +208,12 @@ const Dashboard: NextPage = () => {
Collpase All
</Button>
</div>
<h3 className="mb-3 mt-6 text-base text-th-fgd-3">Banks</h3>
<h3 className="mb-3 mt-6 flex text-base text-th-fgd-3">
<span>Banks</span>
<span className="ml-auto">
Current / <span className="text-th-success">Suggested</span>{' '}
</span>
</h3>
<div className="border-b border-th-bkg-3">
{banks
.sort((a, b) => a.name.localeCompare(b.name))
@ -185,6 +227,9 @@ const Dashboard: NextPage = () => {
bank,
)
const { currentTier, suggestedTierKey } =
getSuggestedAndCurrentTier(bank)
return (
<Disclosure key={bank.publicKey.toString()}>
{({ open }) => (
@ -203,11 +248,21 @@ const Dashboard: NextPage = () => {
className="flex w-full items-center justify-between"
aria-label="panel"
>
<div className="flex items-center">
<div className="flex flex-1 items-center">
<TokenLogo bank={bank} />
<p className="ml-2 text-th-fgd-2">
{formattedBankValues.name} Bank
</p>
<div className="ml-auto flex space-x-2">
<div>{currentTier?.preset_name}</div>
<div>/</div>
<div className="text-th-success">
{
LISTING_PRESETS[suggestedTierKey]
.preset_name
}
</div>
</div>
</div>
<ChevronDownIcon
className={`${
@ -486,17 +541,27 @@ const Dashboard: NextPage = () => {
<div className="mb-4 mt-2 flex">
<Button
className=" ml-auto"
onClick={() => setIsOpenSuggestionModal(true)}
onClick={() =>
setOpenedSuggestedModal(
bank.mint.toBase58(),
)
}
>
Check suggested values
{isOpenSuggestionModal && (
{openedSuggestedModal ===
bank.mint.toBase58() && (
<DashboardSuggestedValues
priceImpacts={priceImpacts}
midPriceImp={midPriceImp}
currentTier={currentTier}
suggestedTierKey={suggestedTierKey}
group={group}
bank={bank}
isOpen={isOpenSuggestionModal}
isOpen={
openedSuggestedModal ===
bank.mint.toBase58()
}
onClose={() =>
setIsOpenSuggestionModal(false)
setOpenedSuggestedModal(null)
}
></DashboardSuggestedValues>
)}

View File

@ -457,10 +457,9 @@ export type MidPriceImpact = Omit<
'side' | 'min_price_impact_percent' | 'max_price_impact_percent'
>
export const getPriceImpacts = async () => {
const resp = await fetch(
'https://api.mngo.cloud/data/v4/risk/listed-tokens-one-week-price-impacts',
)
const jsonReps = (await resp.json()) as PriceImpact[]
return jsonReps
export const getApiTokenName = (bankName: string) => {
if (bankName === 'ETH (Portal)') {
return 'ETH'
}
return bankName
}