merge main
This commit is contained in:
commit
6945707c11
|
@ -26,7 +26,7 @@ import { useTranslation } from 'next-i18next'
|
|||
import TermsOfUseModal from './modals/TermsOfUseModal'
|
||||
import { useTheme } from 'next-themes'
|
||||
|
||||
const sideBarAnimationDuration = 300
|
||||
export const sideBarAnimationDuration = 300
|
||||
const termsLastUpdated = 1679441610978
|
||||
|
||||
const Layout = ({ children }: { children: ReactNode }) => {
|
||||
|
|
|
@ -29,6 +29,7 @@ import HealthHeart from './account/HealthHeart'
|
|||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import { useTheme } from 'next-themes'
|
||||
import LeaderboardIcon from './icons/LeaderboardIcon'
|
||||
import { sideBarAnimationDuration } from './Layout'
|
||||
|
||||
const SideNav = ({ collapsed }: { collapsed: boolean }) => {
|
||||
const { t } = useTranslation(['common', 'search'])
|
||||
|
@ -48,7 +49,7 @@ const SideNav = ({ collapsed }: { collapsed: boolean }) => {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={`transition-all duration-300 ${
|
||||
className={`transition-all duration-${sideBarAnimationDuration} ${
|
||||
collapsed ? 'w-[64px]' : 'w-[200px]'
|
||||
} border-r border-th-bkg-3 bg-th-bkg-1 bg-repeat`}
|
||||
style={{ backgroundImage: `url(${themeData.sideTilePath})` }}
|
||||
|
@ -65,7 +66,7 @@ const SideNav = ({ collapsed }: { collapsed: boolean }) => {
|
|||
<div className="mb-2">
|
||||
<Link href={'/'} shallow={true} passHref legacyBehavior>
|
||||
<div
|
||||
className={`items-center transition-all duration-300 ease-in-out ${
|
||||
className={`items-center transition-all duration-${sideBarAnimationDuration} ease-in-out ${
|
||||
collapsed ? '' : 'justify-start'
|
||||
} pb-1 pl-3`}
|
||||
>
|
||||
|
@ -80,10 +81,10 @@ const SideNav = ({ collapsed }: { collapsed: boolean }) => {
|
|||
<Transition
|
||||
show={!collapsed}
|
||||
as={Fragment}
|
||||
enter="transition ease-in duration-200"
|
||||
enter="transition-all ease-in duration-200"
|
||||
enterFrom="opacity-50"
|
||||
enterTo="opacity-100"
|
||||
leave="transition ease-out duration-200"
|
||||
leave="transition-all ease-out duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
@ -304,10 +305,10 @@ const MenuItem = ({
|
|||
<Transition
|
||||
show={!collapsed}
|
||||
as={Fragment}
|
||||
enter="transition ease-in duration-300"
|
||||
enter={`transition-all ease-in duration-${sideBarAnimationDuration}`}
|
||||
enterFrom="opacity-50"
|
||||
enterTo="opacity-100"
|
||||
leave="transition ease-out duration-300"
|
||||
leave={`transition-all ease-out duration-${sideBarAnimationDuration}`}
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
@ -413,10 +414,10 @@ export const ExpandableMenuItem = ({
|
|||
appear={true}
|
||||
show={!collapsed}
|
||||
as={Fragment}
|
||||
enter="transition ease-in duration-300"
|
||||
enter={`transition-all ease-in duration-${sideBarAnimationDuration}`}
|
||||
enterFrom="opacity-50"
|
||||
enterTo="opacity-100"
|
||||
leave="transition ease-out duration-300"
|
||||
leave={`transition-all ease-out duration-${sideBarAnimationDuration}`}
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
|
@ -431,10 +432,10 @@ export const ExpandableMenuItem = ({
|
|||
</Disclosure.Button>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition-all ease-in duration-300"
|
||||
enter={`transition-all ease-in duration-${sideBarAnimationDuration}`}
|
||||
enterFrom="opacity-100 max-h-0"
|
||||
enterTo="opacity-100 max-h-80"
|
||||
leave="transition-all ease-out duration-300"
|
||||
leave={`transition-all ease-out duration-${sideBarAnimationDuration}`}
|
||||
leaveFrom="opacity-100 max-h-80"
|
||||
leaveTo="opacity-0 max-h-0"
|
||||
>
|
||||
|
|
|
@ -166,9 +166,12 @@ const AccountPage = () => {
|
|||
setShowPnlHistory(false)
|
||||
}
|
||||
|
||||
const accountValue = useMemo(() => {
|
||||
if (!group || !mangoAccount) return 0.0
|
||||
return toUiDecimalsForQuote(mangoAccount.getEquity(group).toNumber())
|
||||
const [accountPnl, accountValue] = useMemo(() => {
|
||||
if (!group || !mangoAccount) return [0, 0]
|
||||
return [
|
||||
toUiDecimalsForQuote(mangoAccount.getPnl(group).toNumber()),
|
||||
toUiDecimalsForQuote(mangoAccount.getEquity(group).toNumber()),
|
||||
]
|
||||
}, [group, mangoAccount])
|
||||
|
||||
const leverage = useMemo(() => {
|
||||
|
@ -184,13 +187,12 @@ const AccountPage = () => {
|
|||
}
|
||||
}, [mangoAccount, group, accountValue])
|
||||
|
||||
const [accountPnl, accountValueChange, oneDayPnlChange] = useMemo(() => {
|
||||
const [accountValueChange, oneDayPnlChange] = useMemo(() => {
|
||||
if (
|
||||
accountValue &&
|
||||
oneDayPerformanceData.length &&
|
||||
performanceData.length
|
||||
) {
|
||||
const accountPnl = performanceData[performanceData.length - 1].pnl
|
||||
const accountValueChange =
|
||||
accountValue - oneDayPerformanceData[0].account_equity
|
||||
const startDayPnl = oneDayPerformanceData[0].pnl
|
||||
|
@ -198,9 +200,9 @@ const AccountPage = () => {
|
|||
oneDayPerformanceData[oneDayPerformanceData.length - 1].pnl
|
||||
const oneDayPnlChange = endDayPnl - startDayPnl
|
||||
|
||||
return [accountPnl, accountValueChange, oneDayPnlChange]
|
||||
return [accountValueChange, oneDayPnlChange]
|
||||
}
|
||||
return [0, 0, 0]
|
||||
return [0, 0]
|
||||
}, [accountValue, oneDayPerformanceData, performanceData])
|
||||
|
||||
const interestTotalValue = useMemo(() => {
|
||||
|
@ -275,12 +277,12 @@ const AccountPage = () => {
|
|||
latestDataItem.borrow_interest_cumulative_usd,
|
||||
deposit_interest_cumulative_usd:
|
||||
latestDataItem.deposit_interest_cumulative_usd,
|
||||
pnl: latestDataItem.pnl,
|
||||
pnl: accountPnl,
|
||||
spot_value: latestDataItem.spot_value,
|
||||
transfer_balance: latestDataItem.transfer_balance,
|
||||
},
|
||||
]
|
||||
}, [accountValue, performanceData])
|
||||
}, [accountPnl, accountValue, performanceData])
|
||||
|
||||
return !chartToShow ? (
|
||||
<>
|
||||
|
@ -595,7 +597,7 @@ const AccountPage = () => {
|
|||
{t('total-interest-earned')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
{Math.abs(interestTotalValue) > 1 && mangoAccountAddress ? (
|
||||
{mangoAccountAddress && Math.abs(interestTotalValue) >= 2 ? (
|
||||
<Tooltip
|
||||
className="hidden md:block"
|
||||
content="Cumulative Interest Chart"
|
||||
|
@ -638,7 +640,7 @@ const AccountPage = () => {
|
|||
{t('account:total-funding-earned')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
{mangoAccountAddress ? (
|
||||
{mangoAccountAddress && Math.abs(fundingTotalValue) >= 2 ? (
|
||||
<Tooltip
|
||||
className="hidden md:block"
|
||||
content="Funding Chart"
|
||||
|
@ -695,7 +697,7 @@ const AccountPage = () => {
|
|||
<AccountChart
|
||||
chartToShow="pnl"
|
||||
setChartToShow={setChartToShow}
|
||||
data={performanceData}
|
||||
data={performanceData.concat(latestAccountData)}
|
||||
hideChart={handleHideChart}
|
||||
yKey="pnl"
|
||||
/>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import mangoStore from '@store/mangoStore'
|
||||
import {
|
||||
HealthType,
|
||||
toUiDecimalsForQuote,
|
||||
|
@ -35,10 +34,12 @@ const MangoAccountSummary = () => {
|
|||
const { t } = useTranslation('common')
|
||||
const { group } = useMangoGroup()
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const performanceData = mangoStore((s) => s.mangoAccount.performance.data)
|
||||
|
||||
const [accountValue, freeCollateral, health] = useMemo(() => {
|
||||
if (!group || !mangoAccount) return [0, 0, 0]
|
||||
const [accountPnl, accountValue, freeCollateral, health] = useMemo(() => {
|
||||
if (!group || !mangoAccount) return [0, 0, 0, 0]
|
||||
const accountPnl = toUiDecimalsForQuote(
|
||||
mangoAccount.getPnl(group).toNumber()
|
||||
)
|
||||
const accountValue = toUiDecimalsForQuote(
|
||||
mangoAccount.getEquity(group).toNumber()
|
||||
)
|
||||
|
@ -46,7 +47,7 @@ const MangoAccountSummary = () => {
|
|||
mangoAccount.getCollateralValue(group).toNumber()
|
||||
)
|
||||
const health = mangoAccount.getHealthRatioUi(group, HealthType.maint)
|
||||
return [accountValue, freeCollateral, health]
|
||||
return [accountPnl, accountValue, freeCollateral, health]
|
||||
}, [group, mangoAccount])
|
||||
|
||||
const leverage = useMemo(() => {
|
||||
|
@ -62,18 +63,13 @@ const MangoAccountSummary = () => {
|
|||
}
|
||||
}, [mangoAccount, group, accountValue])
|
||||
|
||||
const pnl = useMemo(() => {
|
||||
if (!performanceData.length) return 0
|
||||
return performanceData[performanceData.length - 1].pnl
|
||||
}, [performanceData])
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<SummaryItem label={t('account-value')} value={accountValue} isUsd />
|
||||
<SummaryItem label={t('health')} value={health} suffix="%" />
|
||||
<SummaryItem label={t('free-collateral')} value={freeCollateral} isUsd />
|
||||
<SummaryItem label={t('leverage')} value={leverage} suffix="x" />
|
||||
<SummaryItem label={t('pnl')} value={pnl} isUsd />
|
||||
<SummaryItem label={t('pnl')} value={accountPnl} isUsd />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ const Select = ({
|
|||
{({ open }) => (
|
||||
<>
|
||||
<Listbox.Button
|
||||
className={`h-full w-full rounded-md bg-th-input-bkg py-2.5 font-normal ring-1 ring-inset ring-th-input-border focus:outline-none focus:ring-th-fgd-4 md:hover:ring-th-input-border-hover`}
|
||||
className={`h-full w-full rounded-md bg-th-input-bkg py-2.5 font-normal ring-1 ring-inset ring-th-input-border focus:outline-none focus-visible:ring-th-fgd-4 md:hover:ring-th-input-border-hover`}
|
||||
>
|
||||
<div
|
||||
className={`flex items-center justify-between space-x-2 px-3 text-th-fgd-1`}
|
||||
|
@ -45,7 +45,7 @@ const Select = ({
|
|||
</div>
|
||||
</Listbox.Button>
|
||||
<Listbox.Options
|
||||
className={`thin-scroll absolute left-0 z-20 mt-1 max-h-60 w-full origin-top-left space-y-2 overflow-auto rounded-md bg-th-bkg-2 p-4 outline-none ${dropdownPanelClassName}`}
|
||||
className={`thin-scroll absolute left-0 z-20 mt-1 max-h-60 w-full origin-top-left overflow-auto rounded-md bg-th-bkg-2 p-2 outline-none ${dropdownPanelClassName}`}
|
||||
>
|
||||
{children}
|
||||
</Listbox.Options>
|
||||
|
@ -65,7 +65,7 @@ interface OptionProps {
|
|||
const Option = ({ value, children, className }: OptionProps) => {
|
||||
return (
|
||||
<Listbox.Option
|
||||
className="mb-0 text-th-fgd-2 hover:cursor-pointer focus-visible:text-th-active md:hover:text-th-fgd-1"
|
||||
className="default-transition mb-0 rounded-md p-2 text-th-fgd-2 hover:cursor-pointer focus-visible:text-th-active md:hover:bg-th-bkg-3 md:hover:text-th-fgd-1"
|
||||
value={value}
|
||||
>
|
||||
{({ selected }) => (
|
||||
|
|
|
@ -364,8 +364,10 @@ const ListMarket = ({ goBack }: { goBack: () => void }) => {
|
|||
</div>
|
||||
{createOpenbookMarketModal ? (
|
||||
<CreateOpenbookMarketModal
|
||||
quoteSymbol={quoteToken!}
|
||||
baseSymbol={baseToken!}
|
||||
quoteDecimals={quoteBank?.mintDecimals || 0}
|
||||
quoteMint={quoteBank?.mint.toBase58() || ''}
|
||||
baseDecimals={baseBank?.mintDecimals || 0}
|
||||
baseMint={baseBank?.mint.toBase58() || ''}
|
||||
isOpen={createOpenbookMarketModal}
|
||||
onClose={closeCreateOpenBookMarketModal}
|
||||
tradingParams={tradingParams}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import Input from '@components/forms/Input'
|
||||
import Label from '@components/forms/Label'
|
||||
import Button, { IconButton } from '@components/shared/Button'
|
||||
import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { ChangeEvent, useCallback, useMemo, useState } from 'react'
|
||||
import mangoStore, { CLUSTER } from '@store/mangoStore'
|
||||
import { Token } from 'types/jupiter'
|
||||
import { handleGetRoutes } from '@components/swap/useQuoteRoutes'
|
||||
import {
|
||||
JUPITER_API_DEVNET,
|
||||
JUPITER_API_MAINNET,
|
||||
USDC_MINT,
|
||||
} from 'utils/constants'
|
||||
import { PublicKey, SYSVAR_RENT_PUBKEY } from '@solana/web3.js'
|
||||
import { JUPITER_PRICE_API_MAINNET, USDC_MINT } from 'utils/constants'
|
||||
import { AccountMeta, PublicKey, SYSVAR_RENT_PUBKEY } from '@solana/web3.js'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import { OPENBOOK_PROGRAM_ID } from '@blockworks-foundation/mango-v4'
|
||||
import {
|
||||
OPENBOOK_PROGRAM_ID,
|
||||
RouteInfo,
|
||||
toNative,
|
||||
} from '@blockworks-foundation/mango-v4'
|
||||
import {
|
||||
MANGO_DAO_WALLET,
|
||||
MANGO_DAO_WALLET_GOVERNANCE,
|
||||
|
@ -23,7 +23,6 @@ import {
|
|||
ChevronDownIcon,
|
||||
ExclamationCircleIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import BN from 'bn.js'
|
||||
import { createProposal } from 'utils/governance/instructions/createProposal'
|
||||
import GovernanceStore from '@store/governanceStore'
|
||||
import { notify } from 'utils/notifications'
|
||||
|
@ -37,9 +36,19 @@ import { useEnhancedWallet } from '@components/wallet/EnhancedWalletProvider'
|
|||
import { abbreviateAddress } from 'utils/formatting'
|
||||
import { formatNumericValue } from 'utils/numbers'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import { getBestMarket, getOracle } from 'utils/governance/listingTools'
|
||||
import {
|
||||
LISTING_PRESETS,
|
||||
coinTiersToNames,
|
||||
getBestMarket,
|
||||
getOracle,
|
||||
} from 'utils/governance/listingTools'
|
||||
import { fmtTokenAmount, tryGetPubKey } from 'utils/governance/tools'
|
||||
import OnBoarding from '../OnBoarding'
|
||||
import CreateOpenbookMarketModal from '@components/modals/CreateOpenbookMarketModal'
|
||||
import { calculateTradingParameters } from 'utils/governance/listingTools'
|
||||
import useJupiterMints from 'hooks/useJupiterMints'
|
||||
import CreateSwitchboardOracleModal from '@components/modals/CreateSwitchboardOracleModal'
|
||||
import { BN } from '@project-serum/anchor'
|
||||
|
||||
type FormErrors = Partial<Record<keyof TokenListForm, string>>
|
||||
|
||||
|
@ -75,6 +84,7 @@ const defaultTokenListFormValues: TokenListForm = {
|
|||
|
||||
const ListToken = ({ goBack }: { goBack: () => void }) => {
|
||||
const wallet = useWallet()
|
||||
const { jupiterTokens } = useJupiterMints()
|
||||
const connection = mangoStore((s) => s.connection)
|
||||
const client = mangoStore((s) => s.client)
|
||||
const { group } = useMangoGroup()
|
||||
|
@ -93,15 +103,23 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
...defaultTokenListFormValues,
|
||||
})
|
||||
const [loadingListingParams, setLoadingListingParams] = useState(false)
|
||||
const [tokenList, setTokenList] = useState<Token[]>([])
|
||||
const [formErrors, setFormErrors] = useState<FormErrors>({})
|
||||
const [priceImpact, setPriceImpact] = useState<number>(0)
|
||||
const [currentTokenInfo, setCurrentTokenInfo] = useState<
|
||||
Token | null | undefined
|
||||
>(null)
|
||||
const [baseTokenPrice, setBaseTokenPrice] = useState<number>(0)
|
||||
const [proposalPk, setProposalPk] = useState<string | null>(null)
|
||||
const [mint, setMint] = useState('')
|
||||
const [creatingProposal, setCreatingProposal] = useState(false)
|
||||
const [createOpenbookMarketModal, setCreateOpenbookMarket] = useState(false)
|
||||
const [orcaPoolAddress, setOrcaPoolAddress] = useState('')
|
||||
const [raydiumPoolAddress, setRaydiumPoolAddress] = useState('')
|
||||
const [oracleModalOpen, setOracleModalOpen] = useState(false)
|
||||
const [coinTier, setCoinTier] = useState('')
|
||||
const isMidOrPremium = coinTier === 'PREMIUM' || coinTier === 'MID'
|
||||
|
||||
const quoteBank = group?.getFirstBankByMint(new PublicKey(USDC_MINT))
|
||||
const minVoterWeight = useMemo(
|
||||
() =>
|
||||
governances
|
||||
|
@ -109,63 +127,48 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
.minCommunityTokensToCreateProposal
|
||||
: new BN(0),
|
||||
[governances]
|
||||
)
|
||||
) as BN
|
||||
const mintVoterWeightNumber = governances
|
||||
? fmtTokenAmount(minVoterWeight, MANGO_MINT_DECIMALS)
|
||||
: 0
|
||||
const tradingParams = useMemo(() => {
|
||||
if (quoteBank && currentTokenInfo) {
|
||||
return calculateTradingParameters(
|
||||
baseTokenPrice,
|
||||
quoteBank.uiPrice,
|
||||
currentTokenInfo.decimals,
|
||||
quoteBank.mintDecimals
|
||||
)
|
||||
}
|
||||
return {
|
||||
baseLots: 0,
|
||||
quoteLots: 0,
|
||||
minOrderValue: 0,
|
||||
baseLotExponent: 0,
|
||||
quoteLotExponent: 0,
|
||||
minOrderSize: 0,
|
||||
priceIncrement: 0,
|
||||
priceIncrementRelative: 0,
|
||||
}
|
||||
}, [quoteBank, currentTokenInfo, baseTokenPrice])
|
||||
const tierPreset = useMemo(() => {
|
||||
return LISTING_PRESETS[coinTier] || {}
|
||||
}, [coinTier])
|
||||
|
||||
const handleSetAdvForm = (propertyName: string, value: string | number) => {
|
||||
setFormErrors({})
|
||||
setAdvForm({ ...advForm, [propertyName]: value })
|
||||
}
|
||||
|
||||
const handleTokenFind = async () => {
|
||||
cancel()
|
||||
if (!tryGetPubKey(mint)) {
|
||||
notify({
|
||||
title: t('enter-valid-token-mint'),
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
let currentTokenList: Token[] = tokenList
|
||||
if (!tokenList.length) {
|
||||
currentTokenList = await getTokenList()
|
||||
setTokenList(currentTokenList)
|
||||
}
|
||||
const tokenInfo = currentTokenList.find((x) => x.address === mint)
|
||||
setCurrentTokenInfo(tokenInfo)
|
||||
if (tokenInfo) {
|
||||
handleLiqudityCheck(new PublicKey(mint))
|
||||
getListingParams(tokenInfo)
|
||||
}
|
||||
}
|
||||
|
||||
const getTokenList = useCallback(async () => {
|
||||
try {
|
||||
const url =
|
||||
CLUSTER === 'devnet' ? JUPITER_API_DEVNET : JUPITER_API_MAINNET
|
||||
const response = await fetch(url)
|
||||
const data: Token[] = await response.json()
|
||||
return data
|
||||
} catch (e) {
|
||||
notify({
|
||||
title: t('cant-find-token-for-mint'),
|
||||
description: `${e}`,
|
||||
type: 'error',
|
||||
})
|
||||
return []
|
||||
}
|
||||
}, [t])
|
||||
|
||||
const getListingParams = useCallback(
|
||||
async (tokenInfo: Token) => {
|
||||
async (tokenInfo: Token, isMidOrPremium: boolean) => {
|
||||
setLoadingListingParams(true)
|
||||
const [oraclePk, marketPk] = await Promise.all([
|
||||
getOracle({
|
||||
baseSymbol: tokenInfo.symbol,
|
||||
quoteSymbol: 'usd',
|
||||
connection,
|
||||
pythOnly: isMidOrPremium,
|
||||
}),
|
||||
getBestMarket({
|
||||
baseMint: mint,
|
||||
|
@ -211,21 +214,69 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
const handleLiqudityCheck = useCallback(
|
||||
async (tokenMint: PublicKey) => {
|
||||
try {
|
||||
//we check price impact on token for 10k USDC
|
||||
const USDC_AMOUNT = 10000000000
|
||||
const SLIPPAGE_BPS = 50
|
||||
const MODE = 'ExactIn'
|
||||
const FEE = 0
|
||||
const { bestRoute } = await handleGetRoutes(
|
||||
USDC_MINT,
|
||||
tokenMint.toBase58(),
|
||||
USDC_AMOUNT,
|
||||
SLIPPAGE_BPS,
|
||||
MODE,
|
||||
FEE,
|
||||
wallet.publicKey ? wallet.publicKey?.toBase58() : emptyPk
|
||||
const walletForCheck = wallet.publicKey
|
||||
? wallet.publicKey?.toBase58()
|
||||
: emptyPk
|
||||
const shitCoinIdx = 3
|
||||
|
||||
const TIERS = ['PREMIUM', 'MID', 'MEME', 'SHIT']
|
||||
const swaps = await Promise.all([
|
||||
handleGetRoutes(
|
||||
USDC_MINT,
|
||||
tokenMint.toBase58(),
|
||||
toNative(100000, 6).toNumber(),
|
||||
SLIPPAGE_BPS,
|
||||
MODE,
|
||||
FEE,
|
||||
walletForCheck,
|
||||
'JUPITER'
|
||||
),
|
||||
handleGetRoutes(
|
||||
USDC_MINT,
|
||||
tokenMint.toBase58(),
|
||||
toNative(20000, 6).toNumber(),
|
||||
SLIPPAGE_BPS,
|
||||
MODE,
|
||||
FEE,
|
||||
walletForCheck,
|
||||
'JUPITER'
|
||||
),
|
||||
handleGetRoutes(
|
||||
USDC_MINT,
|
||||
tokenMint.toBase58(),
|
||||
toNative(5000, 6).toNumber(),
|
||||
SLIPPAGE_BPS,
|
||||
MODE,
|
||||
FEE,
|
||||
walletForCheck,
|
||||
'JUPITER'
|
||||
),
|
||||
handleGetRoutes(
|
||||
USDC_MINT,
|
||||
tokenMint.toBase58(),
|
||||
toNative(1000, 6).toNumber(),
|
||||
SLIPPAGE_BPS,
|
||||
MODE,
|
||||
FEE,
|
||||
walletForCheck,
|
||||
'JUPITER'
|
||||
),
|
||||
])
|
||||
const mid = swaps[1]
|
||||
const indexForTierFromSwaps = swaps.findIndex(
|
||||
(x) =>
|
||||
x.bestRoute?.priceImpactPct && x.bestRoute?.priceImpactPct * 100 < 1
|
||||
)
|
||||
setPriceImpact(bestRoute ? bestRoute.priceImpactPct * 100 : 100)
|
||||
const tierIdx: number =
|
||||
indexForTierFromSwaps > -1 ? indexForTierFromSwaps : shitCoinIdx
|
||||
const tier = TIERS[tierIdx]
|
||||
setCoinTier(tier)
|
||||
setPriceImpact(mid.bestRoute ? mid.bestRoute.priceImpactPct * 100 : 100)
|
||||
handleGetPoolParams(mid.routes)
|
||||
return tier
|
||||
} catch (e) {
|
||||
notify({
|
||||
title: t('liquidity-check-error'),
|
||||
|
@ -237,11 +288,45 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
[t, wallet.publicKey]
|
||||
)
|
||||
|
||||
const handleGetPoolParams = (routes: never[] | RouteInfo[]) => {
|
||||
const marketInfos = routes.flatMap((x) => x.marketInfos)
|
||||
const orcaPool = marketInfos.find((x) => x.label === 'Orca')
|
||||
const raydiumPool = marketInfos.find((x) => x.label === 'Raydium')
|
||||
setOrcaPoolAddress(orcaPool?.id || '')
|
||||
setRaydiumPoolAddress(raydiumPool?.id || '')
|
||||
}
|
||||
|
||||
const handleTokenFind = useCallback(async () => {
|
||||
cancel()
|
||||
if (!tryGetPubKey(mint)) {
|
||||
notify({
|
||||
title: t('enter-valid-token-mint'),
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
const tokenInfo = jupiterTokens.find((x) => x.address === mint)
|
||||
const priceInfo = await (
|
||||
await fetch(`${JUPITER_PRICE_API_MAINNET}/price?ids=${mint}`)
|
||||
).json()
|
||||
setBaseTokenPrice(priceInfo.data[mint]?.price || 0)
|
||||
setCurrentTokenInfo(tokenInfo)
|
||||
if (tokenInfo) {
|
||||
const tier = await handleLiqudityCheck(new PublicKey(mint))
|
||||
const isMidOrPremium = tier === 'PREMIUM' || tier === 'MID'
|
||||
getListingParams(tokenInfo, isMidOrPremium)
|
||||
}
|
||||
}, [getListingParams, handleLiqudityCheck, jupiterTokens, mint, t])
|
||||
|
||||
const cancel = () => {
|
||||
setCurrentTokenInfo(null)
|
||||
setPriceImpact(0)
|
||||
setAdvForm({ ...defaultTokenListFormValues })
|
||||
setProposalPk(null)
|
||||
setOrcaPoolAddress('')
|
||||
setRaydiumPoolAddress('')
|
||||
setCoinTier('')
|
||||
setBaseTokenPrice(0)
|
||||
}
|
||||
|
||||
const isFormValid = useCallback(
|
||||
|
@ -302,9 +387,44 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
return
|
||||
}
|
||||
|
||||
const [mintInfoPk] = PublicKey.findProgramAddressSync(
|
||||
[
|
||||
Buffer.from('MintInfo'),
|
||||
group!.publicKey.toBuffer(),
|
||||
new PublicKey(advForm.mintPk).toBuffer(),
|
||||
],
|
||||
client.programId
|
||||
)
|
||||
|
||||
const proposalTx = []
|
||||
|
||||
const registerTokenIx = await client!.program.methods
|
||||
.tokenRegisterTrustless(Number(advForm.tokenIndex), advForm.name)
|
||||
.tokenRegister(
|
||||
Number(advForm.tokenIndex),
|
||||
advForm.name,
|
||||
{
|
||||
confFilter: Number(tierPreset.oracleConfFilter),
|
||||
maxStalenessSlots: tierPreset.maxStalenessSlots,
|
||||
},
|
||||
{
|
||||
adjustmentFactor: Number(tierPreset.adjustmentFactor),
|
||||
util0: Number(tierPreset.util0),
|
||||
rate0: Number(tierPreset.rate0),
|
||||
util1: Number(tierPreset.util1),
|
||||
rate1: Number(tierPreset.rate1),
|
||||
maxRate: Number(tierPreset.maxRate),
|
||||
},
|
||||
Number(tierPreset.loanFeeRate),
|
||||
Number(tierPreset.loanOriginationFeeRate),
|
||||
Number(tierPreset.maintAssetWeight),
|
||||
Number(tierPreset.initAssetWeight),
|
||||
Number(tierPreset.maintLiabWeight),
|
||||
Number(tierPreset.initLiabWeight),
|
||||
Number(tierPreset.liquidationFee),
|
||||
Number(tierPreset.minVaultToDepositsRatio),
|
||||
new BN(tierPreset.netBorrowLimitWindowSizeTs),
|
||||
new BN(tierPreset.netBorrowLimitPerWindowQuote)
|
||||
)
|
||||
.accounts({
|
||||
admin: MANGO_DAO_WALLET,
|
||||
group: group!.publicKey,
|
||||
|
@ -314,9 +434,51 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
rent: SYSVAR_RENT_PUBKEY,
|
||||
})
|
||||
.instruction()
|
||||
|
||||
proposalTx.push(registerTokenIx)
|
||||
|
||||
const editIx = await client!.program.methods
|
||||
.tokenEdit(
|
||||
null,
|
||||
null,
|
||||
tierPreset.insuranceFound ? null : false,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
tierPreset.borrowWeightScale,
|
||||
tierPreset.depositWeightScale,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
.accounts({
|
||||
oracle: new PublicKey(advForm.oraclePk),
|
||||
admin: MANGO_DAO_WALLET,
|
||||
group: group!.publicKey,
|
||||
mintInfo: mintInfoPk,
|
||||
})
|
||||
.remainingAccounts([
|
||||
{
|
||||
pubkey: new PublicKey(advForm.baseBankPk),
|
||||
isWritable: true,
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
])
|
||||
.instruction()
|
||||
proposalTx.push(editIx)
|
||||
|
||||
const registerMarketix = await client!.program.methods
|
||||
.serum3RegisterMarket(Number(advForm.marketIndex), advForm.marketName)
|
||||
.accounts({
|
||||
|
@ -366,15 +528,25 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
minVoterWeight,
|
||||
mintVoterWeightNumber,
|
||||
t,
|
||||
tierPreset,
|
||||
voter.tokenOwnerRecord,
|
||||
voter.voteWeight,
|
||||
vsrClient,
|
||||
wallet,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
setTokenList([])
|
||||
}, [])
|
||||
const closeCreateOpenBookMarketModal = () => {
|
||||
setCreateOpenbookMarket(false)
|
||||
if (currentTokenInfo) {
|
||||
getListingParams(currentTokenInfo, isMidOrPremium)
|
||||
}
|
||||
}
|
||||
const closeCreateOracleModal = () => {
|
||||
setOracleModalOpen(false)
|
||||
if (currentTokenInfo) {
|
||||
getListingParams(currentTokenInfo, isMidOrPremium)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -440,6 +612,10 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
<p>{t('symbol')}</p>
|
||||
<p className="text-th-fgd-2">{currentTokenInfo?.symbol}</p>
|
||||
</div>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<p>{t('tier')}</p>
|
||||
<p className="text-th-fgd-2">{coinTiersToNames[coinTier]}</p>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<p>{t('mint')}</p>
|
||||
<p className="flex items-center">
|
||||
|
@ -681,33 +857,76 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
)}
|
||||
</Disclosure>
|
||||
</div>
|
||||
{!advForm.oraclePk && !loadingListingParams ? (
|
||||
<div className="my-4">
|
||||
<InlineNotification
|
||||
desc={t('cant-list-oracle-not-found')}
|
||||
type="error"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{!advForm.openBookMarketExternalPk && !loadingListingParams ? (
|
||||
<div className="mb-4">
|
||||
<InlineNotification
|
||||
desc={
|
||||
<div>
|
||||
<a
|
||||
href="https://raydium.io/create-market"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
className="underline"
|
||||
>
|
||||
{t('cant-list-no-openbook-market')}
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
type="error"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<ol className="list-decimal pl-4">
|
||||
{!advForm.openBookMarketExternalPk &&
|
||||
coinTier &&
|
||||
!loadingListingParams ? (
|
||||
<li className="pl-2">
|
||||
<div className="mb-4">
|
||||
<InlineNotification
|
||||
desc={
|
||||
<div>
|
||||
<a
|
||||
onClick={() => setCreateOpenbookMarket(true)}
|
||||
className="cursor-pointer underline"
|
||||
>
|
||||
{t('cant-list-no-openbook-market')}
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
type="error"
|
||||
/>
|
||||
</div>
|
||||
{createOpenbookMarketModal ? (
|
||||
<CreateOpenbookMarketModal
|
||||
quoteMint={quoteBank?.mint.toBase58() || ''}
|
||||
baseMint={currentTokenInfo?.address || ''}
|
||||
baseDecimals={currentTokenInfo.decimals}
|
||||
quoteDecimals={quoteBank?.mintDecimals || 0}
|
||||
isOpen={createOpenbookMarketModal}
|
||||
onClose={closeCreateOpenBookMarketModal}
|
||||
tradingParams={tradingParams}
|
||||
/>
|
||||
) : null}
|
||||
</li>
|
||||
) : null}
|
||||
{!advForm.oraclePk && coinTier && !loadingListingParams ? (
|
||||
<li
|
||||
className={`my-4 pl-2 ${
|
||||
!advForm.openBookMarketExternalPk
|
||||
? 'disabled pointer-events-none opacity-60'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<InlineNotification
|
||||
desc={
|
||||
<div>
|
||||
{!isMidOrPremium ? (
|
||||
<a
|
||||
onClick={() => setOracleModalOpen(true)}
|
||||
className="cursor-pointer underline"
|
||||
>
|
||||
{t('cant-list-oracle-not-found-switch')}
|
||||
</a>
|
||||
) : (
|
||||
t('cant-list-oracle-not-found-pyth')
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
type="error"
|
||||
/>
|
||||
<CreateSwitchboardOracleModal
|
||||
orcaPoolAddress={orcaPoolAddress}
|
||||
raydiumPoolAddress={raydiumPoolAddress}
|
||||
baseTokenName={currentTokenInfo.symbol}
|
||||
baseTokenPk={currentTokenInfo.address}
|
||||
openbookMarketPk={advForm.openBookMarketExternalPk}
|
||||
isOpen={oracleModalOpen}
|
||||
onClose={closeCreateOracleModal}
|
||||
></CreateSwitchboardOracleModal>
|
||||
</li>
|
||||
) : null}
|
||||
</ol>
|
||||
<div className="mt-6 flex flex-col space-y-3 sm:flex-row sm:space-y-0 sm:space-x-4">
|
||||
<Button secondary onClick={cancel} size="large">
|
||||
{t('cancel')}
|
||||
|
|
|
@ -20,8 +20,11 @@ const OnBoarding = ({ minVotes }: { minVotes?: BN }) => {
|
|||
const minVoterWeight = minVotes
|
||||
? minVotes
|
||||
: governances
|
||||
? governances[MANGO_DAO_WALLET_GOVERNANCE.toBase58()].account.config
|
||||
.minCommunityTokensToCreateProposal
|
||||
? new BN(
|
||||
governances[
|
||||
MANGO_DAO_WALLET_GOVERNANCE.toBase58()
|
||||
].account.config.minCommunityTokensToCreateProposal.toString()
|
||||
)
|
||||
: new BN(0)
|
||||
const mintVoterWeightNumber = governances
|
||||
? fmtTokenAmount(minVoterWeight, MANGO_MINT_DECIMALS)
|
||||
|
|
|
@ -44,9 +44,9 @@ export function VoteCountdown({
|
|||
const now = dayjs().unix()
|
||||
|
||||
let timeToVoteEnd = proposal.isPreVotingState()
|
||||
? governance.config.maxVotingTime
|
||||
? governance.config.baseVotingTime
|
||||
: (proposal.votingAt?.toNumber() ?? 0) +
|
||||
governance.config.maxVotingTime -
|
||||
governance.config.baseVotingTime -
|
||||
now
|
||||
|
||||
if (timeToVoteEnd <= 0) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Tooltip from '@components/shared/Tooltip'
|
||||
import { CheckCircleIcon } from '@heroicons/react/20/solid'
|
||||
import { BN } from '@project-serum/anchor'
|
||||
import { Governance, ProgramAccount, Proposal } from '@solana/spl-governance'
|
||||
import { RawMint } from '@solana/spl-token'
|
||||
import GovernanceStore from '@store/governanceStore'
|
||||
|
@ -29,11 +30,11 @@ const QuorumProgress = ({ governance, proposal, communityMint }: Props) => {
|
|||
)
|
||||
|
||||
const minimumYesVotes =
|
||||
fmtTokenAmount(maxVoteWeight!, communityMint.decimals) *
|
||||
fmtTokenAmount(new BN(maxVoteWeight!.toString()), communityMint.decimals) *
|
||||
(voteThresholdPct / 100)
|
||||
|
||||
const yesVoteCount = fmtTokenAmount(
|
||||
proposal.account.getYesVoteCount(),
|
||||
new BN(proposal.account.getYesVoteCount().toString()),
|
||||
communityMint.decimals
|
||||
)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import VoteResultsBar from './VoteResultBar'
|
|||
import { fmtTokenAmount } from 'utils/governance/tools'
|
||||
import { RawMint } from '@solana/spl-token'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { BN } from '@project-serum/anchor'
|
||||
|
||||
type VoteResultsProps = {
|
||||
proposal: Proposal
|
||||
|
@ -13,11 +14,11 @@ const VoteResults = ({ proposal, communityMint }: VoteResultsProps) => {
|
|||
const { t } = useTranslation(['governance'])
|
||||
|
||||
const yesVoteCount = fmtTokenAmount(
|
||||
proposal.getYesVoteCount(),
|
||||
proposal.getYesVoteCount() as BN,
|
||||
communityMint.decimals
|
||||
)
|
||||
const noVoteCount = fmtTokenAmount(
|
||||
proposal.getNoVoteCount(),
|
||||
proposal.getNoVoteCount() as BN,
|
||||
communityMint.decimals
|
||||
)
|
||||
const totalVoteCount = yesVoteCount + noVoteCount
|
||||
|
|
|
@ -2,7 +2,6 @@ import mangoStore, { CLUSTER } from '@store/mangoStore'
|
|||
import { ModalProps } from '../../types/modal'
|
||||
import Modal from '../shared/Modal'
|
||||
import { OPENBOOK_PROGRAM_ID } from '@blockworks-foundation/mango-v4'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import { ChangeEvent, useEffect, useState } from 'react'
|
||||
import Label from '@components/forms/Label'
|
||||
import Input from '@components/forms/Input'
|
||||
|
@ -46,36 +45,32 @@ const defaultFormValues: CreateObMarketForm = {
|
|||
}
|
||||
|
||||
type CreateOpenbookMarketModalProps = {
|
||||
quoteSymbol: string
|
||||
baseSymbol: string
|
||||
baseMint: string
|
||||
baseDecimals: number
|
||||
quoteMint: string
|
||||
quoteDecimals: number
|
||||
tradingParams: TradingParams
|
||||
}
|
||||
|
||||
const CreateOpenbookMarketModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
quoteSymbol,
|
||||
baseSymbol,
|
||||
baseMint,
|
||||
baseDecimals,
|
||||
quoteMint,
|
||||
quoteDecimals,
|
||||
tradingParams,
|
||||
}: ModalProps & CreateOpenbookMarketModalProps) => {
|
||||
const { t } = useTranslation(['governance'])
|
||||
const connection = mangoStore((s) => s.connection)
|
||||
const wallet = useWallet()
|
||||
const { handleConnect } = useEnhancedWallet()
|
||||
const { group } = useMangoGroup()
|
||||
|
||||
const [form, setForm] = useState({ ...defaultFormValues })
|
||||
const [formErrors, setFormErrors] = useState<FormErrors>({})
|
||||
const [creating, setCreating] = useState(false)
|
||||
const [solNeededToCreateMarket, setSolNeededToCreateMarket] = useState(0)
|
||||
|
||||
const baseBank = group?.banksMapByName.get(baseSymbol)?.length
|
||||
? group.banksMapByName.get(baseSymbol)![0]
|
||||
: null
|
||||
const quoteBank = group?.banksMapByName.get(quoteSymbol)?.length
|
||||
? group.banksMapByName.get(quoteSymbol)![0]
|
||||
: null
|
||||
|
||||
const handleSetAdvForm = (propertyName: string, value: string | number) => {
|
||||
setFormErrors({})
|
||||
setForm({ ...form, [propertyName]: value })
|
||||
|
@ -92,12 +87,12 @@ const CreateOpenbookMarketModal = ({
|
|||
connection,
|
||||
wallet: wallet.publicKey!,
|
||||
baseInfo: {
|
||||
mint: baseBank!.mint,
|
||||
decimals: baseBank!.mintDecimals,
|
||||
mint: new PublicKey(baseMint),
|
||||
decimals: baseDecimals,
|
||||
},
|
||||
quoteInfo: {
|
||||
mint: quoteBank!.mint,
|
||||
decimals: quoteBank!.mintDecimals,
|
||||
mint: new PublicKey(quoteMint),
|
||||
decimals: quoteDecimals,
|
||||
},
|
||||
lotSize: Number(form.minimumOrderSize),
|
||||
tickSize: Number(form.minimumPriceTickSize),
|
||||
|
@ -148,14 +143,14 @@ const CreateOpenbookMarketModal = ({
|
|||
useEffect(() => {
|
||||
setForm({
|
||||
programId: OPENBOOK_PROGRAM_ID[CLUSTER].toBase58(),
|
||||
baseMint: baseBank?.mint.toBase58() || '',
|
||||
quoteMint: quoteBank?.mint.toBase58() || '',
|
||||
baseMint: baseMint || '',
|
||||
quoteMint: quoteMint || '',
|
||||
minimumOrderSize: tradingParams.minOrderSize.toString(),
|
||||
minimumPriceTickSize: tradingParams.priceIncrement.toString(),
|
||||
})
|
||||
}, [
|
||||
baseBank?.mint,
|
||||
quoteBank?.mint,
|
||||
baseMint,
|
||||
quoteMint,
|
||||
tradingParams.minOrderSize,
|
||||
tradingParams.priceIncrement,
|
||||
])
|
||||
|
|
|
@ -0,0 +1,295 @@
|
|||
import mangoStore, { CLUSTER } from '@store/mangoStore'
|
||||
import { ModalProps } from '../../types/modal'
|
||||
import Modal from '../shared/Modal'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import {
|
||||
CrankAccount,
|
||||
QueueAccount,
|
||||
SwitchboardProgram,
|
||||
} from '@switchboard-xyz/solana.js'
|
||||
import { OracleJob } from '@switchboard-xyz/common'
|
||||
import Button from '@components/shared/Button'
|
||||
import { MANGO_DAO_WALLET } from 'utils/governance/constants'
|
||||
import { USDC_MINT } from 'utils/constants'
|
||||
import { Transaction } from '@solana/web3.js'
|
||||
import { chunk } from 'lodash'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { notify } from 'utils/notifications'
|
||||
import { isMangoError } from 'types'
|
||||
import { useCallback, useState } from 'react'
|
||||
import Loading from '@components/shared/Loading'
|
||||
|
||||
const poolAddressError = 'no-pool-address-found'
|
||||
|
||||
const SWITCHBOARD_PERMISSIONLESS_QUE =
|
||||
'5JYwqvKkqp35w8Nq3ba4z1WYUeJQ1rB36V8XvaGp6zn1'
|
||||
const SWITCHBOARD_PERMISSIONLESS_CRANK =
|
||||
'BKtF8yyQsj3Ft6jb2nkfpEKzARZVdGgdEPs6mFmZNmbA'
|
||||
|
||||
type BaseProps = ModalProps & {
|
||||
openbookMarketPk: string
|
||||
baseTokenPk: string
|
||||
baseTokenName: string
|
||||
}
|
||||
|
||||
type RaydiumProps = BaseProps & {
|
||||
raydiumPoolAddress: string
|
||||
orcaPoolAddress?: string
|
||||
}
|
||||
|
||||
type OrcaProps = BaseProps & {
|
||||
raydiumPoolAddress?: string
|
||||
orcaPoolAddress: string
|
||||
}
|
||||
|
||||
const CreateSwitchboardOracleModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
openbookMarketPk,
|
||||
baseTokenPk,
|
||||
baseTokenName,
|
||||
raydiumPoolAddress,
|
||||
orcaPoolAddress,
|
||||
}: RaydiumProps | OrcaProps) => {
|
||||
const { t } = useTranslation(['governance'])
|
||||
const connection = mangoStore((s) => s.connection)
|
||||
const wallet = useWallet()
|
||||
const quoteTokenName = 'USDC'
|
||||
|
||||
const [creatingOracle, setCreatingOracle] = useState(false)
|
||||
|
||||
const create = useCallback(async () => {
|
||||
try {
|
||||
setCreatingOracle(true)
|
||||
const payer = wallet!.publicKey!
|
||||
if (!orcaPoolAddress && !raydiumPoolAddress) {
|
||||
throw poolAddressError
|
||||
}
|
||||
const poolPropertyName = orcaPoolAddress
|
||||
? 'orcaPoolAddress'
|
||||
: 'raydiumPoolAddress'
|
||||
const poolAddress = orcaPoolAddress ? orcaPoolAddress : raydiumPoolAddress
|
||||
|
||||
const program = await SwitchboardProgram.load(CLUSTER, connection)
|
||||
|
||||
const [[queueAccount], [crankAccount]] = await Promise.all([
|
||||
QueueAccount.load(program, SWITCHBOARD_PERMISSIONLESS_QUE),
|
||||
CrankAccount.load(program, SWITCHBOARD_PERMISSIONLESS_CRANK),
|
||||
])
|
||||
|
||||
const [aggregatorAccount, txArray1] =
|
||||
await queueAccount.createFeedInstructions(payer, {
|
||||
name: `${baseTokenName}/${quoteTokenName}`,
|
||||
batchSize: 6,
|
||||
minRequiredOracleResults: 3,
|
||||
minRequiredJobResults: 2,
|
||||
minUpdateDelaySeconds: 300,
|
||||
withdrawAuthority: MANGO_DAO_WALLET,
|
||||
authority: payer,
|
||||
crankDataBuffer: crankAccount.dataBuffer?.publicKey,
|
||||
crankPubkey: crankAccount.publicKey,
|
||||
fundAmount: 2.6,
|
||||
basePriorityFee: 0,
|
||||
disableCrank: false,
|
||||
maxPriorityFeeMultiplier: 0,
|
||||
varianceThreshold: 0.5,
|
||||
priorityFeeBump: 0,
|
||||
priorityFeeBumpPeriod: 0,
|
||||
jobs: [
|
||||
{
|
||||
weight: 1,
|
||||
data: OracleJob.encodeDelimited(
|
||||
OracleJob.fromObject({
|
||||
tasks: [
|
||||
{
|
||||
conditionalTask: {
|
||||
attempt: [
|
||||
{
|
||||
valueTask: {
|
||||
big: '100',
|
||||
},
|
||||
},
|
||||
{
|
||||
divideTask: {
|
||||
job: {
|
||||
tasks: [
|
||||
{
|
||||
jupiterSwapTask: {
|
||||
inTokenAddress: USDC_MINT,
|
||||
outTokenAddress: baseTokenPk,
|
||||
baseAmountString: '100',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
onFailure: [
|
||||
{
|
||||
lpExchangeRateTask: {
|
||||
[poolPropertyName]: poolAddress,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
).finish(),
|
||||
},
|
||||
{
|
||||
weight: 1,
|
||||
data: OracleJob.encodeDelimited(
|
||||
OracleJob.fromObject({
|
||||
tasks: [
|
||||
{
|
||||
conditionalTask: {
|
||||
attempt: [
|
||||
{
|
||||
cacheTask: {
|
||||
cacheItems: [
|
||||
{
|
||||
variableName: 'IN_TOKEN_QTY',
|
||||
job: {
|
||||
tasks: [
|
||||
{
|
||||
valueTask: {
|
||||
big: '100',
|
||||
},
|
||||
},
|
||||
{
|
||||
divideTask: {
|
||||
job: {
|
||||
tasks: [
|
||||
{
|
||||
serumSwapTask: {
|
||||
serumPoolAddress:
|
||||
openbookMarketPk,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
jupiterSwapTask: {
|
||||
inTokenAddress: baseTokenPk,
|
||||
outTokenAddress: USDC_MINT,
|
||||
baseAmountString: '${IN_TOKEN_QTY}',
|
||||
},
|
||||
},
|
||||
{
|
||||
divideTask: {
|
||||
big: '${IN_TOKEN_QTY}',
|
||||
},
|
||||
},
|
||||
],
|
||||
onFailure: [
|
||||
{
|
||||
lpExchangeRateTask: {
|
||||
[poolPropertyName]: poolAddress,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
).finish(),
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const lockTx = aggregatorAccount.lockInstruction(payer, {})
|
||||
const transferAuthIx = aggregatorAccount.setAuthorityInstruction(payer, {
|
||||
newAuthority: MANGO_DAO_WALLET,
|
||||
})
|
||||
|
||||
const txChunks = chunk([...txArray1, lockTx, transferAuthIx], 1)
|
||||
const transactions: Transaction[] = []
|
||||
const latestBlockhash = await connection.getLatestBlockhash('finalized')
|
||||
for (const chunk of txChunks) {
|
||||
const tx = new Transaction()
|
||||
const singers = [...chunk.flatMap((x) => x.signers)]
|
||||
tx.add(...chunk.flatMap((x) => x.ixns))
|
||||
tx.lastValidBlockHeight = latestBlockhash.lastValidBlockHeight
|
||||
tx.recentBlockhash = latestBlockhash.blockhash
|
||||
tx.feePayer = payer
|
||||
if (singers.length) {
|
||||
tx.sign(...singers)
|
||||
}
|
||||
transactions.push(tx)
|
||||
}
|
||||
const signedTransactions = await wallet.signAllTransactions!(transactions)
|
||||
for (const tx of signedTransactions) {
|
||||
const rawTransaction = tx.serialize()
|
||||
const address = await connection.sendRawTransaction(rawTransaction, {
|
||||
skipPreflight: true,
|
||||
})
|
||||
await connection.confirmTransaction({
|
||||
blockhash: latestBlockhash.blockhash,
|
||||
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
||||
signature: address,
|
||||
})
|
||||
}
|
||||
setCreatingOracle(false)
|
||||
onClose()
|
||||
} catch (e) {
|
||||
setCreatingOracle(false)
|
||||
if (e === poolAddressError) {
|
||||
notify({
|
||||
title: 'Transaction failed',
|
||||
description: 'No orca or raydium pool found for oracle',
|
||||
type: 'error',
|
||||
})
|
||||
} else {
|
||||
if (!isMangoError(e)) return
|
||||
notify({
|
||||
title: 'Transaction failed',
|
||||
description: e.message,
|
||||
txid: e?.txid,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [
|
||||
baseTokenName,
|
||||
baseTokenPk,
|
||||
connection,
|
||||
onClose,
|
||||
openbookMarketPk,
|
||||
orcaPoolAddress,
|
||||
raydiumPoolAddress,
|
||||
wallet,
|
||||
])
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<div className="space-y-4 pb-4">
|
||||
<p>
|
||||
{t('create-switch-oracle')} {baseTokenName}/USDC
|
||||
</p>
|
||||
<p>{t('estimated-oracle-cost')}</p>
|
||||
</div>
|
||||
|
||||
<div className="float-right">
|
||||
<Button disabled={creatingOracle} onClick={create}>
|
||||
{creatingOracle ? (
|
||||
<Loading className="w-5"></Loading>
|
||||
) : (
|
||||
t('create-oracle')
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateSwitchboardOracleModal
|
|
@ -3,15 +3,15 @@ import dayjs from 'dayjs'
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { formatYAxis } from 'utils/formatting'
|
||||
// import { formatNumericValue } from 'utils/numbers'
|
||||
// import { usePerpFundingRate } from '@components/trade/PerpFundingRate'
|
||||
import { PerpStatsItem } from 'types'
|
||||
import { PerpMarket } from '@blockworks-foundation/mango-v4'
|
||||
import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart'
|
||||
import AverageFundingChart from './AverageFundingChart'
|
||||
|
||||
const CHART_WRAPPER_CLASSES = 'col-span-2 border-b border-th-bkg-3 py-4 px-6'
|
||||
const CHART_WRAPPER_CLASSES =
|
||||
'col-span-2 lg:col-span-1 border-b border-th-bkg-3 py-4 px-6'
|
||||
import PerpMarketParams from './PerpMarketParams'
|
||||
import PerpVolumeChart from './PerpVolumeChart'
|
||||
|
||||
const PerpMarketDetails = ({
|
||||
marketStats,
|
||||
|
@ -24,53 +24,22 @@ const PerpMarketDetails = ({
|
|||
const loadingPerpStats = mangoStore((s) => s.perpStats.loading)
|
||||
const [priceDaysToShow, setPriceDaysToShow] = useState('30')
|
||||
const [oiDaysToShow, setOiDaysToShow] = useState('30')
|
||||
// const [hourlyFundingeDaysToShow, setHourlyFundingDaysToShow] = useState('30')
|
||||
// const [instantFundingDaysToShow, setInstantFundingDaysToShow] = useState('30')
|
||||
// const rate = usePerpFundingRate()
|
||||
|
||||
const lastStat = useMemo(() => {
|
||||
if (!marketStats.length) return undefined
|
||||
return marketStats[marketStats.length - 1]
|
||||
}, [marketStats])
|
||||
|
||||
// const fundingRate = useMemo(() => {
|
||||
// if (!lastStat) return 0
|
||||
// if (rate?.isSuccess) {
|
||||
// const marketRate = rate?.data?.find(
|
||||
// (r) => r.market_index === perpMarket?.perpMarketIndex
|
||||
// )
|
||||
// return marketRate?.funding_rate_hourly
|
||||
// }
|
||||
// return lastStat.instantaneous_funding_rate
|
||||
// }, [rate, lastStat])
|
||||
|
||||
// const perpHourlyStats = useMemo(() => {
|
||||
// const latestStat = { ...lastStat } as PerpStatsItem
|
||||
// latestStat.instantaneous_funding_rate = fundingRate ? fundingRate : 0
|
||||
// latestStat.date_hour = dayjs().toISOString()
|
||||
// if (marketStats) {
|
||||
// const perpHourly = marketStats.concat([latestStat])
|
||||
// return perpHourly.map((stat) => ({
|
||||
// ...stat,
|
||||
// funding_rate_hourly: stat.funding_rate_hourly * 100,
|
||||
// }))
|
||||
// }
|
||||
// }, [marketStats, fundingRate])
|
||||
|
||||
// const instantFundingRateStats = useMemo(() => {
|
||||
// if (marketStats) {
|
||||
// return marketStats.map((stat) => ({
|
||||
// ...stat,
|
||||
// instantaneous_funding_rate: stat.instantaneous_funding_rate * 100,
|
||||
// }))
|
||||
// }
|
||||
// return []
|
||||
// }, [marketStats])
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2">
|
||||
{marketStats?.length && lastStat ? (
|
||||
<>
|
||||
<div className={`${CHART_WRAPPER_CLASSES} lg:border-r`}>
|
||||
<PerpVolumeChart
|
||||
loading={loadingPerpStats}
|
||||
marketStats={marketStats}
|
||||
/>
|
||||
</div>
|
||||
<div className={CHART_WRAPPER_CLASSES}>
|
||||
<DetailedAreaOrBarChart
|
||||
data={marketStats.concat([
|
||||
|
@ -93,42 +62,7 @@ const PerpMarketDetails = ({
|
|||
yKey={'open_interest'}
|
||||
/>
|
||||
</div>
|
||||
{/* old funding charts */}
|
||||
{/* <div className="col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1">
|
||||
<DetailedAreaOrBarChart
|
||||
data={perpHourlyStats ? perpHourlyStats : []}
|
||||
daysToShow={hourlyFundingeDaysToShow}
|
||||
setDaysToShow={setHourlyFundingDaysToShow}
|
||||
heightClass="h-64"
|
||||
loading={loadingPerpStats}
|
||||
loaderHeightClass="h-[350px]"
|
||||
suffix="%"
|
||||
tickFormat={(x) => formatNumericValue(x, 4)}
|
||||
title={t('trade:hourly-funding')}
|
||||
xKey="date_hour"
|
||||
yKey={'funding_rate_hourly'}
|
||||
yDecimals={5}
|
||||
showZeroLine
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-l md:pl-6">
|
||||
<DetailedAreaOrBarChart
|
||||
data={instantFundingRateStats}
|
||||
daysToShow={instantFundingDaysToShow}
|
||||
setDaysToShow={setInstantFundingDaysToShow}
|
||||
heightClass="h-64"
|
||||
loading={loadingPerpStats}
|
||||
loaderHeightClass="h-[350px]"
|
||||
suffix="%"
|
||||
tickFormat={(x) => formatNumericValue(x, 4)}
|
||||
title={t('trade:instantaneous-funding')}
|
||||
xKey="date_hour"
|
||||
yKey={'instantaneous_funding_rate'}
|
||||
yDecimals={5}
|
||||
showZeroLine
|
||||
/>
|
||||
</div> */}
|
||||
<div className={CHART_WRAPPER_CLASSES}>
|
||||
<div className={`${CHART_WRAPPER_CLASSES} lg:border-r`}>
|
||||
<AverageFundingChart
|
||||
loading={loadingPerpStats}
|
||||
marketStats={marketStats}
|
||||
|
|
|
@ -13,14 +13,16 @@ import {
|
|||
formatFunding,
|
||||
usePerpFundingRate,
|
||||
} from '@components/trade/PerpFundingRate'
|
||||
import { ChevronRightIcon } from '@heroicons/react/20/solid'
|
||||
import { ChevronDownIcon, ChevronRightIcon } from '@heroicons/react/20/solid'
|
||||
import FormatNumericValue from '@components/shared/FormatNumericValue'
|
||||
import { getDecimalCount } from 'utils/numbers'
|
||||
import { getDecimalCount, numberCompacter } from 'utils/numbers'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
import { PerpStatsItem } from 'types'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import { NextRouter, useRouter } from 'next/router'
|
||||
import SimpleAreaChart from '@components/shared/SimpleAreaChart'
|
||||
import { Disclosure, Transition } from '@headlessui/react'
|
||||
import { LinkButton } from '@components/shared/Button'
|
||||
|
||||
export const getOneDayPerpStats = (
|
||||
stats: PerpStatsItem[] | null,
|
||||
|
@ -76,9 +78,10 @@ const PerpMarketsOverviewTable = () => {
|
|||
</span>
|
||||
</Tooltip>
|
||||
</Th>
|
||||
<Th className="text-right">{t('rolling-change')}</Th>
|
||||
<Th className="text-right">{t('trade:24h-volume')}</Th>
|
||||
<Th className="text-right">{t('trade:funding-rate')}</Th>
|
||||
<Th className="text-right">{t('trade:open-interest')}</Th>
|
||||
<Th className="text-right">{t('rolling-change')}</Th>
|
||||
<Th />
|
||||
</TrHead>
|
||||
</thead>
|
||||
|
@ -93,6 +96,11 @@ const PerpMarketsOverviewTable = () => {
|
|||
100
|
||||
: 0
|
||||
|
||||
const volume = marketStats.length
|
||||
? marketStats[marketStats.length - 1].cumulative_quote_volume -
|
||||
marketStats[0].cumulative_quote_volume
|
||||
: 0
|
||||
|
||||
let fundingRate
|
||||
let fundingRateApr
|
||||
if (rate.isSuccess) {
|
||||
|
@ -186,6 +194,18 @@ const PerpMarketsOverviewTable = () => {
|
|||
</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex flex-col items-end">
|
||||
<Change change={change} suffix="%" />
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex flex-col text-right">
|
||||
<p>
|
||||
{volume ? `$${numberCompacter.format(volume)}` : '-'}
|
||||
</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex items-center justify-end">
|
||||
<Tooltip
|
||||
|
@ -224,18 +244,10 @@ const PerpMarketsOverviewTable = () => {
|
|||
/>
|
||||
</p>
|
||||
<p className="text-th-fgd-4">
|
||||
<FormatNumericValue
|
||||
value={openInterest * market.uiPrice}
|
||||
isUsd
|
||||
/>
|
||||
${numberCompacter.format(openInterest * market.uiPrice)}
|
||||
</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex flex-col items-end">
|
||||
<Change change={change} suffix="%" />
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex justify-end">
|
||||
<ChevronRightIcon className="h-5 w-5 text-th-fgd-3" />
|
||||
|
@ -247,7 +259,7 @@ const PerpMarketsOverviewTable = () => {
|
|||
</tbody>
|
||||
</Table>
|
||||
) : (
|
||||
<div>
|
||||
<div className="border-b border-th-bkg-3">
|
||||
{perpMarkets.map((market) => {
|
||||
return (
|
||||
<MobilePerpMarketItem
|
||||
|
@ -270,7 +282,7 @@ const MobilePerpMarketItem = ({ market }: { market: PerpMarket }) => {
|
|||
const perpStats = mangoStore((s) => s.perpStats.data)
|
||||
const { theme } = useTheme()
|
||||
const router = useRouter()
|
||||
// const rate = usePerpFundingRate()
|
||||
const rate = usePerpFundingRate()
|
||||
|
||||
const symbol = market.name.split('-')[0]
|
||||
|
||||
|
@ -280,59 +292,161 @@ const MobilePerpMarketItem = ({ market }: { market: PerpMarket }) => {
|
|||
? ((market.uiPrice - marketStats[0].price) / marketStats[0].price) * 100
|
||||
: 0
|
||||
|
||||
// let fundingRate
|
||||
// if (
|
||||
// rate.isSuccess
|
||||
// // && bids instanceof BookSide &&
|
||||
// // asks instanceof BookSide
|
||||
// ) {
|
||||
// const marketRate = rate.data.find(
|
||||
// (r) => r.market_index === market.perpMarketIndex
|
||||
// )
|
||||
// fundingRate = `${marketRate?.funding_apr.toFixed(2)}%`
|
||||
// } else {
|
||||
// fundingRate = '–'
|
||||
// }
|
||||
const volume = marketStats.length
|
||||
? marketStats[marketStats.length - 1].cumulative_quote_volume -
|
||||
marketStats[0].cumulative_quote_volume
|
||||
: 0
|
||||
|
||||
const openInterest = market.baseLotsToUi(market.openInterest)
|
||||
|
||||
let fundingRate: string
|
||||
let fundingRateApr: string
|
||||
if (rate.isSuccess) {
|
||||
const marketRate = rate?.data?.find(
|
||||
(r) => r.market_index === market.perpMarketIndex
|
||||
)
|
||||
if (marketRate) {
|
||||
fundingRate = formatFunding.format(marketRate.funding_rate_hourly)
|
||||
fundingRateApr = formatFunding.format(
|
||||
marketRate.funding_rate_hourly * 8760
|
||||
)
|
||||
} else {
|
||||
fundingRate = '–'
|
||||
fundingRateApr = '–'
|
||||
}
|
||||
} else {
|
||||
fundingRate = '–'
|
||||
fundingRateApr = '–'
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className="w-full border-b border-th-bkg-3 p-4 focus:outline-none"
|
||||
onClick={() => goToPerpMarketDetails(market, router)}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={market} />
|
||||
<div>
|
||||
<p className="text-left text-th-fgd-1">{market.name}</p>
|
||||
<div className="flex items-center space-x-3">
|
||||
<p className="font-mono">
|
||||
<FormatNumericValue value={market.uiPrice} isUsd />
|
||||
</p>
|
||||
<Change change={change} suffix="%" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
{!loadingPerpStats ? (
|
||||
marketStats.length ? (
|
||||
<div className="ml-4 h-10 w-24">
|
||||
<SimpleAreaChart
|
||||
color={change >= 0 ? COLORS.UP[theme] : COLORS.DOWN[theme]}
|
||||
data={marketStats}
|
||||
name={market.name}
|
||||
xKey="date_hour"
|
||||
yKey="price"
|
||||
<Disclosure>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button
|
||||
className={`w-full border-t border-th-bkg-3 p-4 text-left first:border-t-0 focus:outline-none`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<div className="flex flex-shrink-0 items-center">
|
||||
<MarketLogos market={market} />
|
||||
</div>
|
||||
<p className="leading-none text-th-fgd-1">{market.name}</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
{!loadingPerpStats ? (
|
||||
marketStats.length ? (
|
||||
<div className="ml-4 h-10 w-20">
|
||||
<SimpleAreaChart
|
||||
color={
|
||||
change >= 0 ? COLORS.UP[theme] : COLORS.DOWN[theme]
|
||||
}
|
||||
data={marketStats}
|
||||
name={market.name}
|
||||
xKey="date_hour"
|
||||
yKey="price"
|
||||
/>
|
||||
</div>
|
||||
) : symbol === 'USDC' || symbol === 'USDT' ? null : (
|
||||
<p className="mb-0 text-th-fgd-4">{t('unavailable')}</p>
|
||||
)
|
||||
) : (
|
||||
<div className="h-10 w-[104px] animate-pulse rounded bg-th-bkg-3" />
|
||||
)}
|
||||
<Change change={change} suffix="%" />
|
||||
<ChevronDownIcon
|
||||
className={`${
|
||||
open ? 'rotate-180' : 'rotate-360'
|
||||
} h-6 w-6 flex-shrink-0 text-th-fgd-3`}
|
||||
/>
|
||||
</div>
|
||||
) : symbol === 'USDC' || symbol === 'USDT' ? null : (
|
||||
<p className="mb-0 text-th-fgd-4">{t('unavailable')}</p>
|
||||
)
|
||||
) : (
|
||||
<div className="h-10 w-[104px] animate-pulse rounded bg-th-bkg-3" />
|
||||
)}
|
||||
<ChevronRightIcon className="h-5 w-5" />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</Disclosure.Button>
|
||||
<Transition
|
||||
enter="transition ease-in duration-200"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
>
|
||||
<Disclosure.Panel>
|
||||
<div className="mx-4 grid grid-cols-2 gap-4 border-t border-th-bkg-3 pt-4 pb-4">
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">{t('price')}</p>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
<FormatNumericValue value={market.uiPrice} isUsd />
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
{t('trade:24h-volume')}
|
||||
</p>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
{volume ? (
|
||||
<span>{numberCompacter.format(volume)}</span>
|
||||
) : (
|
||||
'–'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
{t('trade:funding-rate')}
|
||||
</p>
|
||||
<p className="font-mono">
|
||||
<Tooltip
|
||||
content={
|
||||
<>
|
||||
{fundingRateApr ? (
|
||||
<div className="">
|
||||
The 1hr rate as an APR is{' '}
|
||||
<span className="font-mono text-th-fgd-2">
|
||||
{fundingRateApr}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="mt-2">
|
||||
Funding is paid continuously. The 1hr rate displayed
|
||||
is a rolling average of the past 60 mins.
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
When positive, longs will pay shorts and when
|
||||
negative shorts pay longs.
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<span className="tooltip-underline text-th-fgd-2">
|
||||
{fundingRate}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
{t('trade:open-interest')}
|
||||
</p>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
<FormatNumericValue
|
||||
value={openInterest}
|
||||
decimals={getDecimalCount(market.minOrderSize)}
|
||||
/>
|
||||
<span className="mx-1 text-th-fgd-4">|</span>$
|
||||
{numberCompacter.format(openInterest * market.uiPrice)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-1">
|
||||
<LinkButton
|
||||
className="flex items-center"
|
||||
onClick={() => goToPerpMarketDetails(market, router)}
|
||||
>
|
||||
{t('token:token-stats', { token: market.name })}
|
||||
<ChevronRightIcon className="ml-2 h-5 w-5" />
|
||||
</LinkButton>
|
||||
</div>
|
||||
</div>
|
||||
</Disclosure.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
import { useMemo, useState } from 'react'
|
||||
import { GroupedDataItem, PerpStatsItem } from 'types'
|
||||
import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { formatYAxis } from 'utils/formatting'
|
||||
|
||||
const PerpVolumeChart = ({
|
||||
loading,
|
||||
marketStats,
|
||||
}: {
|
||||
loading: boolean
|
||||
marketStats: PerpStatsItem[]
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'stats', 'trade'])
|
||||
const [daysToShow, setDaysToShow] = useState('30')
|
||||
|
||||
const groupArrayByHours = (data: PerpStatsItem[], hours: number) => {
|
||||
const groupedData = []
|
||||
let currentGroup: GroupedDataItem[] = []
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const obj = data[i]
|
||||
const date = new Date(obj.date_hour)
|
||||
|
||||
if (hours === 24) {
|
||||
const day = date.getDate()
|
||||
const month = date.getMonth()
|
||||
if (
|
||||
currentGroup.length === 0 ||
|
||||
(currentGroup[0].day === day && currentGroup[0].month === month)
|
||||
) {
|
||||
currentGroup.push({ ...obj, day: day, month: month })
|
||||
} else {
|
||||
groupedData.push(currentGroup)
|
||||
currentGroup = [{ ...obj, day: day, month: month }]
|
||||
}
|
||||
} else {
|
||||
const intervalMillis = hours * 60 * 60 * 1000
|
||||
const timestamp = date.getTime()
|
||||
if (
|
||||
currentGroup.length === 0 ||
|
||||
timestamp - currentGroup[0].timestamp <= intervalMillis
|
||||
) {
|
||||
currentGroup.push({ ...obj, timestamp: timestamp })
|
||||
} else {
|
||||
groupedData.push(currentGroup)
|
||||
currentGroup = [{ ...obj, timestamp: timestamp }]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentGroup.length > 0) {
|
||||
groupedData.push(currentGroup)
|
||||
}
|
||||
return groupedData
|
||||
}
|
||||
|
||||
const interval = useMemo(() => {
|
||||
if (daysToShow === '30') {
|
||||
return 24
|
||||
} else if (daysToShow === '7') {
|
||||
return 6
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}, [daysToShow])
|
||||
|
||||
const chartData = useMemo(() => {
|
||||
if (!marketStats) return []
|
||||
const chartData = []
|
||||
if (interval !== 1) {
|
||||
const groupedData = groupArrayByHours(marketStats, interval)
|
||||
for (let i = 0; i < groupedData.length; i++) {
|
||||
const volume =
|
||||
groupedData[i][groupedData[i].length - 1].cumulative_quote_volume -
|
||||
groupedData[i][0].cumulative_quote_volume
|
||||
chartData.push({
|
||||
date_hour: groupedData[i][groupedData[i].length - 1].date_hour,
|
||||
volume: volume,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < marketStats.length; i++) {
|
||||
const volume =
|
||||
marketStats[i].cumulative_quote_volume -
|
||||
(marketStats[i - 1] ? marketStats[i - 1].cumulative_quote_volume : 0)
|
||||
chartData.push({
|
||||
date_hour: marketStats[i].date_hour,
|
||||
volume: volume,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return chartData
|
||||
}, [daysToShow, interval, marketStats])
|
||||
|
||||
return (
|
||||
<DetailedAreaOrBarChart
|
||||
data={chartData}
|
||||
daysToShow={daysToShow}
|
||||
setDaysToShow={setDaysToShow}
|
||||
heightClass="h-64"
|
||||
loading={loading}
|
||||
loaderHeightClass="h-[350px]"
|
||||
prefix="$"
|
||||
tickFormat={(x) => formatYAxis(x)}
|
||||
title={t('stats:volume')}
|
||||
xKey="date_hour"
|
||||
yKey="volume"
|
||||
// yDecimals={5}
|
||||
chartType="bar"
|
||||
tooltipDateFormat={daysToShow === '30' ? 'DD MMM YY' : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default PerpVolumeChart
|
|
@ -13,8 +13,13 @@ import useMangoGroup from 'hooks/useMangoGroup'
|
|||
import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
|
||||
import FormatNumericValue from '@components/shared/FormatNumericValue'
|
||||
import { useBirdeyeMarketPrices } from 'hooks/useBirdeyeMarketPrices'
|
||||
import { floorToDecimal, getDecimalCount } from 'utils/numbers'
|
||||
import { floorToDecimal, getDecimalCount, numberCompacter } from 'utils/numbers'
|
||||
import SimpleAreaChart from '@components/shared/SimpleAreaChart'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { fetchSpotVolume } from '@components/trade/AdvancedMarketHeader'
|
||||
import { TickerData } from 'types'
|
||||
import { Disclosure, Transition } from '@headlessui/react'
|
||||
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||
|
||||
const SpotMarketsTable = () => {
|
||||
const { t } = useTranslation('common')
|
||||
|
@ -26,6 +31,17 @@ const SpotMarketsTable = () => {
|
|||
const { data: birdeyePrices, isLoading: loadingPrices } =
|
||||
useBirdeyeMarketPrices()
|
||||
|
||||
const { data: spotVolumeData } = useQuery(
|
||||
['spot-market-volume'],
|
||||
() => fetchSpotVolume(),
|
||||
{
|
||||
cacheTime: 1000 * 60 * 10,
|
||||
staleTime: 1000 * 60,
|
||||
retry: 3,
|
||||
refetchOnWindowFocus: false,
|
||||
}
|
||||
)
|
||||
|
||||
return (
|
||||
<ContentBox hideBorder hidePadding>
|
||||
{showTableView ? (
|
||||
|
@ -34,8 +50,9 @@ const SpotMarketsTable = () => {
|
|||
<TrHead>
|
||||
<Th className="text-left">{t('market')}</Th>
|
||||
<Th className="text-right">{t('price')}</Th>
|
||||
<Th className="hidden text-right lg:block"></Th>
|
||||
<Th className="hidden text-right md:block"></Th>
|
||||
<Th className="text-right">{t('rolling-change')}</Th>
|
||||
<Th className="text-right">{t('trade:24h-volume')}</Th>
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -59,6 +76,12 @@ const SpotMarketsTable = () => {
|
|||
getDecimalCount(market.tickSize)
|
||||
).toNumber()
|
||||
}
|
||||
let tickerData: TickerData | undefined
|
||||
if (spotVolumeData && spotVolumeData.length) {
|
||||
tickerData = spotVolumeData.find(
|
||||
(m: TickerData) => m.ticker_id === mkt.name
|
||||
)
|
||||
}
|
||||
|
||||
const birdeyeData = birdeyePrices.find(
|
||||
(m) => m.mint === mkt.serumMarketExternal.toString()
|
||||
|
@ -103,7 +126,7 @@ const SpotMarketsTable = () => {
|
|||
: COLORS.DOWN[theme]
|
||||
}
|
||||
data={chartData}
|
||||
name={baseBank!.name}
|
||||
name={baseBank!.name + quoteBank!.name}
|
||||
xKey="unixTime"
|
||||
yKey="value"
|
||||
/>
|
||||
|
@ -123,13 +146,31 @@ const SpotMarketsTable = () => {
|
|||
<Change change={change} suffix="%" />
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex flex-col text-right">
|
||||
<p>
|
||||
{tickerData ? (
|
||||
<span>
|
||||
{numberCompacter.format(
|
||||
parseFloat(tickerData.target_volume)
|
||||
)}{' '}
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{quoteBank?.name}
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
'–'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</Td>
|
||||
</TrBody>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
) : (
|
||||
<div>
|
||||
<div className="border-b border-th-bkg-3">
|
||||
{serumMarkets
|
||||
.slice()
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
|
@ -138,6 +179,7 @@ const SpotMarketsTable = () => {
|
|||
<MobileSpotMarketItem
|
||||
key={market.publicKey.toString()}
|
||||
market={market}
|
||||
spotVolumeData={spotVolumeData}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
@ -149,7 +191,13 @@ const SpotMarketsTable = () => {
|
|||
|
||||
export default SpotMarketsTable
|
||||
|
||||
const MobileSpotMarketItem = ({ market }: { market: Serum3Market }) => {
|
||||
const MobileSpotMarketItem = ({
|
||||
market,
|
||||
spotVolumeData,
|
||||
}: {
|
||||
market: Serum3Market
|
||||
spotVolumeData: TickerData[] | undefined
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { data: birdeyePrices, isLoading: loadingPrices } =
|
||||
useBirdeyeMarketPrices()
|
||||
|
@ -192,39 +240,93 @@ const MobileSpotMarketItem = ({ market }: { market: Serum3Market }) => {
|
|||
return undefined
|
||||
}, [birdeyeData])
|
||||
|
||||
let tickerData: TickerData | undefined
|
||||
if (spotVolumeData && spotVolumeData.length) {
|
||||
tickerData = spotVolumeData.find(
|
||||
(m: TickerData) => m.ticker_id === market.name
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="border-b border-th-bkg-3 px-6 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={market} />
|
||||
<div>
|
||||
<p className="text-th-fgd-1">{market.name}</p>
|
||||
<div className="flex items-center space-x-3">
|
||||
<p className="font-mono">
|
||||
{price ? <FormatNumericValue value={price} isUsd /> : '-'}
|
||||
</p>
|
||||
{change ? <Change change={change} suffix="%" /> : '–'}
|
||||
<Disclosure>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button
|
||||
className={`w-full border-t border-th-bkg-3 p-4 text-left first:border-t-0 focus:outline-none`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<div className="flex flex-shrink-0 items-center">
|
||||
<MarketLogos market={market} />
|
||||
</div>
|
||||
<p className="leading-none text-th-fgd-1">{market.name}</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
{!loadingPrices ? (
|
||||
chartData !== undefined ? (
|
||||
<div className="h-10 w-20">
|
||||
<SimpleAreaChart
|
||||
color={
|
||||
change >= 0 ? COLORS.UP[theme] : COLORS.DOWN[theme]
|
||||
}
|
||||
data={chartData}
|
||||
name={baseBank!.name + quoteBank!.name}
|
||||
xKey="unixTime"
|
||||
yKey="value"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<p className="mb-0 text-th-fgd-4">{t('unavailable')}</p>
|
||||
)
|
||||
) : (
|
||||
<div className="h-10 w-[104px] animate-pulse rounded bg-th-bkg-3" />
|
||||
)}
|
||||
<Change change={change} suffix="%" />
|
||||
<ChevronDownIcon
|
||||
className={`${
|
||||
open ? 'rotate-180' : 'rotate-360'
|
||||
} h-6 w-6 flex-shrink-0 text-th-fgd-3`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!loadingPrices ? (
|
||||
chartData !== undefined ? (
|
||||
<div className="h-10 w-24">
|
||||
<SimpleAreaChart
|
||||
color={change >= 0 ? COLORS.UP[theme] : COLORS.DOWN[theme]}
|
||||
data={chartData}
|
||||
name={baseBank!.name}
|
||||
xKey="unixTime"
|
||||
yKey="value"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<p className="mb-0 text-th-fgd-4">{t('unavailable')}</p>
|
||||
)
|
||||
) : (
|
||||
<div className="h-10 w-[104px] animate-pulse rounded bg-th-bkg-3" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Disclosure.Button>
|
||||
<Transition
|
||||
enter="transition ease-in duration-200"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
>
|
||||
<Disclosure.Panel>
|
||||
<div className="mx-4 grid grid-cols-2 gap-4 border-t border-th-bkg-3 pt-4 pb-4">
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">{t('price')}</p>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
{price ? <FormatNumericValue value={price} isUsd /> : '-'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
{t('trade:24h-volume')}
|
||||
</p>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
{tickerData ? (
|
||||
<span>
|
||||
{numberCompacter.format(
|
||||
parseFloat(tickerData.target_volume)
|
||||
)}{' '}
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{quoteBank?.name}
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
'–'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Disclosure.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import {
|
|||
Cog8ToothIcon,
|
||||
ExclamationCircleIcon,
|
||||
LinkIcon,
|
||||
PencilIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import NumberFormat, {
|
||||
NumberFormatValues,
|
||||
|
@ -20,7 +19,7 @@ import useDebounce from '../shared/useDebounce'
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import SwapFormTokenList from './SwapFormTokenList'
|
||||
import { Transition } from '@headlessui/react'
|
||||
import Button, { IconButton } from '../shared/Button'
|
||||
import Button, { IconButton, LinkButton } from '../shared/Button'
|
||||
import Loading from '../shared/Loading'
|
||||
import { EnterBottomExitBottom } from '../shared/Transitions'
|
||||
import useQuoteRoutes from './useQuoteRoutes'
|
||||
|
@ -48,6 +47,7 @@ import { useEnhancedWallet } from '@components/wallet/EnhancedWalletProvider'
|
|||
import SwapSettings from './SwapSettings'
|
||||
import InlineNotification from '@components/shared/InlineNotification'
|
||||
import useUnownedAccount from 'hooks/useUnownedAccount'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
|
||||
const MAX_DIGITS = 11
|
||||
export const withValueLimit = (values: NumberFormatValues): boolean => {
|
||||
|
@ -486,19 +486,26 @@ const SwapForm = () => {
|
|||
<HealthImpact maintProjectedHealth={maintProjectedHealth} />
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-sm text-th-fgd-3">{t('swap:max-slippage')}</p>
|
||||
<div className="flex items-center space-x-1">
|
||||
<p className="text-right font-mono text-sm text-th-fgd-2">
|
||||
{slippage}%
|
||||
<Tooltip content={t('swap:tooltip-margin')}>
|
||||
<p className="tooltip-underline text-sm text-th-fgd-3">
|
||||
{t('swap:margin')}
|
||||
</p>
|
||||
<IconButton
|
||||
className="text-th-fgd-3"
|
||||
hideBg
|
||||
onClick={() => setShowSettings(true)}
|
||||
>
|
||||
<PencilIcon className="ml-2 h-4 w-4" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<LinkButton
|
||||
className="text-right text-sm font-normal text-th-fgd-2 underline underline-offset-2 md:hover:no-underline"
|
||||
onClick={() => setShowSettings(true)}
|
||||
>
|
||||
{useMargin ? t('swap:enabled') : t('swap:disabled')}
|
||||
</LinkButton>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-sm text-th-fgd-3">{t('swap:max-slippage')}</p>
|
||||
<LinkButton
|
||||
className="text-right font-mono text-sm font-normal text-th-fgd-2 underline underline-offset-2 md:hover:no-underline"
|
||||
onClick={() => setShowSettings(true)}
|
||||
>
|
||||
{slippage}%
|
||||
</LinkButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -111,30 +111,43 @@ export const handleGetRoutes = async (
|
|||
slippage = 50,
|
||||
swapMode = 'ExactIn',
|
||||
feeBps = 0,
|
||||
wallet: string | undefined | null
|
||||
wallet: string | undefined | null,
|
||||
mode: 'ALL' | 'JUPITER' | 'MANGO' = 'ALL'
|
||||
) => {
|
||||
try {
|
||||
wallet ||= PublicKey.default.toBase58()
|
||||
const mangoRoute = fetchMangoRoutes(
|
||||
inputMint,
|
||||
outputMint,
|
||||
amount,
|
||||
slippage,
|
||||
swapMode,
|
||||
feeBps,
|
||||
wallet
|
||||
)
|
||||
const jupiterRoute = fetchJupiterRoutes(
|
||||
inputMint,
|
||||
outputMint,
|
||||
amount,
|
||||
slippage,
|
||||
swapMode,
|
||||
feeBps
|
||||
)
|
||||
|
||||
const results = await Promise.allSettled([
|
||||
fetchMangoRoutes(
|
||||
inputMint,
|
||||
outputMint,
|
||||
amount,
|
||||
slippage,
|
||||
swapMode,
|
||||
feeBps,
|
||||
wallet
|
||||
),
|
||||
fetchJupiterRoutes(
|
||||
inputMint,
|
||||
outputMint,
|
||||
amount,
|
||||
slippage,
|
||||
swapMode,
|
||||
feeBps
|
||||
),
|
||||
])
|
||||
const routes = []
|
||||
if (mode == 'ALL') {
|
||||
routes.push(mangoRoute)
|
||||
routes.push(jupiterRoute)
|
||||
}
|
||||
|
||||
if (mode === 'MANGO') {
|
||||
routes.push(mangoRoute)
|
||||
}
|
||||
if (mode === 'JUPITER') {
|
||||
routes.push(jupiterRoute)
|
||||
}
|
||||
|
||||
const results = await Promise.allSettled(routes)
|
||||
const responses = results
|
||||
.filter((x) => x.status === 'fulfilled' && x.value.bestRoute !== null)
|
||||
.map((x) => (x as any).value)
|
||||
|
|
|
@ -18,23 +18,12 @@ import useMangoGroup from 'hooks/useMangoGroup'
|
|||
import OraclePrice from './OraclePrice'
|
||||
import SpotMarketDetailsModal from '@components/modals/SpotMarketDetailsModal'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { MANGO_DATA_OPENBOOK_URL } from 'utils/constants'
|
||||
import { TickerData } from 'types'
|
||||
|
||||
type TickerData = {
|
||||
base_currency: string
|
||||
base_volume: string
|
||||
high: string
|
||||
last_price: string
|
||||
low: string
|
||||
target_currency: string
|
||||
target_volume: string
|
||||
ticker_id: string
|
||||
}
|
||||
|
||||
const fetchSpotVolume = async () => {
|
||||
export const fetchSpotVolume = async () => {
|
||||
try {
|
||||
const data = await fetch(
|
||||
'https://api.mngo.cloud/openbook/v1/coingecko/tickers'
|
||||
)
|
||||
const data = await fetch(`${MANGO_DATA_OPENBOOK_URL}/coingecko/tickers`)
|
||||
const res = await data.json()
|
||||
return res
|
||||
} catch (e) {
|
||||
|
@ -103,6 +92,17 @@ const AdvancedMarketHeader = ({
|
|||
)
|
||||
}, [birdeyePrices, selectedMarket])
|
||||
|
||||
const oneDayPerpStats = useMemo(() => {
|
||||
if (
|
||||
!perpStats ||
|
||||
!perpStats.length ||
|
||||
!selectedMarketName ||
|
||||
!selectedMarketName.includes('PERP')
|
||||
)
|
||||
return []
|
||||
return getOneDayPerpStats(perpStats, selectedMarketName)
|
||||
}, [perpStats, selectedMarketName])
|
||||
|
||||
const change = useMemo(() => {
|
||||
if (
|
||||
!changePrice ||
|
||||
|
@ -111,9 +111,10 @@ const AdvancedMarketHeader = ({
|
|||
)
|
||||
return 0
|
||||
if (serumOrPerpMarket instanceof PerpMarket) {
|
||||
const changeData = getOneDayPerpStats(perpStats, selectedMarketName)
|
||||
return changeData.length
|
||||
? ((changePrice - changeData[0].price) / changeData[0].price) * 100
|
||||
return oneDayPerpStats.length
|
||||
? ((changePrice - oneDayPerpStats[0].price) /
|
||||
oneDayPerpStats[0].price) *
|
||||
100
|
||||
: 0
|
||||
} else {
|
||||
if (!birdeyeData) return 0
|
||||
|
@ -127,11 +128,19 @@ const AdvancedMarketHeader = ({
|
|||
birdeyeData,
|
||||
changePrice,
|
||||
serumOrPerpMarket,
|
||||
perpStats,
|
||||
oneDayPerpStats,
|
||||
previousMarketName,
|
||||
selectedMarketName,
|
||||
])
|
||||
|
||||
const perpVolume = useMemo(() => {
|
||||
if (!oneDayPerpStats.length) return
|
||||
return (
|
||||
oneDayPerpStats[oneDayPerpStats.length - 1].cumulative_quote_volume -
|
||||
oneDayPerpStats[0].cumulative_quote_volume
|
||||
)
|
||||
}, [oneDayPerpStats])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
|
@ -159,6 +168,18 @@ const AdvancedMarketHeader = ({
|
|||
</div>
|
||||
{serumOrPerpMarket instanceof PerpMarket ? (
|
||||
<>
|
||||
<div className="ml-6 flex-col whitespace-nowrap text-xs">
|
||||
<div className="mb-0.5 text-th-fgd-4 ">
|
||||
{t('trade:24h-volume')}
|
||||
</div>
|
||||
{perpVolume ? (
|
||||
<span className="font-mono">
|
||||
${numberCompacter.format(perpVolume)}{' '}
|
||||
</span>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</div>
|
||||
<PerpFundingRate />
|
||||
<div className="ml-6 flex-col whitespace-nowrap text-xs">
|
||||
<div className="mb-0.5 text-th-fgd-4 ">
|
||||
|
@ -192,7 +213,7 @@ const AdvancedMarketHeader = ({
|
|||
spotMarketVolume ? (
|
||||
<span className="font-mono">
|
||||
{numberCompacter.format(spotMarketVolume.target_volume)}{' '}
|
||||
<span className="font-body text-th-fgd-4">
|
||||
<span className="font-body text-th-fgd-3">
|
||||
{selectedMarketName.split('/')[1]}
|
||||
</span>
|
||||
</span>
|
||||
|
|
|
@ -59,14 +59,15 @@ const PerpPositions = () => {
|
|||
)
|
||||
}
|
||||
const newSide = positionSize > 0 ? 'sell' : 'buy'
|
||||
const baseSize = Math.abs(positionSize)
|
||||
const quoteSize = floorToDecimal(
|
||||
positionSize * price,
|
||||
baseSize * price,
|
||||
getDecimalCount(market.tickSize)
|
||||
)
|
||||
|
||||
set((s) => {
|
||||
s.tradeForm.side = newSide
|
||||
s.tradeForm.baseSize = positionSize.toString()
|
||||
s.tradeForm.baseSize = baseSize.toString()
|
||||
s.tradeForm.quoteSize = quoteSize.toString()
|
||||
})
|
||||
}
|
||||
|
@ -88,9 +89,17 @@ const PerpPositions = () => {
|
|||
|
||||
if (!group) return null
|
||||
|
||||
const openPerpPositions = Object.values(perpPositions).filter((p) =>
|
||||
p.basePositionLots.toNumber()
|
||||
)
|
||||
const openPerpPositions = Object.values(perpPositions)
|
||||
.filter((p) => p.basePositionLots.toNumber())
|
||||
.sort((a, b) => {
|
||||
const aMarket = group.getPerpMarketByMarketIndex(a.marketIndex)
|
||||
const bMarket = group.getPerpMarketByMarketIndex(b.marketIndex)
|
||||
const aBasePosition = a.getBasePositionUi(aMarket)
|
||||
const bBasePosition = b.getBasePositionUi(bMarket)
|
||||
const aNotional = aBasePosition * aMarket._uiPrice
|
||||
const bNotional = bBasePosition * bMarket._uiPrice
|
||||
return Math.abs(bNotional) - Math.abs(aNotional)
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -19,18 +19,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@blockworks-foundation/mango-feeds": "0.1.6",
|
||||
"@blockworks-foundation/mango-v4": "^0.15.12",
|
||||
"@blockworks-foundation/mango-v4": "^0.15.13",
|
||||
"@headlessui/react": "1.6.6",
|
||||
"@heroicons/react": "2.0.10",
|
||||
"@metaplex-foundation/js": "0.18.3",
|
||||
"@project-serum/anchor": "0.25.0",
|
||||
"@pythnetwork/client": "2.15.0",
|
||||
"@solana/spl-governance": "0.3.25",
|
||||
"@solana/spl-governance": "0.3.27",
|
||||
"@solana/spl-token": "0.3.7",
|
||||
"@solana/wallet-adapter-base": "0.9.20",
|
||||
"@solana/wallet-adapter-react": "0.15.32",
|
||||
"@solana/wallet-adapter-wallets": "0.19.11",
|
||||
"@solflare-wallet/pfp": "0.0.6",
|
||||
"@switchboard-xyz/solana.js": "2.2.0",
|
||||
"@tanstack/react-query": "4.10.1",
|
||||
"@tippyjs/react": "4.2.6",
|
||||
"@types/howler": "2.2.7",
|
||||
|
@ -113,6 +114,7 @@
|
|||
"@solana/wallet-adapter-wallets>@solana/wallet-adapter-torus>@toruslabs/solana-embed>@toruslabs/openlogin-jrpc>@toruslabs/openlogin-utils>keccak": true,
|
||||
"@metaplex-foundation/js>@bundlr-network/client>arbundles>secp256k1": true,
|
||||
"@metaplex-foundation/js>@bundlr-network/client>arbundles>keccak": true,
|
||||
"@switchboard-xyz/solana.js>@switchboard-xyz/common>protobufjs": true,
|
||||
"@solana/web3.js>bigint-buffer": false,
|
||||
"@solana/web3.js>rpc-websockets>bufferutil": true,
|
||||
"@solana/web3.js>rpc-websockets>utf-8-validate": true,
|
||||
|
|
|
@ -22,13 +22,18 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
|||
}
|
||||
}
|
||||
|
||||
type TableRow = {
|
||||
val: string | number | PublicKey
|
||||
highlight: boolean
|
||||
}
|
||||
|
||||
type TableData = {
|
||||
title: string
|
||||
data: Array<Record<string, string | number | PublicKey>>
|
||||
data: Array<Record<string, TableRow>>
|
||||
}
|
||||
|
||||
const formatValue = (val: string | number | PublicKey) => {
|
||||
if (val instanceof PublicKey) {
|
||||
if (val instanceof PublicKey || typeof val === 'object') {
|
||||
return val.toString()
|
||||
}
|
||||
if (typeof val === 'string') {
|
||||
|
@ -41,7 +46,7 @@ const formatValue = (val: string | number | PublicKey) => {
|
|||
const RiskDashboard: NextPage = () => {
|
||||
const { group } = useMangoGroup()
|
||||
|
||||
const { data, isLoading, isFetching } = useQuery(
|
||||
const { data } = useQuery(
|
||||
['risks'],
|
||||
() => {
|
||||
const provider = new AnchorProvider(
|
||||
|
@ -64,13 +69,13 @@ const RiskDashboard: NextPage = () => {
|
|||
{
|
||||
cacheTime: 1000 * 60 * 5,
|
||||
staleTime: 1000 * 60 * 5,
|
||||
retry: 1,
|
||||
retry: 0,
|
||||
refetchOnWindowFocus: false,
|
||||
enabled: !!group,
|
||||
}
|
||||
)
|
||||
|
||||
console.log('resp', isLoading, isFetching, data)
|
||||
console.log('resp', data)
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-12">
|
||||
|
@ -82,6 +87,8 @@ const RiskDashboard: NextPage = () => {
|
|||
<div className="mt-4">
|
||||
{Object.entries(data).map(
|
||||
([tableType, table]: [string, TableData]) => {
|
||||
console.log('table', table)
|
||||
if (!table?.data?.length) return null
|
||||
return (
|
||||
<div className="mt-12" key={tableType}>
|
||||
<div className="mb-4">
|
||||
|
@ -118,18 +125,19 @@ const RiskDashboard: NextPage = () => {
|
|||
</thead>
|
||||
<tbody>
|
||||
{table.data.map((rowData, index: number) => {
|
||||
console.log('rowData', Object.values(rowData))
|
||||
|
||||
return (
|
||||
<TrBody key={index}>
|
||||
{Object.values(rowData).map(
|
||||
(
|
||||
val: string | number | PublicKey,
|
||||
idx: number
|
||||
) => {
|
||||
(val, idx: number) => {
|
||||
return (
|
||||
<Td xBorder className={''} key={idx}>
|
||||
{formatValue(val)}
|
||||
<Td
|
||||
xBorder
|
||||
className={`${
|
||||
val?.highlight ? 'bg-th-bkg-2' : ''
|
||||
}`}
|
||||
key={idx}
|
||||
>
|
||||
{formatValue(val?.val)}
|
||||
</Td>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
"before-listing-4": "New markets and tokens are approved by DAO vote. This takes 3 days",
|
||||
"cancel": "Cancel",
|
||||
"cant-find-token-for-mint": "Can't find token for given mint",
|
||||
"cant-list-oracle-not-found": "Oracle not found. Create a Pyth/Switchboard oracle before continuing.",
|
||||
"cant-list-oracle-not-found-pyth": "Oracle not found. Create a Pyth oracle before continuing.",
|
||||
"cant-list-oracle-not-found-switch": "Oracle not found. Create a Switchboard oracle before continuing.",
|
||||
"cant-list-no-openbook-market": "Openbook market not found. Create an Openbook market before continuing.",
|
||||
"connect-to-list": "Connect to list a spot market or token",
|
||||
"connect-wallet": "Connect Wallet",
|
||||
|
@ -29,7 +30,7 @@
|
|||
"invalid-num": "Invalid Number",
|
||||
"invalid-pk": "Invalid Public Key",
|
||||
"liquidity-check-error": "Error during liquidity check",
|
||||
"liquidity-warning": "Low liquidity. Price impact of {{priceImpactPct}}% on a 10k USDC swap.",
|
||||
"liquidity-warning": "Low liquidity. Price impact of {{priceImpactPct}}% on a 20k USDC swap.",
|
||||
"list-spot-market": "List Spot Market",
|
||||
"list-spot-market-desc": "Create a market pair from tokens listed on Mango",
|
||||
"list-token": "List Token",
|
||||
|
@ -95,5 +96,9 @@
|
|||
"what-happens-next-3": "{{token}} will be listed on Mango automatically on execution.",
|
||||
"yes": "Yes",
|
||||
"yes-votes": "Yes Votes",
|
||||
"your-votes": "Your Votes:"
|
||||
"your-votes": "Your Votes:",
|
||||
"create-switch-oracle": "Create switchboard oracle for",
|
||||
"estimated-oracle-cost": "Estimated cost with funding oracle for ~6 months 2.8 SOL",
|
||||
"create-oracle": "Create oracle",
|
||||
"tier": "Tier"
|
||||
}
|
|
@ -11,5 +11,6 @@
|
|||
"tooltip-init-asset-liability-weight": "The contribution a perp position has to your initial account health. Asset weight is applied to long positions and liability weight is applied to shorts. Initial health controls your ability to withdraw and open new positions and is shown as an account's free collateral.",
|
||||
"tooltip-maint-asset-liability-weight": "The contribution a perp position has to your maintenance account health. Asset weight is applied to long positions and liability weight is applied to shorts. Maintenance health is what's displayed on your account page. If this value reaches zero your account will be liquidated.",
|
||||
"tooltip-pnl-liquidation-fee": "The liqee pays this liquidation fee when a liquidator has to take over positive unsettled perp pnl.",
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period."
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period.",
|
||||
"volume": "Volume"
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"confirm-swap": "Confirm Swap",
|
||||
"connect-swap": "Connect to view your swap history",
|
||||
"disabled": "Disabled",
|
||||
"enabled": "Enabled",
|
||||
"est-liq-price": "Est. Liq Price",
|
||||
"fees-paid-to": "Fees Paid to {{route}}",
|
||||
"health-impact": "Health Impact",
|
||||
|
@ -9,6 +11,7 @@
|
|||
"input-reduce-only-warning": "{{symbol}} is in reduce only mode. You can swap your balance to another token",
|
||||
"insufficient-balance": "Insufficient {{symbol}} Balance",
|
||||
"insufficient-collateral": "Insufficient Collateral",
|
||||
"margin": "Margin",
|
||||
"margin-swap": "Margin Swap",
|
||||
"max-slippage": "Max Slippage",
|
||||
"maximum-cost": "Maximum Cost",
|
||||
|
@ -34,6 +37,7 @@
|
|||
"swap-route": "Swap Route",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-margin": "When margin is enabled you can add leverage to your swaps. Borrows are opened automatically if a swap exceeds your balance.",
|
||||
"tooltip-max-slippage": "If price slips beyond your maximum slippage your swap will not be executed",
|
||||
"use-margin": "Allow Margin",
|
||||
"no-swap-found": "No swap found"
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
"before-listing-4": "New markets and tokens are approved by DAO vote. This takes 3 days",
|
||||
"cancel": "Cancel",
|
||||
"cant-find-token-for-mint": "Can't find token for given mint",
|
||||
"cant-list-oracle-not-found": "Oracle not found. Create a Pyth/Switchboard oracle before continuing.",
|
||||
"cant-list-oracle-not-found-pyth": "Oracle not found. Create a Pyth oracle before continuing.",
|
||||
"cant-list-oracle-not-found-switch": "Oracle not found. Create a Switchboard oracle before continuing.",
|
||||
"cant-list-no-openbook-market": "Openbook market not found. Create an Openbook market before continuing.",
|
||||
"connect-to-list": "Connect to list a spot market or token",
|
||||
"connect-wallet": "Connect Wallet",
|
||||
|
@ -29,7 +30,7 @@
|
|||
"invalid-num": "Invalid Number",
|
||||
"invalid-pk": "Invalid Public Key",
|
||||
"liquidity-check-error": "Error during liquidity check",
|
||||
"liquidity-warning": "Low liquidity. Price impact of {{priceImpactPct}}% on a 10k USDC swap.",
|
||||
"liquidity-warning": "Low liquidity. Price impact of {{priceImpactPct}}% on a 20k USDC swap.",
|
||||
"list-spot-market": "List Spot Market",
|
||||
"list-spot-market-desc": "Create a market pair from tokens listed on Mango",
|
||||
"list-token": "List Token",
|
||||
|
@ -95,5 +96,9 @@
|
|||
"what-happens-next-3": "{{token}} will be listed on Mango automatically on execution.",
|
||||
"yes": "Yes",
|
||||
"yes-votes": "Yes Votes",
|
||||
"your-votes": "Your Votes:"
|
||||
"your-votes": "Your Votes:",
|
||||
"create-switch-oracle": "Create switchboard oracle for",
|
||||
"estimated-oracle-cost": "Estimated cost with funding oracle for ~6 months 2.8 SOL",
|
||||
"create-oracle": "Create oracle",
|
||||
"tier": "Tier"
|
||||
}
|
|
@ -11,5 +11,6 @@
|
|||
"tooltip-init-asset-liability-weight": "The contribution a perp position has to your initial account health. Asset weight is applied to long positions and liability weight is applied to shorts. Initial health controls your ability to withdraw and open new positions and is shown as an account's free collateral.",
|
||||
"tooltip-maint-asset-liability-weight": "The contribution a perp position has to your maintenance account health. Asset weight is applied to long positions and liability weight is applied to shorts. Maintenance health is what's displayed on your account page. If this value reaches zero your account will be liquidated.",
|
||||
"tooltip-pnl-liquidation-fee": "The liqee pays this liquidation fee when a liquidator has to take over positive unsettled perp pnl.",
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period."
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period.",
|
||||
"volume": "Volume"
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"confirm-swap": "Confirm Swap",
|
||||
"connect-swap": "Connect to view your swap history",
|
||||
"disabled": "Disabled",
|
||||
"enabled": "Enabled",
|
||||
"est-liq-price": "Est. Liq Price",
|
||||
"fees-paid-to": "Fees Paid to {{route}}",
|
||||
"health-impact": "Health Impact",
|
||||
|
@ -9,6 +11,7 @@
|
|||
"input-reduce-only-warning": "{{symbol}} is in reduce only mode. You can swap your balance to another token",
|
||||
"insufficient-balance": "Insufficient {{symbol}} Balance",
|
||||
"insufficient-collateral": "Insufficient Collateral",
|
||||
"margin": "Margin",
|
||||
"margin-swap": "Margin Swap",
|
||||
"max-slippage": "Max Slippage",
|
||||
"maximum-cost": "Maximum Cost",
|
||||
|
@ -34,6 +37,7 @@
|
|||
"swap-route": "Swap Route",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-margin": "When margin is enabled you can add leverage to your swaps. Borrows are opened automatically if a swap exceeds your balance.",
|
||||
"tooltip-max-slippage": "If price slips beyond your maximum slippage your swap will not be executed",
|
||||
"use-margin": "Allow Margin",
|
||||
"no-swap-found": "No swap found"
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
"before-listing-4": "New markets and tokens are approved by DAO vote. This takes 3 days",
|
||||
"cancel": "Cancel",
|
||||
"cant-find-token-for-mint": "Can't find token for given mint",
|
||||
"cant-list-oracle-not-found": "Oracle not found. Create a Pyth/Switchboard oracle before continuing.",
|
||||
"cant-list-oracle-not-found-pyth": "Oracle not found. Create a Pyth oracle before continuing.",
|
||||
"cant-list-oracle-not-found-switch": "Oracle not found. Create a Switchboard oracle before continuing.",
|
||||
"cant-list-no-openbook-market": "Openbook market not found. Create an Openbook market before continuing.",
|
||||
"connect-to-list": "Connect to list a spot market or token",
|
||||
"connect-wallet": "Connect Wallet",
|
||||
|
@ -29,7 +30,7 @@
|
|||
"invalid-num": "Invalid Number",
|
||||
"invalid-pk": "Invalid Public Key",
|
||||
"liquidity-check-error": "Error during liquidity check",
|
||||
"liquidity-warning": "Low liquidity. Price impact of {{priceImpactPct}}% on a 10k USDC swap.",
|
||||
"liquidity-warning": "Low liquidity. Price impact of {{priceImpactPct}}% on a 20k USDC swap.",
|
||||
"list-spot-market": "List Spot Market",
|
||||
"list-spot-market-desc": "Create a market pair from tokens listed on Mango",
|
||||
"list-token": "List Token",
|
||||
|
@ -95,5 +96,9 @@
|
|||
"what-happens-next-3": "{{token}} will be listed on Mango automatically on execution.",
|
||||
"yes": "Yes",
|
||||
"yes-votes": "Yes Votes",
|
||||
"your-votes": "Your Votes:"
|
||||
"your-votes": "Your Votes:",
|
||||
"create-switch-oracle": "Create switchboard oracle for",
|
||||
"estimated-oracle-cost": "Estimated cost with funding oracle for ~6 months 2.8 SOL",
|
||||
"create-oracle": "Create oracle",
|
||||
"tier": "Tier"
|
||||
}
|
|
@ -11,5 +11,6 @@
|
|||
"tooltip-init-asset-liability-weight": "The contribution a perp position has to your initial account health. Asset weight is applied to long positions and liability weight is applied to shorts. Initial health controls your ability to withdraw and open new positions and is shown as an account's free collateral.",
|
||||
"tooltip-maint-asset-liability-weight": "The contribution a perp position has to your maintenance account health. Asset weight is applied to long positions and liability weight is applied to shorts. Maintenance health is what's displayed on your account page. If this value reaches zero your account will be liquidated.",
|
||||
"tooltip-pnl-liquidation-fee": "The liqee pays this liquidation fee when a liquidator has to take over positive unsettled perp pnl.",
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period."
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period.",
|
||||
"volume": "Volume"
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"confirm-swap": "Confirm Swap",
|
||||
"connect-swap": "Connect to view your swap history",
|
||||
"disabled": "Disabled",
|
||||
"enabled": "Enabled",
|
||||
"est-liq-price": "Est. Liq Price",
|
||||
"fees-paid-to": "Fees Paid to {{route}}",
|
||||
"health-impact": "Health Impact",
|
||||
|
@ -9,6 +11,7 @@
|
|||
"input-reduce-only-warning": "{{symbol}} is in reduce only mode. You can swap your balance to another token",
|
||||
"insufficient-balance": "Insufficient {{symbol}} Balance",
|
||||
"insufficient-collateral": "Insufficient Collateral",
|
||||
"margin": "Margin",
|
||||
"margin-swap": "Margin Swap",
|
||||
"max-slippage": "Max Slippage",
|
||||
"maximum-cost": "Maximum Cost",
|
||||
|
@ -34,6 +37,7 @@
|
|||
"swap-route": "Swap Route",
|
||||
"tooltip-borrow-balance": "You'll use your {{balance}} {{token}} balance and borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-borrow-no-balance": "You'll borrow {{borrowAmount}} {{token}} to execute this swap. The current {{token}} variable borrow rate is {{rate}}%",
|
||||
"tooltip-margin": "When margin is enabled you can add leverage to your swaps. Borrows are opened automatically if a swap exceeds your balance.",
|
||||
"tooltip-max-slippage": "If price slips beyond your maximum slippage your swap will not be executed",
|
||||
"use-margin": "Allow Margin",
|
||||
"no-swap-found": "No swap found"
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
"before-listing-4": "New markets and tokens are approved by DAO vote. This takes 3 days",
|
||||
"cancel": "Cancel",
|
||||
"cant-find-token-for-mint": "Can't find token for given mint",
|
||||
"cant-list-oracle-not-found": "Oracle not found. Create a Pyth/Switchboard oracle before continuing.",
|
||||
"cant-list-oracle-not-found-pyth": "Oracle not found. Create a Pyth oracle before continuing.",
|
||||
"cant-list-oracle-not-found-switch": "Oracle not found. Create a Switchboard oracle before continuing.",
|
||||
"cant-list-no-openbook-market": "Openbook market not found. Create an Openbook market before continuing.",
|
||||
"connect-to-list": "Connect to list a spot market or token",
|
||||
"connect-wallet": "Connect Wallet",
|
||||
|
@ -29,7 +30,7 @@
|
|||
"invalid-num": "Invalid Number",
|
||||
"invalid-pk": "Invalid Public Key",
|
||||
"liquidity-check-error": "Error during liquidity check",
|
||||
"liquidity-warning": "Low liquidity. Price impact of {{priceImpactPct}}% on a 10k USDC swap.",
|
||||
"liquidity-warning": "Low liquidity. Price impact of {{priceImpactPct}}% on a 20k USDC swap.",
|
||||
"list-spot-market": "List Spot Market",
|
||||
"list-spot-market-desc": "Create a market pair from tokens listed on Mango",
|
||||
"list-token": "List Token",
|
||||
|
@ -95,5 +96,9 @@
|
|||
"what-happens-next-3": "{{token}} will be listed on Mango automatically on execution.",
|
||||
"yes": "Yes",
|
||||
"yes-votes": "Yes Votes",
|
||||
"your-votes": "Your Votes:"
|
||||
"your-votes": "Your Votes:",
|
||||
"create-switch-oracle": "Create switchboard oracle for",
|
||||
"estimated-oracle-cost": "Estimated cost with funding oracle for ~6 months 2.8 SOL",
|
||||
"create-oracle": "Create oracle",
|
||||
"tier": "Tier"
|
||||
}
|
|
@ -11,5 +11,6 @@
|
|||
"tooltip-init-asset-liability-weight": "The contribution a perp position has to your initial account health. Asset weight is applied to long positions and liability weight is applied to shorts. Initial health controls your ability to withdraw and open new positions and is shown as an account's free collateral.",
|
||||
"tooltip-maint-asset-liability-weight": "The contribution a perp position has to your maintenance account health. Asset weight is applied to long positions and liability weight is applied to shorts. Maintenance health is what's displayed on your account page. If this value reaches zero your account will be liquidated.",
|
||||
"tooltip-pnl-liquidation-fee": "The liqee pays this liquidation fee when a liquidator has to take over positive unsettled perp pnl.",
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period."
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period.",
|
||||
"volume": "Volume"
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"confirm-swap": "确定换币",
|
||||
"connect-swap": "连接来查换币纪录",
|
||||
"disabled": "Disabled",
|
||||
"enabled": "Enabled",
|
||||
"est-liq-price": "预计清算价格",
|
||||
"fees-paid-to": "缴给{{route}}的费用",
|
||||
"health-impact": "健康影响",
|
||||
|
@ -9,6 +11,7 @@
|
|||
"input-reduce-only-warning": "{{symbol}}处于仅减少模式。您可以将余额换成另一个币种",
|
||||
"insufficient-balance": "{{symbol}}余额不够",
|
||||
"insufficient-collateral": "质押品不够",
|
||||
"margin": "Margin",
|
||||
"margin-swap": "杠杆换币",
|
||||
"max-slippage": "最多下滑",
|
||||
"maximum-cost": "最大成本",
|
||||
|
@ -35,6 +38,7 @@
|
|||
"swap-route": "换币路线",
|
||||
"tooltip-borrow-balance": "您将使用您的 {{balance}} {{token}} 余额并借入 {{borrowAmount}} {{token}} 来执行此交换。当前的 {{token}} 可变借贷利率为 {{rate}}%",
|
||||
"tooltip-borrow-no-balance": "您将借入 {{borrowAmount}} {{token}} 来执行此交换。当前的 {{token}} 可变借贷利率为 {{rate}}%",
|
||||
"tooltip-margin": "When margin is enabled you can add leverage to your swaps. Borrows are opened automatically if a swap exceeds your balance.",
|
||||
"tooltip-max-slippage": "如果价格下滑超过您的最大滑点,换币交易将不会被执行",
|
||||
"use-margin": "允许杠杆"
|
||||
}
|
|
@ -11,7 +11,8 @@
|
|||
"before-listing-4": "New markets and tokens are approved by DAO vote. This takes 3 days",
|
||||
"cancel": "Cancel",
|
||||
"cant-find-token-for-mint": "Can't find token for given mint",
|
||||
"cant-list-oracle-not-found": "Oracle not found. Create a Pyth/Switchboard oracle before continuing.",
|
||||
"cant-list-oracle-not-found-pyth": "Oracle not found. Create a Pyth oracle before continuing.",
|
||||
"cant-list-oracle-not-found-switch": "Oracle not found. Create a Switchboard oracle before continuing.",
|
||||
"cant-list-no-openbook-market": "Openbook market not found. Create an Openbook market before continuing.",
|
||||
"connect-to-list": "Connect to list a spot market or token",
|
||||
"connect-wallet": "Connect Wallet",
|
||||
|
@ -29,7 +30,7 @@
|
|||
"invalid-num": "Invalid Number",
|
||||
"invalid-pk": "Invalid Public Key",
|
||||
"liquidity-check-error": "Error during liquidity check",
|
||||
"liquidity-warning": "Low liquidity. Price impact of {{priceImpactPct}}% on a 10k USDC swap.",
|
||||
"liquidity-warning": "Low liquidity. Price impact of {{priceImpactPct}}% on a 20k USDC swap.",
|
||||
"list-spot-market": "List Spot Market",
|
||||
"list-spot-market-desc": "Create a market pair from tokens listed on Mango",
|
||||
"list-token": "List Token",
|
||||
|
@ -95,5 +96,9 @@
|
|||
"what-happens-next-3": "{{token}} will be listed on Mango automatically on execution.",
|
||||
"yes": "Yes",
|
||||
"yes-votes": "Yes Votes",
|
||||
"your-votes": "Your Votes:"
|
||||
"your-votes": "Your Votes:",
|
||||
"create-switch-oracle": "Create switchboard oracle for",
|
||||
"estimated-oracle-cost": "Estimated cost with funding oracle for ~6 months 2.8 SOL",
|
||||
"create-oracle": "Create oracle",
|
||||
"tier": "Tier"
|
||||
}
|
|
@ -11,5 +11,6 @@
|
|||
"tooltip-init-asset-liability-weight": "The contribution a perp position has to your initial account health. Asset weight is applied to long positions and liability weight is applied to shorts. Initial health controls your ability to withdraw and open new positions and is shown as an account's free collateral.",
|
||||
"tooltip-maint-asset-liability-weight": "The contribution a perp position has to your maintenance account health. Asset weight is applied to long positions and liability weight is applied to shorts. Maintenance health is what's displayed on your account page. If this value reaches zero your account will be liquidated.",
|
||||
"tooltip-pnl-liquidation-fee": "The liqee pays this liquidation fee when a liquidator has to take over positive unsettled perp pnl.",
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period."
|
||||
"tooltip-settle-pnl-factor": "As an exploit mitigation, settlement of unrealized pnl is limited to this multiple of perp notional value in each time period.",
|
||||
"volume": "Volume"
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"confirm-swap": "確定換幣",
|
||||
"connect-swap": "連接來查換幣紀錄",
|
||||
"disabled": "Disabled",
|
||||
"enabled": "Enabled",
|
||||
"est-liq-price": "預計清算價格",
|
||||
"fees-paid-to": "繳給{{route}}的費用",
|
||||
"health-impact": "健康影響",
|
||||
|
@ -9,6 +11,7 @@
|
|||
"input-reduce-only-warning": "{{symbol}}處於僅減少模式。您可以將餘額換成另一個幣種",
|
||||
"insufficient-balance": "{{symbol}}餘額不夠",
|
||||
"insufficient-collateral": "質押品不夠",
|
||||
"margin": "Margin",
|
||||
"margin-swap": "槓桿換幣",
|
||||
"max-slippage": "最多下滑",
|
||||
"maximum-cost": "最大成本",
|
||||
|
@ -35,6 +38,7 @@
|
|||
"swap-route": "換幣路線",
|
||||
"tooltip-borrow-balance": "您將使用您的 {{balance}} {{token}} 餘額並借入 {{borrowAmount}} {{token}} 來執行此交換。當前的 {{token}} 可變借貸利率為 {{rate}}%",
|
||||
"tooltip-borrow-no-balance": "您將借入 {{borrowAmount}} {{token}} 來執行此交換。當前的 {{token}} 可變借貸利率為 {{rate}}%",
|
||||
"tooltip-margin": "When margin is enabled you can add leverage to your swaps. Borrows are opened automatically if a swap exceeds your balance.",
|
||||
"tooltip-max-slippage": "如果價格下滑超過您的最大滑點,換幣交易將不會被執行",
|
||||
"use-margin": "允許槓桿"
|
||||
}
|
|
@ -286,6 +286,8 @@ export interface NFT {
|
|||
}
|
||||
|
||||
export interface PerpStatsItem {
|
||||
cumulative_base_volume: number
|
||||
cumulative_quote_volume: number
|
||||
date_hour: string
|
||||
fees_accrued: number
|
||||
fees_settled: number
|
||||
|
@ -300,6 +302,8 @@ export interface PerpStatsItem {
|
|||
total_fees: number
|
||||
}
|
||||
|
||||
export type GroupedDataItem = PerpStatsItem & Record<string, any>
|
||||
|
||||
export type ActivityFeed = {
|
||||
activity_type: string
|
||||
block_datetime: string
|
||||
|
@ -386,3 +390,14 @@ export function isMangoError(error: unknown): error is MangoError {
|
|||
typeof (error as Record<string, unknown>).message === 'string'
|
||||
)
|
||||
}
|
||||
|
||||
export type TickerData = {
|
||||
base_currency: string
|
||||
base_volume: string
|
||||
high: string
|
||||
last_price: string
|
||||
low: string
|
||||
target_currency: string
|
||||
target_volume: string
|
||||
ticker_id: string
|
||||
}
|
||||
|
|
|
@ -76,6 +76,8 @@ export const MANGO_ROUTER_API_URL = 'https://api.mngo.cloud/router/v1'
|
|||
|
||||
export const MANGO_DATA_API_URL = 'https://api.mngo.cloud/data/v4'
|
||||
|
||||
export const MANGO_DATA_OPENBOOK_URL = 'https://api.mngo.cloud/openbook/v1'
|
||||
|
||||
export const DEFAULT_MARKET_NAME = 'SOL/USDC'
|
||||
|
||||
export const MIN_SOL_BALANCE = 0.001
|
||||
|
@ -92,6 +94,11 @@ export const JUPITER_API_MAINNET = 'https://token.jup.ag/strict'
|
|||
|
||||
export const JUPITER_API_DEVNET = 'https://api.jup.ag/api/tokens/devnet'
|
||||
|
||||
export const JUPITER_PRICE_API_MAINNET = 'https://price.jup.ag/v4/'
|
||||
|
||||
export const NOTIFICATION_API = 'https://notifications-api.herokuapp.com/'
|
||||
export const NOTIFICATION_API_WEBSOCKET =
|
||||
'wss://notifications-api.herokuapp.com/ws'
|
||||
|
||||
export const SWITCHBOARD_PROGRAM_ID =
|
||||
'SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f'
|
||||
|
|
|
@ -2,7 +2,7 @@ import { AnchorProvider, Program } from '@project-serum/anchor'
|
|||
import { PythHttpClient } from '@pythnetwork/client'
|
||||
import { notify } from 'utils/notifications'
|
||||
import { MAINNET_PYTH_PROGRAM } from './constants'
|
||||
import { OPENBOOK_PROGRAM_ID } from '@blockworks-foundation/mango-v4'
|
||||
import { OPENBOOK_PROGRAM_ID, toNative } from '@blockworks-foundation/mango-v4'
|
||||
import { Market } from '@project-serum/serum'
|
||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js'
|
||||
import EmptyWallet from 'utils/wallet'
|
||||
|
@ -11,10 +11,12 @@ export const getOracle = async ({
|
|||
baseSymbol,
|
||||
quoteSymbol,
|
||||
connection,
|
||||
pythOnly = false,
|
||||
}: {
|
||||
baseSymbol: string
|
||||
quoteSymbol: string
|
||||
connection: Connection
|
||||
pythOnly?: boolean
|
||||
}) => {
|
||||
try {
|
||||
let oraclePk = ''
|
||||
|
@ -25,7 +27,7 @@ export const getOracle = async ({
|
|||
})
|
||||
if (pythOracle) {
|
||||
oraclePk = pythOracle
|
||||
} else {
|
||||
} else if (!pythOnly) {
|
||||
const switchBoardOracle = await getSwitchBoardOracle({
|
||||
baseSymbol,
|
||||
quoteSymbol,
|
||||
|
@ -282,3 +284,92 @@ export const getQuoteSymbol = (quoteTokenSymbol: string) => {
|
|||
}
|
||||
return quoteTokenSymbol
|
||||
}
|
||||
|
||||
const listingBase = {
|
||||
maxStalenessSlots: 120 as number | null,
|
||||
oracleConfFilter: 0.1,
|
||||
adjustmentFactor: 0.004,
|
||||
util0: 0.5,
|
||||
rate0: 0.052,
|
||||
util1: 0.8,
|
||||
rate1: 0.1446,
|
||||
maxRate: 1.4456,
|
||||
loanFeeRate: 0.005,
|
||||
loanOriginationFeeRate: 0.001,
|
||||
maintAssetWeight: 0.9,
|
||||
initAssetWeight: 0.8,
|
||||
maintLiabWeight: 1.1,
|
||||
initLiabWeight: 1.2,
|
||||
liquidationFee: 0.05,
|
||||
minVaultToDepositsRatio: 0.2,
|
||||
netBorrowLimitWindowSizeTs: 24 * 60 * 60,
|
||||
netBorrowLimitPerWindowQuote: toNative(50000, 6).toNumber(),
|
||||
insuranceFound: true,
|
||||
borrowWeightScale: toNative(250000, 6).toNumber(),
|
||||
depositWeightScale: toNative(250000, 6).toNumber(),
|
||||
}
|
||||
|
||||
export const LISTING_PRESETS: {
|
||||
[key: string]: typeof listingBase & { name: string }
|
||||
} = {
|
||||
//Price impact $100,000 < 1%
|
||||
PREMIUM: {
|
||||
...listingBase,
|
||||
name: 'Premium',
|
||||
},
|
||||
//Price impact $20,000 < 1%
|
||||
MID: {
|
||||
...listingBase,
|
||||
maintAssetWeight: 0.75,
|
||||
initAssetWeight: 0.5,
|
||||
maintLiabWeight: 1.2,
|
||||
initLiabWeight: 1.4,
|
||||
liquidationFee: 0.1,
|
||||
netBorrowLimitPerWindowQuote: toNative(20000, 6).toNumber(),
|
||||
name: 'Mid',
|
||||
borrowWeightScale: toNative(50000, 6).toNumber(),
|
||||
depositWeightScale: toNative(50000, 6).toNumber(),
|
||||
insuranceFound: false,
|
||||
},
|
||||
//Price impact $5,000 < 1%
|
||||
MEME: {
|
||||
...listingBase,
|
||||
maxStalenessSlots: 800,
|
||||
loanOriginationFeeRate: 0.002,
|
||||
maintAssetWeight: 0,
|
||||
initAssetWeight: 0,
|
||||
maintLiabWeight: 1.25,
|
||||
initLiabWeight: 1.5,
|
||||
liquidationFee: 0.125,
|
||||
netBorrowLimitPerWindowQuote: toNative(5000, 6).toNumber(),
|
||||
borrowWeightScale: toNative(20000, 6).toNumber(),
|
||||
depositWeightScale: toNative(20000, 6).toNumber(),
|
||||
insuranceFound: false,
|
||||
name: 'Meme',
|
||||
},
|
||||
//Price impact $1,000 < 1%
|
||||
SHIT: {
|
||||
...listingBase,
|
||||
maxStalenessSlots: 800,
|
||||
loanOriginationFeeRate: 0.002,
|
||||
maintAssetWeight: 0,
|
||||
initAssetWeight: 0,
|
||||
maintLiabWeight: 1.4,
|
||||
initLiabWeight: 1.8,
|
||||
liquidationFee: 0.2,
|
||||
netBorrowLimitPerWindowQuote: toNative(1000, 6).toNumber(),
|
||||
borrowWeightScale: toNative(5000, 6).toNumber(),
|
||||
depositWeightScale: toNative(5000, 6).toNumber(),
|
||||
insuranceFound: false,
|
||||
name: 'Shit',
|
||||
},
|
||||
}
|
||||
|
||||
export const coinTiersToNames: {
|
||||
[key: string]: string
|
||||
} = {
|
||||
PREMIUM: 'Blue Chip',
|
||||
MID: 'Mid-wit',
|
||||
MEME: 'Meme',
|
||||
SHIT: 'Shit Coin',
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ export const isInCoolOffTime = (
|
|||
governance: Governance | undefined
|
||||
) => {
|
||||
const mainVotingEndedAt = proposal?.signingOffAt
|
||||
?.addn(governance?.config.maxVotingTime || 0)
|
||||
?.addn(governance?.config.baseVotingTime || 0)
|
||||
.toNumber()
|
||||
|
||||
const votingCoolOffTime = governance?.config.votingCoolOffTime || 0
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
import { Connection, PublicKey } from '@solana/web3.js'
|
||||
import { TokenProgramAccount } from './accounts/vsrAccounts'
|
||||
import { MintLayout, RawMint } from '@solana/spl-token'
|
||||
import BN from 'bn.js'
|
||||
import { BN } from '@project-serum/anchor'
|
||||
|
||||
export async function fetchRealm({
|
||||
connection,
|
||||
|
|
202
yarn.lock
202
yarn.lock
|
@ -21,10 +21,10 @@
|
|||
dependencies:
|
||||
ws "^8.13.0"
|
||||
|
||||
"@blockworks-foundation/mango-v4@^0.15.12":
|
||||
version "0.15.12"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4/-/mango-v4-0.15.12.tgz#c870dbf31e188b4884fd716468db546861f06bd9"
|
||||
integrity sha512-k0Bn9VmdwFyAF+G5dYS2aSZJ4MWH7RtTHz9ZZNgUKKTaLR+N2WhA7oH6d4O1k4KCIIVudvsN6burMgMpzm4zXg==
|
||||
"@blockworks-foundation/mango-v4@^0.15.13":
|
||||
version "0.15.13"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4/-/mango-v4-0.15.13.tgz#adbfb63a69fc94ddaed9a7d65733e881335d092d"
|
||||
integrity sha512-akqs0LYK7sjD5mqxwGWBaN7AaPIgDhdUswRWjFRCRXJy1B9la2pmJv9iWjXFU9DDWPORrpu5psFZ0NZ8hYKpnA==
|
||||
dependencies:
|
||||
"@coral-xyz/anchor" "^0.26.0"
|
||||
"@project-serum/serum" "0.13.65"
|
||||
|
@ -105,6 +105,27 @@
|
|||
superstruct "^0.15.4"
|
||||
toml "^3.0.0"
|
||||
|
||||
"@coral-xyz/anchor@^0.27.0":
|
||||
version "0.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.27.0.tgz#621e5ef123d05811b97e49973b4ed7ede27c705c"
|
||||
integrity sha512-+P/vPdORawvg3A9Wj02iquxb4T0C5m4P6aZBVYysKl4Amk+r6aMPZkUhilBkD6E4Nuxnoajv3CFykUfkGE0n5g==
|
||||
dependencies:
|
||||
"@coral-xyz/borsh" "^0.27.0"
|
||||
"@solana/web3.js" "^1.68.0"
|
||||
base64-js "^1.5.1"
|
||||
bn.js "^5.1.2"
|
||||
bs58 "^4.0.1"
|
||||
buffer-layout "^1.2.2"
|
||||
camelcase "^6.3.0"
|
||||
cross-fetch "^3.1.5"
|
||||
crypto-hash "^1.3.0"
|
||||
eventemitter3 "^4.0.7"
|
||||
js-sha256 "^0.9.0"
|
||||
pako "^2.0.3"
|
||||
snake-case "^3.0.4"
|
||||
superstruct "^0.15.4"
|
||||
toml "^3.0.0"
|
||||
|
||||
"@coral-xyz/borsh@^0.26.0":
|
||||
version "0.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.26.0.tgz#d054f64536d824634969e74138f9f7c52bbbc0d5"
|
||||
|
@ -113,6 +134,14 @@
|
|||
bn.js "^5.1.2"
|
||||
buffer-layout "^1.2.0"
|
||||
|
||||
"@coral-xyz/borsh@^0.27.0":
|
||||
version "0.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.27.0.tgz#700c647ea5262b1488957ac7fb4e8acf72c72b63"
|
||||
integrity sha512-tJKzhLukghTWPLy+n8K8iJKgBq1yLT/AxaNd10yJrX8mI56ao5+OFAKAqW/h0i79KCvb4BK0VGO5ECmmolFz9A==
|
||||
dependencies:
|
||||
bn.js "^5.1.2"
|
||||
buffer-layout "^1.2.0"
|
||||
|
||||
"@cspotcode/source-map-support@^0.8.0":
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
|
||||
|
@ -938,21 +967,23 @@
|
|||
jsbi "^3.1.5"
|
||||
sha.js "^2.4.11"
|
||||
|
||||
"@noble/ed25519@^1.6.1", "@noble/ed25519@^1.7.0", "@noble/ed25519@^1.7.1":
|
||||
"@noble/curves@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932"
|
||||
integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==
|
||||
dependencies:
|
||||
"@noble/hashes" "1.3.0"
|
||||
|
||||
"@noble/ed25519@^1.6.1", "@noble/ed25519@^1.7.1":
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.3.tgz#57e1677bf6885354b466c38e2b620c62f45a7123"
|
||||
integrity sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==
|
||||
|
||||
"@noble/hashes@^1.1.2", "@noble/hashes@^1.1.3":
|
||||
"@noble/hashes@1.3.0", "@noble/hashes@^1.1.3", "@noble/hashes@^1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1"
|
||||
integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==
|
||||
|
||||
"@noble/secp256k1@^1.6.3":
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c"
|
||||
integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||
|
@ -1117,6 +1148,59 @@
|
|||
bs58 "^4.0.1"
|
||||
eventemitter3 "^4.0.7"
|
||||
|
||||
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
|
||||
integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==
|
||||
|
||||
"@protobufjs/base64@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
|
||||
integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
|
||||
|
||||
"@protobufjs/codegen@^2.0.4":
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb"
|
||||
integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==
|
||||
|
||||
"@protobufjs/eventemitter@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70"
|
||||
integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==
|
||||
|
||||
"@protobufjs/fetch@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45"
|
||||
integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==
|
||||
dependencies:
|
||||
"@protobufjs/aspromise" "^1.1.1"
|
||||
"@protobufjs/inquire" "^1.1.0"
|
||||
|
||||
"@protobufjs/float@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1"
|
||||
integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==
|
||||
|
||||
"@protobufjs/inquire@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
|
||||
integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==
|
||||
|
||||
"@protobufjs/path@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d"
|
||||
integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==
|
||||
|
||||
"@protobufjs/pool@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54"
|
||||
integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==
|
||||
|
||||
"@protobufjs/utf8@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
|
||||
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
|
||||
|
||||
"@pythnetwork/client@2.15.0":
|
||||
version "2.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@pythnetwork/client/-/client-2.15.0.tgz#e84a1e52e7b142acc7476940f94f6dffc2e94bc2"
|
||||
|
@ -1203,10 +1287,10 @@
|
|||
dependencies:
|
||||
buffer "~6.0.3"
|
||||
|
||||
"@solana/spl-governance@0.3.25":
|
||||
version "0.3.25"
|
||||
resolved "https://registry.yarnpkg.com/@solana/spl-governance/-/spl-governance-0.3.25.tgz#57563003ebd4cf0bf778876035828196e34e29fd"
|
||||
integrity sha512-+qepFswOvG0mHLoLnONw+6bwSuEKr7UhJyeAuM/nodjkR8Z5IfH4HQuOzl+TlPTjMStLFJ7Ja6AzTu+FkPGIUQ==
|
||||
"@solana/spl-governance@0.3.27":
|
||||
version "0.3.27"
|
||||
resolved "https://registry.yarnpkg.com/@solana/spl-governance/-/spl-governance-0.3.27.tgz#54ab8310a142b3d581d8abc3df37e3511f02619c"
|
||||
integrity sha512-5qAgaOWXNNbfOAiBeX5GqX+ZahMGzm1s/kLADAVRWtPYI/vpwc8ENI5jpPOEWG5bgTnnBP+wLT9mgxgwoS4e+A==
|
||||
dependencies:
|
||||
"@solana/web3.js" "^1.22.0"
|
||||
axios "^1.1.3"
|
||||
|
@ -1696,15 +1780,14 @@
|
|||
"@wallet-standard/app" "^1.0.1"
|
||||
"@wallet-standard/base" "^1.0.1"
|
||||
|
||||
"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.22.0", "@solana/web3.js@^1.31.0", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.44.3", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.63.1", "@solana/web3.js@^1.66.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.73.2":
|
||||
version "1.75.0"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.75.0.tgz#824c6f78865007bca758ca18f268d6f7363b42e5"
|
||||
integrity sha512-rHQgdo1EWfb+nPUpHe4O7i8qJPELHKNR5PAZRK+a7XxiykqOfbaAlPt5boDWAGPnYbSv0ziWZv5mq9DlFaQCxg==
|
||||
"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.22.0", "@solana/web3.js@^1.31.0", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.44.3", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.63.1", "@solana/web3.js@^1.66.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.73.0", "@solana/web3.js@^1.73.2":
|
||||
version "1.77.2"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.77.2.tgz#4b4d71f07efb9aca1f7ab3ae8746c2e79389fe39"
|
||||
integrity sha512-pKu9S21NGAi6Nsayz2KEdhqOlPUJIr3L911bgQvPg2Dbk/U4gJsk41XGdxyfsfnwKPEI/KbitcByterst4VQ3g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@noble/ed25519" "^1.7.0"
|
||||
"@noble/hashes" "^1.1.2"
|
||||
"@noble/secp256k1" "^1.6.3"
|
||||
"@noble/curves" "^1.0.0"
|
||||
"@noble/hashes" "^1.3.0"
|
||||
"@solana/buffer-layout" "^4.0.0"
|
||||
agentkeepalive "^4.2.1"
|
||||
bigint-buffer "^1.1.5"
|
||||
|
@ -1897,6 +1980,21 @@
|
|||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@switchboard-xyz/common@^2.2.3":
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@switchboard-xyz/common/-/common-2.2.3.tgz#f4d39cea8cea9354ad369f749462fa37152c4ec9"
|
||||
integrity sha512-E4NQf9aXdOiul+sySAbFPAW9k0qz4wRTfqrU7cEa8nRIvUkg6VIZ+5JfajHv/VfK9UOD+6ZfMBxq2+dHkiz9zw==
|
||||
dependencies:
|
||||
"@solana/web3.js" "^1.66.2"
|
||||
"@types/big.js" "^6.1.6"
|
||||
"@types/bn.js" "^5.1.1"
|
||||
big.js "^6.2.1"
|
||||
bn.js "^5.2.1"
|
||||
bs58 "^5.0.0"
|
||||
decimal.js "^10.4.3"
|
||||
protobufjs "^7.2.3"
|
||||
yaml "^2.2.1"
|
||||
|
||||
"@switchboard-xyz/sbv2-lite@^0.1.6":
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@switchboard-xyz/sbv2-lite/-/sbv2-lite-0.1.6.tgz#dc3fbb5b3b028dbd3c688b991bcc48a670131ddb"
|
||||
|
@ -1905,6 +2003,19 @@
|
|||
"@project-serum/anchor" "^0.24.2"
|
||||
big.js "^6.1.1"
|
||||
|
||||
"@switchboard-xyz/solana.js@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@switchboard-xyz/solana.js/-/solana.js-2.2.0.tgz#5108cfbbf0ca6e48297ae8c6e8c11f39e04ac32a"
|
||||
integrity sha512-UzAyKDY1wq1UO50PsKc/6huF6xYX/3B5kA0lmEnZMb+5L6M3YtDckbxk6mD4kG7J0curvvX6Alu9cO6uGqnI3A==
|
||||
dependencies:
|
||||
"@coral-xyz/anchor" "^0.27.0"
|
||||
"@coral-xyz/borsh" "^0.27.0"
|
||||
"@solana/spl-token" "^0.3.6"
|
||||
"@solana/web3.js" "^1.73.0"
|
||||
"@switchboard-xyz/common" "^2.2.3"
|
||||
dotenv "^16.0.3"
|
||||
lodash "^4.17.21"
|
||||
|
||||
"@tanstack/query-core@4.10.1":
|
||||
version "4.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.10.1.tgz#a587e39c28a905168bdf8a3906e36715c30ec083"
|
||||
|
@ -2070,7 +2181,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
|
||||
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
|
||||
|
||||
"@types/big.js@6.1.6":
|
||||
"@types/big.js@6.1.6", "@types/big.js@^6.1.6":
|
||||
version "6.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/big.js/-/big.js-6.1.6.tgz#3d417e758483d55345a03a087f7e0c87137ca444"
|
||||
integrity sha512-0r9J+Zz9rYm2hOTwiMAVkm3XFQ4u5uTK37xrQMhc9bysn/sf/okzovWMYYIBMFTn/yrEZ11pusgLEaoarTlQbA==
|
||||
|
@ -2082,7 +2193,7 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/bn.js@^5.1.0":
|
||||
"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.1":
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682"
|
||||
integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==
|
||||
|
@ -2199,10 +2310,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.185.tgz#c9843f5a40703a8f5edfd53358a58ae729816908"
|
||||
integrity sha512-evMDG1bC4rgQg4ku9tKpuMh5iBNEwNa3tf9zRHdP1qlv+1WUg44xat4IxCE14gIpZRGUUWAx2VhItCZc25NfMA==
|
||||
|
||||
"@types/node@*":
|
||||
version "18.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.0.tgz#4668bc392bb6938637b47e98b1f2ed5426f33316"
|
||||
integrity sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==
|
||||
"@types/node@*", "@types/node@>=13.7.0":
|
||||
version "20.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.5.tgz#26d295f3570323b2837d322180dfbf1ba156fefb"
|
||||
integrity sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==
|
||||
|
||||
"@types/node@11.11.6":
|
||||
version "11.11.6"
|
||||
|
@ -3163,7 +3274,7 @@ bech32@1.1.4:
|
|||
resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9"
|
||||
integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==
|
||||
|
||||
big.js@6.2.1, big.js@^6.1.1:
|
||||
big.js@6.2.1, big.js@^6.1.1, big.js@^6.2.1:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.2.1.tgz#7205ce763efb17c2e41f26f121c420c6a7c2744f"
|
||||
integrity sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ==
|
||||
|
@ -4014,6 +4125,11 @@ decimal.js@10.4.0:
|
|||
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.0.tgz#97a7448873b01e92e5ff9117d89a7bca8e63e0fe"
|
||||
integrity sha512-Nv6ENEzyPQ6AItkGwLE2PGKinZZ9g59vSh2BeH6NqPu0OTKZ5ruJsVqh/orbAnqXc9pBbgXAIrc2EyaCj8NpGg==
|
||||
|
||||
decimal.js@^10.4.3:
|
||||
version "10.4.3"
|
||||
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
|
||||
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
|
||||
|
||||
decode-uri-component@^0.2.0:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9"
|
||||
|
@ -6029,6 +6145,11 @@ loglevel@^1.8.0, loglevel@^1.8.1:
|
|||
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4"
|
||||
integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==
|
||||
|
||||
long@^5.0.0:
|
||||
version "5.2.3"
|
||||
resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1"
|
||||
integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==
|
||||
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
|
@ -6888,6 +7009,24 @@ prop-types@15.x, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, pro
|
|||
object-assign "^4.1.1"
|
||||
react-is "^16.13.1"
|
||||
|
||||
protobufjs@^7.2.3:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.3.tgz#01af019e40d9c6133c49acbb3ff9e30f4f0f70b2"
|
||||
integrity sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg==
|
||||
dependencies:
|
||||
"@protobufjs/aspromise" "^1.1.2"
|
||||
"@protobufjs/base64" "^1.1.2"
|
||||
"@protobufjs/codegen" "^2.0.4"
|
||||
"@protobufjs/eventemitter" "^1.1.0"
|
||||
"@protobufjs/fetch" "^1.1.0"
|
||||
"@protobufjs/float" "^1.0.2"
|
||||
"@protobufjs/inquire" "^1.1.0"
|
||||
"@protobufjs/path" "^1.1.2"
|
||||
"@protobufjs/pool" "^1.1.0"
|
||||
"@protobufjs/utf8" "^1.1.0"
|
||||
"@types/node" ">=13.7.0"
|
||||
long "^5.0.0"
|
||||
|
||||
proxy-from-env@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
|
@ -8775,6 +8914,11 @@ yaml@^1.10.2:
|
|||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||
|
||||
yaml@^2.2.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b"
|
||||
integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==
|
||||
|
||||
yargs-parser@20.2.4:
|
||||
version "20.2.4"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
|
||||
|
|
Loading…
Reference in New Issue