merge main
This commit is contained in:
commit
8cf2da402c
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ export const calcPricePrecision = (num: number | string) => {
|
|||
export const formatPrice = (
|
||||
num: number,
|
||||
precision?: number,
|
||||
gr0 = true
|
||||
gr0 = true,
|
||||
): string => {
|
||||
if (!num) {
|
||||
return num.toString()
|
||||
|
@ -132,7 +132,7 @@ export const formatPrice = (
|
|||
const matchString: string = match[0].slice(1)
|
||||
formated = formated.replace(
|
||||
/\.0{4,15}/g,
|
||||
`.0${SUBSCRIPT_NUMBER_MAP[matchString.length]}`
|
||||
`.0${SUBSCRIPT_NUMBER_MAP[matchString.length]}`,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ export function subscribeOnStream(
|
|||
onRealtimeCallback: any,
|
||||
subscriberUID: any,
|
||||
onResetCacheNeededCallback: any,
|
||||
lastBar: any
|
||||
lastBar: any,
|
||||
) {
|
||||
subscriptionItem = {
|
||||
resolution,
|
||||
|
|
|
@ -3,7 +3,7 @@ type CoingeckoOhlcv = [
|
|||
open: number,
|
||||
high: number,
|
||||
low: number,
|
||||
close: number
|
||||
close: number,
|
||||
][]
|
||||
|
||||
export type ChartDataItem = {
|
||||
|
@ -16,16 +16,16 @@ export type ChartDataItem = {
|
|||
export const fetchChartData = async (
|
||||
baseTokenId: string | undefined,
|
||||
quoteTokenId: string | undefined,
|
||||
daysToShow: string
|
||||
daysToShow: string,
|
||||
): Promise<ChartDataItem[]> => {
|
||||
if (!baseTokenId || !quoteTokenId) return []
|
||||
try {
|
||||
const [inputResponse, outputResponse] = await Promise.all([
|
||||
fetch(
|
||||
`https://api.coingecko.com/api/v3/coins/${baseTokenId}/ohlc?vs_currency=usd&days=${daysToShow}`
|
||||
`https://api.coingecko.com/api/v3/coins/${baseTokenId}/ohlc?vs_currency=usd&days=${daysToShow}`,
|
||||
),
|
||||
fetch(
|
||||
`https://api.coingecko.com/api/v3/coins/${quoteTokenId}/ohlc?vs_currency=usd&days=${daysToShow}`
|
||||
`https://api.coingecko.com/api/v3/coins/${quoteTokenId}/ohlc?vs_currency=usd&days=${daysToShow}`,
|
||||
),
|
||||
])
|
||||
|
||||
|
@ -36,7 +36,7 @@ export const fetchChartData = async (
|
|||
const parsedData: ChartDataItem[] = []
|
||||
for (const inputTokenCandle of inputTokenData) {
|
||||
const outputTokenCandle = outputTokenData.find(
|
||||
(outputTokenCandle) => outputTokenCandle[0] === inputTokenCandle[0]
|
||||
(outputTokenCandle) => outputTokenCandle[0] === inputTokenCandle[0],
|
||||
)
|
||||
if (outputTokenCandle) {
|
||||
if (['usd-coin', 'tether'].includes(quoteTokenId)) {
|
||||
|
|
|
@ -72,11 +72,11 @@ const configurationData = {
|
|||
|
||||
const getTickerFromMktAddress = (
|
||||
group: Group,
|
||||
symbolAddress: string
|
||||
symbolAddress: string,
|
||||
): string | null => {
|
||||
try {
|
||||
const serumMkt = group.getSerum3MarketByExternalMarket(
|
||||
new PublicKey(symbolAddress)
|
||||
new PublicKey(symbolAddress),
|
||||
)
|
||||
|
||||
if (serumMkt) {
|
||||
|
@ -88,7 +88,7 @@ const getTickerFromMktAddress = (
|
|||
|
||||
const perpMarkets = Array.from(group.perpMarketsMapByMarketIndex.values())
|
||||
const perpMkt = perpMarkets.find(
|
||||
(perpMarket) => perpMarket.publicKey.toString() === symbolAddress
|
||||
(perpMarket) => perpMarket.publicKey.toString() === symbolAddress,
|
||||
)
|
||||
|
||||
if (perpMkt) {
|
||||
|
@ -102,12 +102,12 @@ let marketType: 'spot' | 'perp'
|
|||
|
||||
export const queryPerpBars = async (
|
||||
tokenAddress: string,
|
||||
resolution: typeof SUPPORTED_RESOLUTIONS[number],
|
||||
resolution: (typeof SUPPORTED_RESOLUTIONS)[number],
|
||||
periodParams: {
|
||||
firstDataRequest: boolean
|
||||
from: number
|
||||
to: number
|
||||
}
|
||||
},
|
||||
): Promise<Bar[]> => {
|
||||
const { from, to } = periodParams
|
||||
|
||||
|
@ -150,12 +150,12 @@ export const queryPerpBars = async (
|
|||
|
||||
export const queryBirdeyeBars = async (
|
||||
tokenAddress: string,
|
||||
resolution: typeof SUPPORTED_RESOLUTIONS[number],
|
||||
resolution: (typeof SUPPORTED_RESOLUTIONS)[number],
|
||||
periodParams: {
|
||||
firstDataRequest: boolean
|
||||
from: number
|
||||
to: number
|
||||
}
|
||||
},
|
||||
): Promise<Bar[]> => {
|
||||
const { from, to } = periodParams
|
||||
const urlParameters = {
|
||||
|
@ -167,7 +167,7 @@ export const queryBirdeyeBars = async (
|
|||
const query = Object.keys(urlParameters)
|
||||
.map(
|
||||
(name: string) =>
|
||||
`${name}=${encodeURIComponent((urlParameters as any)[name])}`
|
||||
`${name}=${encodeURIComponent((urlParameters as any)[name])}`,
|
||||
)
|
||||
.join('&')
|
||||
|
||||
|
@ -207,14 +207,14 @@ export default {
|
|||
_userInput: string,
|
||||
_exchange: string,
|
||||
_symbolType: string,
|
||||
_onResultReadyCallback: (items: SearchSymbolResultItem[]) => void
|
||||
_onResultReadyCallback: (items: SearchSymbolResultItem[]) => void,
|
||||
) => {
|
||||
return
|
||||
},
|
||||
|
||||
resolveSymbol: async (
|
||||
symbolAddress: string,
|
||||
onSymbolResolvedCallback: (symbolInfo: SymbolInfo) => void
|
||||
onSymbolResolvedCallback: (symbolInfo: SymbolInfo) => void,
|
||||
// _onResolveErrorCallback: any,
|
||||
// _extension: any
|
||||
) => {
|
||||
|
@ -283,9 +283,9 @@ export default {
|
|||
bars: Bar[],
|
||||
t: {
|
||||
noData: boolean
|
||||
}
|
||||
},
|
||||
) => void,
|
||||
onErrorCallback: (e: any) => void
|
||||
onErrorCallback: (e: any) => void,
|
||||
) => {
|
||||
try {
|
||||
const { firstDataRequest } = periodParams
|
||||
|
@ -298,14 +298,14 @@ export default {
|
|||
bars = await queryPerpBars(
|
||||
symbolInfo.address,
|
||||
resolution as any,
|
||||
periodParams
|
||||
periodParams,
|
||||
)
|
||||
} else {
|
||||
marketType = 'spot'
|
||||
bars = await queryBirdeyeBars(
|
||||
symbolInfo.address,
|
||||
resolution as any,
|
||||
periodParams
|
||||
periodParams,
|
||||
)
|
||||
}
|
||||
if (!bars || bars.length === 0) {
|
||||
|
@ -335,7 +335,7 @@ export default {
|
|||
resolution: string,
|
||||
onRealtimeCallback: (data: any) => void,
|
||||
subscriberUID: string,
|
||||
onResetCacheNeededCallback: () => void
|
||||
onResetCacheNeededCallback: () => void,
|
||||
) => {
|
||||
subscriptionIds.set(subscriberUID, symbolInfo.address)
|
||||
if (symbolInfo.description?.includes('PERP')) {
|
||||
|
@ -345,7 +345,7 @@ export default {
|
|||
onRealtimeCallback,
|
||||
subscriberUID,
|
||||
onResetCacheNeededCallback,
|
||||
lastBarsCache.get(symbolInfo.address)
|
||||
lastBarsCache.get(symbolInfo.address),
|
||||
)
|
||||
} else {
|
||||
subscribeOnSpotStream(
|
||||
|
@ -354,7 +354,7 @@ export default {
|
|||
onRealtimeCallback,
|
||||
subscriberUID,
|
||||
onResetCacheNeededCallback,
|
||||
lastBarsCache.get(symbolInfo.address)
|
||||
lastBarsCache.get(symbolInfo.address),
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
|
@ -66,12 +66,12 @@ const configurationData = {
|
|||
|
||||
export const queryBars = async (
|
||||
tokenAddress: string,
|
||||
resolution: typeof SUPPORTED_RESOLUTIONS[number],
|
||||
resolution: (typeof SUPPORTED_RESOLUTIONS)[number],
|
||||
periodParams: {
|
||||
firstDataRequest: boolean
|
||||
from: number
|
||||
to: number
|
||||
}
|
||||
},
|
||||
): Promise<Bar[]> => {
|
||||
const { from, to } = periodParams
|
||||
const urlParameters = {
|
||||
|
@ -121,14 +121,14 @@ export default {
|
|||
_userInput: string,
|
||||
_exchange: string,
|
||||
_symbolType: string,
|
||||
_onResultReadyCallback: (items: SearchSymbolResultItem[]) => void
|
||||
_onResultReadyCallback: (items: SearchSymbolResultItem[]) => void,
|
||||
) => {
|
||||
return
|
||||
},
|
||||
|
||||
resolveSymbol: async (
|
||||
symbolAddress: string,
|
||||
onSymbolResolvedCallback: (symbolInfo: SymbolInfo) => void
|
||||
onSymbolResolvedCallback: (symbolInfo: SymbolInfo) => void,
|
||||
// _onResolveErrorCallback: any,
|
||||
// _extension: any
|
||||
) => {
|
||||
|
@ -187,16 +187,16 @@ export default {
|
|||
bars: Bar[],
|
||||
t: {
|
||||
noData: boolean
|
||||
}
|
||||
},
|
||||
) => void,
|
||||
onErrorCallback: (e: any) => void
|
||||
onErrorCallback: (e: any) => void,
|
||||
) => {
|
||||
try {
|
||||
const { firstDataRequest } = periodParams
|
||||
const bars = await queryBars(
|
||||
symbolInfo.address,
|
||||
resolution as any,
|
||||
periodParams
|
||||
periodParams,
|
||||
)
|
||||
if (!bars || bars.length === 0) {
|
||||
// "noData" should be set if there is no data in the requested period.
|
||||
|
@ -226,7 +226,7 @@ export default {
|
|||
resolution: string,
|
||||
onRealtimeCallback: (data: any) => void,
|
||||
subscriberUID: string,
|
||||
onResetCacheNeededCallback: () => void
|
||||
onResetCacheNeededCallback: () => void,
|
||||
) => {
|
||||
subscribeOnStream(
|
||||
symbolInfo,
|
||||
|
@ -234,7 +234,7 @@ export default {
|
|||
onRealtimeCallback,
|
||||
subscriberUID,
|
||||
onResetCacheNeededCallback,
|
||||
lastBarsCache.get(symbolInfo.address)
|
||||
lastBarsCache.get(symbolInfo.address),
|
||||
)
|
||||
},
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ export function subscribeOnStream(
|
|||
onRealtimeCallback: any,
|
||||
subscriberUID: any,
|
||||
onResetCacheNeededCallback: any,
|
||||
lastBar: any
|
||||
lastBar: any,
|
||||
) {
|
||||
subscriptionItem = {
|
||||
resolution,
|
||||
|
|
|
@ -6,7 +6,7 @@ export type NotificationSettings = {
|
|||
|
||||
export const fetchNotificationSettings = async (
|
||||
wallet: string,
|
||||
token: string
|
||||
token: string,
|
||||
) => {
|
||||
const data = await fetch(
|
||||
`${NOTIFICATION_API}notifications/user/getSettings`,
|
||||
|
@ -15,7 +15,7 @@ export const fetchNotificationSettings = async (
|
|||
authorization: token,
|
||||
publickey: wallet,
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
const body = await data.json()
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ import useMangoAccount from 'hooks/useMangoAccount'
|
|||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import TokenVaultWarnings from '@components/shared/TokenVaultWarnings'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import { useEnhancedWallet } from './wallet/EnhancedWalletProvider'
|
||||
import FormatNumericValue from './shared/FormatNumericValue'
|
||||
import { floorToDecimal } from 'utils/numbers'
|
||||
import BankAmountWithValue from './shared/BankAmountWithValue'
|
||||
|
@ -58,13 +57,12 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
|
|||
const [inputAmount, setInputAmount] = useState('')
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const [selectedToken, setSelectedToken] = useState(
|
||||
token || INPUT_TOKEN_DEFAULT
|
||||
token || INPUT_TOKEN_DEFAULT,
|
||||
)
|
||||
const [showTokenList, setShowTokenList] = useState(false)
|
||||
const [sizePercentage, setSizePercentage] = useState('')
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const { connected, publicKey } = useWallet()
|
||||
const { handleConnect } = useEnhancedWallet()
|
||||
const { connected, publicKey, connect } = useWallet()
|
||||
const banks = useBanksWithBalances('maxBorrow')
|
||||
|
||||
const bank = useMemo(() => {
|
||||
|
@ -93,11 +91,11 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
|
|||
setSizePercentage(percentage)
|
||||
const amount = floorToDecimal(
|
||||
new Decimal(percentage).div(100).mul(tokenMax),
|
||||
bank.mintDecimals
|
||||
bank.mintDecimals,
|
||||
)
|
||||
setInputAmount(amount.toFixed())
|
||||
},
|
||||
[tokenMax, bank]
|
||||
[tokenMax, bank],
|
||||
)
|
||||
|
||||
const setMax = useCallback(() => {
|
||||
|
@ -125,7 +123,7 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
|
|||
mangoAccount,
|
||||
bank!.mint,
|
||||
Number(inputAmount),
|
||||
true
|
||||
true,
|
||||
)
|
||||
notify({
|
||||
title: 'Transaction confirmed',
|
||||
|
@ -329,7 +327,7 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
|
|||
) : null}
|
||||
</div>
|
||||
<Button
|
||||
onClick={connected ? handleBorrow : handleConnect}
|
||||
onClick={connected ? handleBorrow : connect}
|
||||
className="flex w-full items-center justify-center"
|
||||
disabled={connected && (!inputAmount || showInsufficientBalance)}
|
||||
size="large"
|
||||
|
|
|
@ -25,7 +25,6 @@ import MaxAmountButton from '@components/shared/MaxAmountButton'
|
|||
import Tooltip from '@components/shared/Tooltip'
|
||||
import HealthImpactTokenChange from '@components/HealthImpactTokenChange'
|
||||
import SolBalanceWarnings from '@components/shared/SolBalanceWarnings'
|
||||
import { useEnhancedWallet } from './wallet/EnhancedWalletProvider'
|
||||
import useSolBalance from 'hooks/useSolBalance'
|
||||
import FormatNumericValue from './shared/FormatNumericValue'
|
||||
import Decimal from 'decimal.js'
|
||||
|
@ -44,7 +43,7 @@ interface DepositFormProps {
|
|||
|
||||
export const walletBalanceForToken = (
|
||||
walletTokens: TokenAccount[],
|
||||
token: string
|
||||
token: string,
|
||||
): { maxAmount: number; maxDecimals: number } => {
|
||||
const group = mangoStore.getState().group
|
||||
const bank = group?.banksMapByName.get(token)?.[0]
|
||||
|
@ -68,11 +67,11 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
|
|||
const [inputAmount, setInputAmount] = useState('')
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const [selectedToken, setSelectedToken] = useState(
|
||||
token || INPUT_TOKEN_DEFAULT
|
||||
token || INPUT_TOKEN_DEFAULT,
|
||||
)
|
||||
const [showTokenList, setShowTokenList] = useState(false)
|
||||
const [sizePercentage, setSizePercentage] = useState('')
|
||||
const { handleConnect } = useEnhancedWallet()
|
||||
const { connect } = useWallet()
|
||||
const { maxSolDeposit } = useSolBalance()
|
||||
const banks = useBanksWithBalances('walletBalance')
|
||||
|
||||
|
@ -99,11 +98,11 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
|
|||
setSizePercentage(percentage)
|
||||
const amount = floorToDecimal(
|
||||
new Decimal(tokenMax.maxAmount).mul(percentage).div(100),
|
||||
tokenMax.maxDecimals
|
||||
tokenMax.maxDecimals,
|
||||
)
|
||||
setInputAmount(amount.toFixed())
|
||||
},
|
||||
[tokenMax]
|
||||
[tokenMax],
|
||||
)
|
||||
|
||||
const handleSelectToken = (token: string) => {
|
||||
|
@ -125,7 +124,7 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
|
|||
group,
|
||||
mangoAccount,
|
||||
bank.mint,
|
||||
parseFloat(inputAmount)
|
||||
parseFloat(inputAmount),
|
||||
)
|
||||
notify({
|
||||
title: 'Transaction confirmed',
|
||||
|
@ -226,7 +225,7 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
|
|||
value={inputAmount}
|
||||
onValueChange={(e: NumberFormatValues) => {
|
||||
setInputAmount(
|
||||
!Number.isNaN(Number(e.value)) ? e.value : ''
|
||||
!Number.isNaN(Number(e.value)) ? e.value : '',
|
||||
)
|
||||
}}
|
||||
isAllowed={withValueLimit}
|
||||
|
@ -272,7 +271,7 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
|
|||
) : null}
|
||||
</div>
|
||||
<Button
|
||||
onClick={connected ? handleDeposit : handleConnect}
|
||||
onClick={connected ? handleDeposit : connect}
|
||||
className="flex w-full items-center justify-center"
|
||||
disabled={connected && (!inputAmount || showInsufficientBalance)}
|
||||
size="large"
|
||||
|
|
|
@ -22,7 +22,7 @@ const HealthImpactTokenChange = ({
|
|||
mangoAccount.simHealthRatioWithTokenPositionUiChanges(
|
||||
group,
|
||||
[{ mintPk, uiTokenAmount }],
|
||||
HealthType.maint
|
||||
HealthType.maint,
|
||||
)
|
||||
|
||||
return projectedHealth! > 100
|
||||
|
|
|
@ -12,10 +12,13 @@ import { useViewport } from '../hooks/useViewport'
|
|||
import { breakpoints, nftThemeMeta } from '../utils/theme'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import BottomBar from './mobile/BottomBar'
|
||||
import BounceLoader from './shared/BounceLoader'
|
||||
import TopBar from './TopBar'
|
||||
import useLocalStorageState from '../hooks/useLocalStorageState'
|
||||
import { ACCEPT_TERMS_KEY, SIDEBAR_COLLAPSE_KEY } from '../utils/constants'
|
||||
import {
|
||||
ACCEPT_TERMS_KEY,
|
||||
SECONDS,
|
||||
SIDEBAR_COLLAPSE_KEY,
|
||||
} from '../utils/constants'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import SuccessParticles from './shared/SuccessParticles'
|
||||
import { tsParticles } from 'tsparticles-engine'
|
||||
|
@ -36,14 +39,14 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
const { theme } = useTheme()
|
||||
const [isCollapsed, setIsCollapsed] = useLocalStorageState(
|
||||
SIDEBAR_COLLAPSE_KEY,
|
||||
false
|
||||
false,
|
||||
)
|
||||
|
||||
const { width } = useViewport()
|
||||
const { asPath } = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
if (width < breakpoints.xl) {
|
||||
if (width < breakpoints['2xl']) {
|
||||
setIsCollapsed(true)
|
||||
}
|
||||
}, [width])
|
||||
|
@ -52,9 +55,12 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
const animationFrames = 15
|
||||
|
||||
for (let x = 1; x <= animationFrames; x++) {
|
||||
setTimeout(() => {
|
||||
window.dispatchEvent(new Event('resize'))
|
||||
}, (sideBarAnimationDuration / animationFrames) * x)
|
||||
setTimeout(
|
||||
() => {
|
||||
window.dispatchEvent(new Event('resize'))
|
||||
},
|
||||
(sideBarAnimationDuration / animationFrames) * x,
|
||||
)
|
||||
}
|
||||
}, [isCollapsed])
|
||||
|
||||
|
@ -72,7 +78,7 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
|
||||
useEffect(() => {
|
||||
const set = mangoStore.getState().set
|
||||
if (nftThemeMeta[theme]) {
|
||||
if (theme && nftThemeMeta[theme]) {
|
||||
set((s) => {
|
||||
s.themeData = nftThemeMeta[theme]
|
||||
})
|
||||
|
@ -90,7 +96,6 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
<div className="fixed z-30">
|
||||
<SuccessParticles />
|
||||
</div>
|
||||
<MangoAccountLoadingOverlay />
|
||||
<div
|
||||
className={`min-h-screen flex-grow ${
|
||||
!themeData.useGradientBg
|
||||
|
@ -104,7 +109,7 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
|
||||
<div className="fixed z-20 hidden h-screen md:block">
|
||||
<button
|
||||
className="absolute right-0 top-1/2 z-20 hidden h-8 w-3 -translate-y-1/2 rounded-none rounded-l bg-th-bkg-3 hover:bg-th-bkg-4 focus:outline-none focus-visible:bg-th-bkg-4 xl:block"
|
||||
className="absolute right-0 top-1/2 z-20 hidden h-8 w-3 -translate-y-1/2 rounded-none rounded-l bg-th-bkg-3 hover:bg-th-bkg-4 focus:outline-none focus-visible:bg-th-bkg-4 2xl:block"
|
||||
onClick={handleToggleSidebar}
|
||||
>
|
||||
<ChevronRightIcon
|
||||
|
@ -140,26 +145,11 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
|
||||
export default Layout
|
||||
|
||||
const MangoAccountLoadingOverlay = () => {
|
||||
const { connected } = useWallet()
|
||||
const loadingMangoAccount = mangoStore((s) => s.mangoAccount.initialLoad)
|
||||
|
||||
return (
|
||||
<>
|
||||
{connected && loadingMangoAccount ? (
|
||||
<div className="fixed z-30 flex h-screen w-full items-center justify-center bg-[rgba(0,0,0,0.7)]">
|
||||
<BounceLoader />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const TermsOfUse = () => {
|
||||
const { connected } = useWallet()
|
||||
const [acceptTerms, setAcceptTerms] = useLocalStorageState(
|
||||
ACCEPT_TERMS_KEY,
|
||||
''
|
||||
'',
|
||||
)
|
||||
|
||||
const showTermsOfUse = useMemo(() => {
|
||||
|
@ -190,7 +180,7 @@ function DeployRefreshManager(): JSX.Element | null {
|
|||
// There's a new version deployed that we need to load
|
||||
setNewBuildAvailable(true)
|
||||
}
|
||||
}, 300000)
|
||||
}, 300 * SECONDS)
|
||||
|
||||
return (
|
||||
<Transition
|
||||
|
|
|
@ -5,6 +5,10 @@ import { useRouter } from 'next/router'
|
|||
import { MangoAccount } from '@blockworks-foundation/mango-v4'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import useInterval from './shared/useInterval'
|
||||
import { SECONDS } from 'utils/constants'
|
||||
import useNetworkSpeed from 'hooks/useNetworkSpeed'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
|
||||
const set = mangoStore.getState().set
|
||||
const actions = mangoStore.getState().actions
|
||||
|
@ -14,6 +18,16 @@ const HydrateStore = () => {
|
|||
const { name: marketName } = router.query
|
||||
const { mangoAccountPk, mangoAccountAddress } = useMangoAccount()
|
||||
const connection = mangoStore((s) => s.connection)
|
||||
const slowNetwork = useNetworkSpeed()
|
||||
const { wallet } = useWallet()
|
||||
|
||||
const [, setLastWalletName] = useLocalStorageState('lastWalletName', '')
|
||||
|
||||
useEffect(() => {
|
||||
if (wallet?.adapter) {
|
||||
setLastWalletName(wallet?.adapter.name)
|
||||
}
|
||||
}, [wallet, setLastWalletName])
|
||||
|
||||
useEffect(() => {
|
||||
if (marketName && typeof marketName === 'string') {
|
||||
|
@ -24,17 +38,23 @@ const HydrateStore = () => {
|
|||
actions.fetchGroup()
|
||||
}, [marketName])
|
||||
|
||||
useInterval(() => {
|
||||
actions.fetchGroup()
|
||||
}, 25000)
|
||||
useInterval(
|
||||
() => {
|
||||
actions.fetchGroup()
|
||||
},
|
||||
(slowNetwork ? 40 : 20) * SECONDS,
|
||||
)
|
||||
|
||||
// refetches open orders every 30 seconds
|
||||
// only the selected market's open orders are updated via websocket
|
||||
useInterval(() => {
|
||||
if (mangoAccountAddress) {
|
||||
actions.fetchOpenOrders()
|
||||
}
|
||||
}, 30000)
|
||||
useInterval(
|
||||
() => {
|
||||
if (mangoAccountAddress) {
|
||||
actions.fetchOpenOrders()
|
||||
}
|
||||
},
|
||||
(slowNetwork ? 60 : 30) * SECONDS,
|
||||
)
|
||||
|
||||
// refetch trade history and activity feed when switching accounts
|
||||
useEffect(() => {
|
||||
|
@ -45,10 +65,13 @@ const HydrateStore = () => {
|
|||
}, [mangoAccountAddress])
|
||||
|
||||
// reload and parse market fills from the event queue
|
||||
useInterval(async () => {
|
||||
const actions = mangoStore.getState().actions
|
||||
actions.loadMarketFills()
|
||||
}, 30000)
|
||||
useInterval(
|
||||
async () => {
|
||||
const actions = mangoStore.getState().actions
|
||||
actions.loadMarketFills()
|
||||
},
|
||||
(slowNetwork ? 60 : 20) * SECONDS,
|
||||
)
|
||||
|
||||
// The websocket library solana/web3.js uses closes its websocket connection when the subscription list
|
||||
// is empty after opening its first time, preventing subsequent subscriptions from receiving responses.
|
||||
|
@ -78,11 +101,11 @@ const HydrateStore = () => {
|
|||
if (context.slot > lastSeenSlot) {
|
||||
const decodedMangoAccount = client.program.coder.accounts.decode(
|
||||
'mangoAccount',
|
||||
info?.data
|
||||
info?.data,
|
||||
)
|
||||
const newMangoAccount = MangoAccount.from(
|
||||
mangoAccount.publicKey,
|
||||
decodedMangoAccount
|
||||
decodedMangoAccount,
|
||||
)
|
||||
if (newMangoAccount.serum3Active().length > 0) {
|
||||
await newMangoAccount.reloadSerum3OpenOrders(client)
|
||||
|
@ -93,7 +116,7 @@ const HydrateStore = () => {
|
|||
})
|
||||
actions.fetchOpenOrders()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return () => {
|
||||
|
|
|
@ -45,7 +45,7 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
const [inputAmount, setInputAmount] = useState('')
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const [selectedToken, setSelectedToken] = useState(
|
||||
token || INPUT_TOKEN_DEFAULT
|
||||
token || INPUT_TOKEN_DEFAULT,
|
||||
)
|
||||
const [showTokenList, setShowTokenList] = useState(false)
|
||||
const [sizePercentage, setSizePercentage] = useState('')
|
||||
|
@ -69,7 +69,7 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
const borrowAmount = useMemo(() => {
|
||||
if (!mangoAccount || !bank) return new Decimal(0)
|
||||
const amount = new Decimal(
|
||||
mangoAccount.getTokenBorrowsUi(bank)
|
||||
mangoAccount.getTokenBorrowsUi(bank),
|
||||
).toDecimalPlaces(bank.mintDecimals, Decimal.ROUND_UP)
|
||||
return amount
|
||||
}, [bank, mangoAccount])
|
||||
|
@ -78,7 +78,7 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
if (!bank) return
|
||||
const amount = new Decimal(borrowAmount).toDecimalPlaces(
|
||||
bank.mintDecimals,
|
||||
Decimal.ROUND_UP
|
||||
Decimal.ROUND_UP,
|
||||
)
|
||||
setInputAmount(amount.toFixed())
|
||||
setSizePercentage('100')
|
||||
|
@ -95,7 +95,7 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
|
||||
setInputAmount(amount.toFixed())
|
||||
},
|
||||
[bank, borrowAmount]
|
||||
[bank, borrowAmount],
|
||||
)
|
||||
|
||||
const handleSelectToken = (token: string) => {
|
||||
|
@ -130,7 +130,7 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
mangoAccount,
|
||||
bank.mint,
|
||||
actualAmount,
|
||||
true
|
||||
true,
|
||||
)
|
||||
notify({
|
||||
title: 'Transaction confirmed',
|
||||
|
@ -154,7 +154,7 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
})
|
||||
}
|
||||
},
|
||||
[bank, publicKey?.toBase58(), sizePercentage]
|
||||
[bank, publicKey?.toBase58(), sizePercentage],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -236,7 +236,7 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
|
|||
value={inputAmount}
|
||||
onValueChange={(e: NumberFormatValues) => {
|
||||
setInputAmount(
|
||||
!Number.isNaN(Number(e.value)) ? e.value : ''
|
||||
!Number.isNaN(Number(e.value)) ? e.value : '',
|
||||
)
|
||||
}}
|
||||
isAllowed={withValueLimit}
|
||||
|
|
|
@ -10,7 +10,7 @@ const tpsWarningThreshold = 1300
|
|||
|
||||
const getRecentPerformance = async (
|
||||
connection: Connection,
|
||||
setTps: (x: number) => void
|
||||
setTps: (x: number) => void,
|
||||
) => {
|
||||
try {
|
||||
const samples = 2
|
||||
|
@ -36,7 +36,7 @@ const SolanaTps = () => {
|
|||
|
||||
useInterval(() => {
|
||||
getRecentPerformance(connection, setTps)
|
||||
}, 45 * 1000)
|
||||
}, 60 * 1000)
|
||||
|
||||
if (CLUSTER == 'mainnet-beta') {
|
||||
return (
|
||||
|
|
|
@ -32,17 +32,19 @@ import useBanksWithBalances, {
|
|||
import useUnownedAccount from 'hooks/useUnownedAccount'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import TokenLogo from './shared/TokenLogo'
|
||||
import useHealthContributions from 'hooks/useHealthContributions'
|
||||
|
||||
const TokenList = () => {
|
||||
const { t } = useTranslation(['common', 'token', 'trade'])
|
||||
const [showZeroBalances, setShowZeroBalances] = useLocalStorageState(
|
||||
SHOW_ZERO_BALANCES_KEY,
|
||||
true
|
||||
true,
|
||||
)
|
||||
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
|
||||
const { initContributions } = useHealthContributions()
|
||||
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
|
||||
const totalInterestData = mangoStore(
|
||||
(s) => s.mangoAccount.interestTotals.data
|
||||
(s) => s.mangoAccount.interestTotals.data,
|
||||
)
|
||||
const { width } = useViewport()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
|
@ -57,6 +59,18 @@ const TokenList = () => {
|
|||
return []
|
||||
}, [banks, mangoAccountAddress, showZeroBalances])
|
||||
|
||||
// const filteredBanks = useMemo(() => {
|
||||
// if (!banks.length) return []
|
||||
// if (showZeroBalances || !mangoAccountAddress) return banks
|
||||
// const filtered = banks.filter((b) => {
|
||||
// const contribution =
|
||||
// initContributions.find((cont) => cont.asset === b.bank.name)
|
||||
// ?.contribution || 0
|
||||
// return Math.abs(contribution) > 0
|
||||
// })
|
||||
// return filtered
|
||||
// }, [banks, mangoAccountAddress, showZeroBalances, initContributions])
|
||||
|
||||
return (
|
||||
<ContentBox hideBorder hidePadding>
|
||||
{mangoAccountAddress ? (
|
||||
|
@ -66,141 +80,181 @@ const TokenList = () => {
|
|||
disabled={!mangoAccount}
|
||||
onChange={() => setShowZeroBalances(!showZeroBalances)}
|
||||
>
|
||||
{t('show-zero-balances')}
|
||||
{t('account:zero-balances')}
|
||||
</Switch>
|
||||
</div>
|
||||
) : null}
|
||||
{showTableView ? (
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
<Th className="text-left">{t('token')}</Th>
|
||||
<Th>
|
||||
<div className="flex justify-end">
|
||||
<Tooltip content="A negative balance represents a borrow">
|
||||
<span className="tooltip-underline">{t('balance')}</span>
|
||||
<>
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
<Th className="text-left">{t('token')}</Th>
|
||||
<Th>
|
||||
<div className="flex justify-end">
|
||||
<Tooltip content="A negative balance represents a borrow">
|
||||
<span className="tooltip-underline">{t('balance')}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Th>
|
||||
<Th>
|
||||
<div className="flex justify-end">
|
||||
<Tooltip content={t('tooltip-collateral-value')}>
|
||||
<span className="tooltip-underline">
|
||||
{t('collateral-value')}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Th>
|
||||
<Th className="text-right">{t('trade:in-orders')}</Th>
|
||||
<Th className="text-right">{t('trade:unsettled')}</Th>
|
||||
<Th className="flex justify-end" id="account-step-nine">
|
||||
<Tooltip content="The sum of interest earned and interest paid for each token">
|
||||
<span className="tooltip-underline">
|
||||
{t('interest-earned-paid')}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Th>
|
||||
<Th className="text-right">{t('trade:in-orders')}</Th>
|
||||
<Th className="text-right">{t('trade:unsettled')}</Th>
|
||||
<Th className="flex justify-end" id="account-step-nine">
|
||||
<Tooltip content="The sum of interest earned and interest paid for each token">
|
||||
<span className="tooltip-underline">
|
||||
{t('interest-earned-paid')}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Th>
|
||||
<Th id="account-step-ten">
|
||||
<div className="flex justify-end">
|
||||
<Tooltip content="The interest rates for depositing (green/left) and borrowing (red/right)">
|
||||
<span className="tooltip-underline">{t('rates')}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Th>
|
||||
<Th className="text-right">
|
||||
<span>{t('actions')}</span>
|
||||
</Th>
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filteredBanks.map((b) => {
|
||||
const bank = b.bank
|
||||
</Th>
|
||||
<Th id="account-step-ten">
|
||||
<div className="flex justify-end">
|
||||
<Tooltip content="The interest rates for depositing (green/left) and borrowing (red/right)">
|
||||
<span className="tooltip-underline">{t('rates')}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Th>
|
||||
<Th className="text-right">
|
||||
<span>{t('actions')}</span>
|
||||
</Th>
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filteredBanks.map((b) => {
|
||||
const bank = b.bank
|
||||
|
||||
const tokenBalance = b.balance
|
||||
const symbol = bank.name === 'MSOL' ? 'mSOL' : bank.name
|
||||
const tokenBalance = b.balance
|
||||
const symbol = bank.name === 'MSOL' ? 'mSOL' : bank.name
|
||||
|
||||
const hasInterestEarned = totalInterestData.find(
|
||||
(d) =>
|
||||
d.symbol.toLowerCase() === symbol.toLowerCase() ||
|
||||
(symbol === 'ETH (Portal)' && d.symbol === 'ETH')
|
||||
)
|
||||
const hasInterestEarned = totalInterestData.find(
|
||||
(d) =>
|
||||
d.symbol.toLowerCase() === symbol.toLowerCase() ||
|
||||
(symbol === 'ETH (Portal)' && d.symbol === 'ETH'),
|
||||
)
|
||||
|
||||
const interestAmount = hasInterestEarned
|
||||
? hasInterestEarned.borrow_interest * -1 +
|
||||
hasInterestEarned.deposit_interest
|
||||
: 0
|
||||
const interestAmount = hasInterestEarned
|
||||
? hasInterestEarned.borrow_interest * -1 +
|
||||
hasInterestEarned.deposit_interest
|
||||
: 0
|
||||
|
||||
const interestValue = hasInterestEarned
|
||||
? hasInterestEarned.borrow_interest_usd * -1 +
|
||||
hasInterestEarned.deposit_interest_usd
|
||||
: 0.0
|
||||
const interestValue = hasInterestEarned
|
||||
? hasInterestEarned.borrow_interest_usd * -1 +
|
||||
hasInterestEarned.deposit_interest_usd
|
||||
: 0.0
|
||||
|
||||
const inOrders = spotBalances[bank.mint.toString()]?.inOrders || 0
|
||||
const inOrders =
|
||||
spotBalances[bank.mint.toString()]?.inOrders || 0
|
||||
|
||||
const unsettled =
|
||||
spotBalances[bank.mint.toString()]?.unsettled || 0
|
||||
const unsettled =
|
||||
spotBalances[bank.mint.toString()]?.unsettled || 0
|
||||
|
||||
return (
|
||||
<TrBody key={bank.name}>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<div className="mr-2.5 flex flex-shrink-0 items-center">
|
||||
<TokenLogo bank={bank} />
|
||||
const collateralValue =
|
||||
initContributions.find((val) => val.asset === bank.name)
|
||||
?.contribution || 0
|
||||
|
||||
const assetWeight = bank
|
||||
.scaledInitAssetWeight(bank.price)
|
||||
.toFixed(2)
|
||||
const liabWeight = bank
|
||||
.scaledInitLiabWeight(bank.price)
|
||||
.toFixed(2)
|
||||
|
||||
return (
|
||||
<TrBody key={bank.name}>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<div className="mr-2.5 flex flex-shrink-0 items-center">
|
||||
<TokenLogo bank={bank} />
|
||||
</div>
|
||||
<p className="font-body">{symbol}</p>
|
||||
</div>
|
||||
<p className="font-body">{symbol}</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<BankAmountWithValue
|
||||
amount={tokenBalance}
|
||||
bank={bank}
|
||||
stacked
|
||||
/>
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<BankAmountWithValue
|
||||
amount={inOrders}
|
||||
bank={bank}
|
||||
stacked
|
||||
/>
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<BankAmountWithValue
|
||||
amount={unsettled}
|
||||
bank={bank}
|
||||
stacked
|
||||
/>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex flex-col text-right">
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<BankAmountWithValue
|
||||
amount={interestAmount}
|
||||
amount={tokenBalance}
|
||||
bank={bank}
|
||||
value={interestValue}
|
||||
stacked
|
||||
/>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex justify-end space-x-1.5">
|
||||
<p className="text-th-up">
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<p>
|
||||
<FormatNumericValue
|
||||
value={bank.getDepositRateUi()}
|
||||
value={collateralValue}
|
||||
decimals={2}
|
||||
isUsd
|
||||
/>
|
||||
%
|
||||
</p>
|
||||
<span className="text-th-fgd-4">|</span>
|
||||
<p className="text-th-down">
|
||||
<p className="text-sm text-th-fgd-4">
|
||||
<FormatNumericValue
|
||||
value={bank.getBorrowRateUi()}
|
||||
decimals={2}
|
||||
value={
|
||||
collateralValue <= -0.01 ? liabWeight : assetWeight
|
||||
}
|
||||
/>
|
||||
%
|
||||
x
|
||||
</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex items-center justify-end">
|
||||
<ActionsMenu bank={bank} mangoAccount={mangoAccount} />
|
||||
</div>
|
||||
</Td>
|
||||
</TrBody>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<BankAmountWithValue
|
||||
amount={inOrders}
|
||||
bank={bank}
|
||||
stacked
|
||||
/>
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<BankAmountWithValue
|
||||
amount={unsettled}
|
||||
bank={bank}
|
||||
stacked
|
||||
/>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex flex-col text-right">
|
||||
<BankAmountWithValue
|
||||
amount={interestAmount}
|
||||
bank={bank}
|
||||
value={interestValue}
|
||||
stacked
|
||||
/>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex justify-end space-x-1.5">
|
||||
<p className="text-th-up">
|
||||
<FormatNumericValue
|
||||
value={bank.getDepositRateUi()}
|
||||
decimals={2}
|
||||
/>
|
||||
%
|
||||
</p>
|
||||
<span className="text-th-fgd-4">|</span>
|
||||
<p className="text-th-down">
|
||||
<FormatNumericValue
|
||||
value={bank.getBorrowRateUi()}
|
||||
decimals={2}
|
||||
/>
|
||||
%
|
||||
</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex items-center justify-end">
|
||||
<ActionsMenu bank={bank} mangoAccount={mangoAccount} />
|
||||
</div>
|
||||
</Td>
|
||||
</TrBody>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
</>
|
||||
) : (
|
||||
<div className="border-b border-th-bkg-3">
|
||||
{filteredBanks.map((b) => {
|
||||
|
@ -218,8 +272,9 @@ const MobileTokenListItem = ({ bank }: { bank: BankWithBalance }) => {
|
|||
const { t } = useTranslation(['common', 'token'])
|
||||
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const { initContributions } = useHealthContributions()
|
||||
const totalInterestData = mangoStore(
|
||||
(s) => s.mangoAccount.interestTotals.data
|
||||
(s) => s.mangoAccount.interestTotals.data,
|
||||
)
|
||||
const tokenBank = bank.bank
|
||||
const mint = tokenBank.mint
|
||||
|
@ -228,7 +283,7 @@ const MobileTokenListItem = ({ bank }: { bank: BankWithBalance }) => {
|
|||
const hasInterestEarned = totalInterestData.find(
|
||||
(d) =>
|
||||
d.symbol.toLowerCase() === symbol.toLowerCase() ||
|
||||
(symbol === 'ETH (Portal)' && d.symbol === 'ETH')
|
||||
(symbol === 'ETH (Portal)' && d.symbol === 'ETH'),
|
||||
)
|
||||
|
||||
const interestAmount = hasInterestEarned
|
||||
|
@ -242,10 +297,17 @@ const MobileTokenListItem = ({ bank }: { bank: BankWithBalance }) => {
|
|||
: 0
|
||||
|
||||
const tokenBalance = bank.balance
|
||||
|
||||
const unsettled = spotBalances[mint.toString()]?.unsettled || 0
|
||||
const inOrders = spotBalances[mint.toString()]?.inOrders || 0
|
||||
|
||||
const unsettled = spotBalances[mint.toString()]?.unsettled || 0
|
||||
const collateralValue =
|
||||
initContributions.find((val) => val.asset === tokenBank.name)
|
||||
?.contribution || 0
|
||||
|
||||
const assetWeight = tokenBank
|
||||
.scaledInitAssetWeight(tokenBank.price)
|
||||
.toFixed(2)
|
||||
const liabWeight = tokenBank.scaledInitLiabWeight(tokenBank.price).toFixed(2)
|
||||
|
||||
return (
|
||||
<Disclosure>
|
||||
|
@ -296,6 +358,29 @@ const MobileTokenListItem = ({ bank }: { bank: BankWithBalance }) => {
|
|||
>
|
||||
<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">
|
||||
<Tooltip content={t('tooltip-collateral-value')}>
|
||||
<p className="tooltip-underline text-xs text-th-fgd-3">
|
||||
{t('collateral-value')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
<FormatNumericValue
|
||||
value={collateralValue}
|
||||
decimals={2}
|
||||
isUsd
|
||||
/>
|
||||
<span className="text-th-fgd-3">
|
||||
{' '}
|
||||
<FormatNumericValue
|
||||
value={
|
||||
collateralValue <= -0.01 ? liabWeight : assetWeight
|
||||
}
|
||||
/>
|
||||
x
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
{t('trade:in-orders')}
|
||||
|
@ -387,7 +472,7 @@ const ActionsMenu = ({
|
|||
? setShowWithdrawModal(true)
|
||||
: setShowRepayModal(true)
|
||||
},
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const balance = useMemo(() => {
|
||||
|
@ -397,7 +482,7 @@ const ActionsMenu = ({
|
|||
|
||||
const handleSwap = useCallback(() => {
|
||||
const tokenInfo = mangoTokens.find(
|
||||
(t) => t.address === bank.mint.toString()
|
||||
(t) => t.address === bank.mint.toString(),
|
||||
)
|
||||
const group = mangoStore.getState().group
|
||||
if (balance && balance > 0) {
|
||||
|
@ -416,7 +501,7 @@ const ActionsMenu = ({
|
|||
} else {
|
||||
if (tokenInfo?.symbol === 'USDC') {
|
||||
const solTokenInfo = mangoTokens.find(
|
||||
(t) => t.address === WRAPPED_SOL_MINT.toString()
|
||||
(t) => t.address === WRAPPED_SOL_MINT.toString(),
|
||||
)
|
||||
const solBank = group?.getFirstBankByMint(WRAPPED_SOL_MINT)
|
||||
set((s) => {
|
||||
|
@ -466,7 +551,7 @@ const ActionsMenu = ({
|
|||
leaveTo="opacity-0"
|
||||
>
|
||||
<Popover.Panel
|
||||
className={`thin-scroll absolute bottom-12 left-0 z-40 max-h-60 w-32 space-y-2 overflow-auto rounded-md bg-th-bkg-2 p-4 pt-2 md:bottom-0 md:right-12 md:left-auto md:pt-4`}
|
||||
className={`thin-scroll absolute bottom-12 left-0 z-20 max-h-60 w-32 space-y-2 overflow-auto rounded-md bg-th-bkg-2 p-4 pt-2 md:bottom-0 md:right-12 md:left-auto md:pt-4`}
|
||||
>
|
||||
<div className="hidden items-center justify-center border-b border-th-bkg-3 pb-2 md:flex">
|
||||
<div className="mr-2 flex flex-shrink-0 items-center">
|
||||
|
|
|
@ -12,12 +12,9 @@ import { useTranslation } from 'next-i18next'
|
|||
import WalletIcon from './icons/WalletIcon'
|
||||
import Button from './shared/Button'
|
||||
import ConnectedMenu from './wallet/ConnectedMenu'
|
||||
import { ConnectWalletButton } from './wallet/ConnectWalletButton'
|
||||
import { IS_ONBOARDED_KEY } from '../utils/constants'
|
||||
import useLocalStorageState from '../hooks/useLocalStorageState'
|
||||
import ConnectWalletButton from './wallet/ConnectWalletButton'
|
||||
import CreateAccountModal from './modals/CreateAccountModal'
|
||||
import { useRouter } from 'next/router'
|
||||
import UserSetupModal from './modals/UserSetupModal'
|
||||
import SolanaTps from './SolanaTps'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import useOnlineStatus from 'hooks/useOnlineStatus'
|
||||
|
@ -31,33 +28,46 @@ import NotificationsButton from './notifications/NotificationsButton'
|
|||
import Tooltip from './shared/Tooltip'
|
||||
import { copyToClipboard } from 'utils'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import UserSetupModal from './modals/UserSetupModal'
|
||||
import { IS_ONBOARDED_KEY } from 'utils/constants'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
|
||||
const set = mangoStore.getState().set
|
||||
|
||||
const TopBar = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const { connected } = useWallet()
|
||||
const themeData = mangoStore((s) => s.themeData)
|
||||
const [isOnboarded, setIsOnboarded] = useLocalStorageState(IS_ONBOARDED_KEY)
|
||||
|
||||
const [action, setAction] = useState<'deposit' | 'withdraw'>('deposit')
|
||||
const [showUserSetup, setShowUserSetup] = useState(false)
|
||||
const [copied, setCopied] = useState('')
|
||||
const [showDepositWithdrawModal, setShowDepositWithdrawModal] =
|
||||
useState(false)
|
||||
const [showCreateAccountModal, setShowCreateAccountModal] = useState(false)
|
||||
const isOnline = useOnlineStatus()
|
||||
|
||||
const router = useRouter()
|
||||
const { query } = router
|
||||
|
||||
const { width } = useViewport()
|
||||
const isMobile = width ? width < breakpoints.sm : false
|
||||
|
||||
const { isUnownedAccount } = useUnownedAccount()
|
||||
const showUserSetup = mangoStore((s) => s.showUserSetup)
|
||||
const [, setIsOnboarded] = useLocalStorageState(IS_ONBOARDED_KEY)
|
||||
|
||||
const handleCloseSetup = useCallback(() => {
|
||||
setShowUserSetup(false)
|
||||
set((s) => {
|
||||
s.showUserSetup = false
|
||||
})
|
||||
setIsOnboarded(true)
|
||||
}, [setShowUserSetup, setIsOnboarded])
|
||||
}, [setIsOnboarded])
|
||||
|
||||
const handleShowSetup = useCallback(() => {
|
||||
setShowUserSetup(true)
|
||||
set((s) => {
|
||||
s.showUserSetup = true
|
||||
})
|
||||
}, [])
|
||||
|
||||
const handleDepositWithdrawModal = (action: 'deposit' | 'withdraw') => {
|
||||
|
@ -194,18 +204,8 @@ const TopBar = () => {
|
|||
<AccountsButton />
|
||||
<ConnectedMenu />
|
||||
</div>
|
||||
) : isOnboarded ? (
|
||||
<ConnectWalletButton />
|
||||
) : (
|
||||
<button
|
||||
className="relative h-16 rounded-none bg-th-bkg-2 bg-gradient-to-bl px-6 font-display text-base text-th-fgd-1 before:absolute before:inset-0 before:bg-gradient-to-r before:from-transparent before:via-th-bkg-4 before:to-transparent before:opacity-0 hover:cursor-pointer hover:overflow-hidden hover:before:-translate-x-full hover:before:animate-[shimmer_0.75s_normal] hover:before:opacity-100"
|
||||
onClick={handleShowSetup}
|
||||
>
|
||||
<div className="relative z-10 flex items-center justify-center">
|
||||
<WalletIcon className="mr-2 h-5 w-5 flex-shrink-0" />
|
||||
{t('connect')}
|
||||
</div>
|
||||
</button>
|
||||
<ConnectWalletButton handleShowSetup={handleShowSetup} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -30,7 +30,6 @@ import useMangoAccount from 'hooks/useMangoAccount'
|
|||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import TokenVaultWarnings from '@components/shared/TokenVaultWarnings'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import { useEnhancedWallet } from './wallet/EnhancedWalletProvider'
|
||||
import { floorToDecimal } from 'utils/numbers'
|
||||
import BankAmountWithValue from './shared/BankAmountWithValue'
|
||||
import useBanksWithBalances from 'hooks/useBanksWithBalances'
|
||||
|
@ -50,13 +49,12 @@ function WithdrawForm({ onSuccess, token }: WithdrawFormProps) {
|
|||
const [inputAmount, setInputAmount] = useState('')
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const [selectedToken, setSelectedToken] = useState(
|
||||
token || INPUT_TOKEN_DEFAULT
|
||||
token || INPUT_TOKEN_DEFAULT,
|
||||
)
|
||||
const [showTokenList, setShowTokenList] = useState(false)
|
||||
const [sizePercentage, setSizePercentage] = useState('')
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const { connected } = useWallet()
|
||||
const { handleConnect } = useEnhancedWallet()
|
||||
const { connect, connected } = useWallet()
|
||||
const banks = useBanksWithBalances('maxWithdraw')
|
||||
|
||||
const bank = useMemo(() => {
|
||||
|
@ -84,17 +82,17 @@ function WithdrawForm({ onSuccess, token }: WithdrawFormProps) {
|
|||
if (percentage !== '100') {
|
||||
amount = floorToDecimal(
|
||||
new Decimal(adjustedTokenMax).mul(percentage).div(100),
|
||||
bank.mintDecimals
|
||||
bank.mintDecimals,
|
||||
)
|
||||
} else {
|
||||
amount = floorToDecimal(
|
||||
new Decimal(adjustedTokenMax),
|
||||
bank.mintDecimals
|
||||
bank.mintDecimals,
|
||||
)
|
||||
}
|
||||
setInputAmount(amount.toString())
|
||||
},
|
||||
[bank, adjustedTokenMax]
|
||||
[bank, adjustedTokenMax],
|
||||
)
|
||||
|
||||
const setMax = useCallback(() => {
|
||||
|
@ -118,7 +116,7 @@ function WithdrawForm({ onSuccess, token }: WithdrawFormProps) {
|
|||
mangoAccount,
|
||||
bank.mint,
|
||||
withdrawAmount,
|
||||
false
|
||||
false,
|
||||
)
|
||||
notify({
|
||||
title: 'Transaction confirmed',
|
||||
|
@ -229,7 +227,7 @@ function WithdrawForm({ onSuccess, token }: WithdrawFormProps) {
|
|||
value={inputAmount}
|
||||
onValueChange={(e: NumberFormatValues) =>
|
||||
setInputAmount(
|
||||
!Number.isNaN(Number(e.value)) ? e.value : ''
|
||||
!Number.isNaN(Number(e.value)) ? e.value : '',
|
||||
)
|
||||
}
|
||||
isAllowed={withValueLimit}
|
||||
|
@ -259,7 +257,7 @@ function WithdrawForm({ onSuccess, token }: WithdrawFormProps) {
|
|||
) : null}
|
||||
</div>
|
||||
<Button
|
||||
onClick={connected ? handleWithdraw : handleConnect}
|
||||
onClick={connected ? handleWithdraw : connect}
|
||||
className="flex w-full items-center justify-center"
|
||||
size="large"
|
||||
disabled={
|
||||
|
|
|
@ -29,7 +29,7 @@ import { breakpoints } from 'utils/theme'
|
|||
|
||||
export const handleCopyAddress = (
|
||||
mangoAccount: MangoAccount,
|
||||
successMessage: string
|
||||
successMessage: string,
|
||||
) => {
|
||||
copyToClipboard(mangoAccount.publicKey.toString())
|
||||
notify({
|
||||
|
@ -121,7 +121,7 @@ const AccountActions = () => {
|
|||
mangoAccount!,
|
||||
t('copy-address-success', {
|
||||
pk: abbreviateAddress(mangoAccount!.publicKey),
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
>
|
||||
|
|
|
@ -40,7 +40,7 @@ const AccountHeroStats = ({
|
|||
useAccountHourlyVolumeStats()
|
||||
|
||||
const totalInterestData = mangoStore(
|
||||
(s) => s.mangoAccount.interestTotals.data
|
||||
(s) => s.mangoAccount.interestTotals.data,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -63,7 +63,7 @@ const AccountHeroStats = ({
|
|||
retry: 3,
|
||||
refetchOnWindowFocus: false,
|
||||
enabled: !!mangoAccountAddress,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const {
|
||||
|
@ -79,7 +79,7 @@ const AccountHeroStats = ({
|
|||
retry: 3,
|
||||
refetchOnWindowFocus: false,
|
||||
enabled: !!mangoAccountAddress,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const maintHealth = useMemo(() => {
|
||||
|
@ -91,7 +91,7 @@ const AccountHeroStats = ({
|
|||
const leverage = useMemo(() => {
|
||||
if (!group || !mangoAccount) return 0
|
||||
const assetsValue = toUiDecimalsForQuote(
|
||||
mangoAccount.getAssetsValue(group).toNumber()
|
||||
mangoAccount.getAssetsValue(group).toNumber(),
|
||||
)
|
||||
|
||||
if (isNaN(assetsValue / accountValue)) {
|
||||
|
@ -110,7 +110,7 @@ const AccountHeroStats = ({
|
|||
if (totalInterestData.length) {
|
||||
return totalInterestData.reduce(
|
||||
(a, c) => a + (c.borrow_interest_usd * -1 + c.deposit_interest_usd),
|
||||
0
|
||||
0,
|
||||
)
|
||||
}
|
||||
return 0.0
|
||||
|
@ -120,7 +120,7 @@ const AccountHeroStats = ({
|
|||
if (fundingData?.length && mangoAccountAddress) {
|
||||
return fundingData.reduce(
|
||||
(a, c) => a + c.long_funding + c.short_funding,
|
||||
0
|
||||
0,
|
||||
)
|
||||
}
|
||||
return 0.0
|
||||
|
@ -167,7 +167,7 @@ const AccountHeroStats = ({
|
|||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-6 border-b border-th-bkg-3">
|
||||
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 md:col-span-3 lg:col-span-2 lg:border-t-0 xl:col-span-1">
|
||||
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 md:col-span-3 lg:col-span-2 lg:border-t-0 2xl:col-span-1">
|
||||
<div id="account-step-four">
|
||||
<div className="flex justify-between">
|
||||
<Tooltip
|
||||
|
@ -214,9 +214,7 @@ const AccountHeroStats = ({
|
|||
</div>
|
||||
}
|
||||
>
|
||||
<p className="tooltip-underline text-sm font-normal text-th-fgd-3 xl:text-base">
|
||||
{t('health')}
|
||||
</p>
|
||||
<p className="tooltip-underline">{t('health')}</p>
|
||||
</Tooltip>
|
||||
{mangoAccountAddress ? (
|
||||
<Tooltip
|
||||
|
@ -255,7 +253,7 @@ const AccountHeroStats = ({
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-6 flex border-t border-th-bkg-3 py-3 pl-6 md:col-span-3 md:border-l lg:col-span-2 lg:border-t-0 xl:col-span-1">
|
||||
<div className="col-span-6 flex border-t border-th-bkg-3 py-3 pl-6 md:col-span-3 md:border-l lg:col-span-2 lg:border-t-0 2xl:col-span-1">
|
||||
<div id="account-step-five">
|
||||
<Tooltip
|
||||
content={t('account:tooltip-free-collateral')}
|
||||
|
@ -263,16 +261,14 @@ const AccountHeroStats = ({
|
|||
placement="top-start"
|
||||
delay={100}
|
||||
>
|
||||
<p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base">
|
||||
{t('free-collateral')}
|
||||
</p>
|
||||
<p className="tooltip-underline">{t('free-collateral')}</p>
|
||||
</Tooltip>
|
||||
<p className="mt-1 mb-0.5 text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
|
||||
<FormatNumericValue
|
||||
value={
|
||||
group && mangoAccount
|
||||
? toUiDecimalsForQuote(
|
||||
mangoAccount.getCollateralValue(group)
|
||||
mangoAccount.getCollateralValue(group),
|
||||
)
|
||||
: 0
|
||||
}
|
||||
|
@ -293,7 +289,7 @@ const AccountHeroStats = ({
|
|||
value={
|
||||
group && mangoAccount
|
||||
? toUiDecimalsForQuote(
|
||||
mangoAccount.getAssetsValue(group, HealthType.init)
|
||||
mangoAccount.getAssetsValue(group, HealthType.init),
|
||||
)
|
||||
: 0
|
||||
}
|
||||
|
@ -305,7 +301,7 @@ const AccountHeroStats = ({
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-6 flex border-t border-th-bkg-3 py-3 pl-6 pr-4 md:col-span-3 lg:col-span-2 lg:border-l lg:border-t-0 xl:col-span-1">
|
||||
<div className="col-span-6 flex border-t border-th-bkg-3 py-3 pl-6 pr-4 md:col-span-3 lg:col-span-2 lg:border-l lg:border-t-0 2xl:col-span-1">
|
||||
<div
|
||||
id="account-step-seven"
|
||||
className="flex w-full flex-col items-start"
|
||||
|
@ -316,9 +312,7 @@ const AccountHeroStats = ({
|
|||
placement="top-start"
|
||||
delay={100}
|
||||
>
|
||||
<p className="tooltip-underline inline text-sm text-th-fgd-3 xl:text-base">
|
||||
{t('pnl')}
|
||||
</p>
|
||||
<p className="tooltip-underline">{t('pnl')}</p>
|
||||
</Tooltip>
|
||||
{mangoAccountAddress ? (
|
||||
<div className="flex items-center space-x-3">
|
||||
|
@ -364,12 +358,10 @@ const AccountHeroStats = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 md:col-span-3 md:border-l lg:col-span-2 lg:border-l-0 xl:col-span-1 xl:border-l xl:border-t-0">
|
||||
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 md:col-span-3 md:border-l lg:col-span-2 lg:border-l-0 2xl:col-span-1 2xl:border-l 2xl:border-t-0">
|
||||
<div id="account-step-six">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<p className="text-sm text-th-fgd-3 xl:text-base">
|
||||
{t('account:lifetime-volume')}
|
||||
</p>
|
||||
<p>{t('account:lifetime-volume')}</p>
|
||||
{mangoAccountAddress ? (
|
||||
<Tooltip
|
||||
className="hidden md:block"
|
||||
|
@ -413,7 +405,7 @@ const AccountHeroStats = ({
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 text-left md:col-span-3 lg:col-span-2 lg:border-l xl:col-span-1 xl:border-t-0">
|
||||
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 text-left md:col-span-3 lg:col-span-2 lg:border-l 2xl:col-span-1 2xl:border-t-0">
|
||||
<div id="account-step-eight">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<Tooltip
|
||||
|
@ -422,7 +414,7 @@ const AccountHeroStats = ({
|
|||
placement="top-start"
|
||||
delay={100}
|
||||
>
|
||||
<p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base">
|
||||
<p className="tooltip-underline">
|
||||
{t('total-interest-earned')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
|
@ -457,7 +449,7 @@ const AccountHeroStats = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 text-left md:col-span-3 md:border-l lg:col-span-2 xl:col-span-1 xl:border-t-0">
|
||||
<div className="col-span-6 border-t border-th-bkg-3 py-3 pl-6 pr-4 text-left md:col-span-3 md:border-l lg:col-span-2 2xl:col-span-1 2xl:border-t-0">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<Tooltip
|
||||
content={t('account:tooltip-total-funding')}
|
||||
|
@ -465,7 +457,7 @@ const AccountHeroStats = ({
|
|||
placement="top-start"
|
||||
delay={100}
|
||||
>
|
||||
<p className="tooltip-underline text-sm text-th-fgd-3 xl:text-base">
|
||||
<p className="tooltip-underline">
|
||||
{t('account:total-funding-earned')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
|
|
|
@ -42,7 +42,7 @@ const AccountPage = () => {
|
|||
const isMobile = width ? width < breakpoints.md : false
|
||||
const [activeTab, setActiveTab] = useLocalStorageState(
|
||||
'accountHeroKey-0.1',
|
||||
'account-value'
|
||||
'account-value',
|
||||
)
|
||||
const { performanceData, rollingDailyData } = useAccountPerformanceData()
|
||||
const router = useRouter()
|
||||
|
@ -53,7 +53,7 @@ const AccountPage = () => {
|
|||
const query = { ...router.query, ['view']: view }
|
||||
router.push({ pathname: router.pathname, query })
|
||||
},
|
||||
[router]
|
||||
[router],
|
||||
)
|
||||
|
||||
const handleCloseDailyPnlModal = () => {
|
||||
|
|
|
@ -8,7 +8,6 @@ import { Transition } from '@headlessui/react'
|
|||
import SheenLoader from '../shared/SheenLoader'
|
||||
import Change from '../shared/Change'
|
||||
import FormatNumericValue from '@components/shared/FormatNumericValue'
|
||||
import { useTheme } from 'next-themes'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
|
||||
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
|
||||
|
@ -21,6 +20,7 @@ import { useViewport } from 'hooks/useViewport'
|
|||
import { breakpoints } from 'utils/theme'
|
||||
import { ViewToShow } from './AccountPage'
|
||||
import useAccountPerformanceData from 'hooks/useAccountPerformanceData'
|
||||
import useThemeWrapper from 'hooks/useThemeWrapper'
|
||||
|
||||
const AccountValue = ({
|
||||
accountValue,
|
||||
|
@ -34,13 +34,13 @@ const AccountValue = ({
|
|||
handleViewChange: (view: ViewToShow) => void
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { theme } = useTheme()
|
||||
const { theme } = useThemeWrapper()
|
||||
const { group } = useMangoGroup()
|
||||
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
|
||||
const [showExpandChart, setShowExpandChart] = useState<boolean>(false)
|
||||
const [animationSettings] = useLocalStorageState(
|
||||
ANIMATION_SETTINGS_KEY,
|
||||
INITIAL_ANIMATION_SETTINGS
|
||||
INITIAL_ANIMATION_SETTINGS,
|
||||
)
|
||||
const { width } = useViewport()
|
||||
const { performanceLoading: loading } = useAccountPerformanceData()
|
||||
|
|
|
@ -48,7 +48,7 @@ export const getFee = (activity: any, mangoAccountAddress: string) => {
|
|||
const value = price * quantity
|
||||
fee = {
|
||||
value: formatFee(
|
||||
mangoAccountAddress === maker ? maker_fee * value : taker_fee * value
|
||||
mangoAccountAddress === maker ? maker_fee * value : taker_fee * value,
|
||||
),
|
||||
symbol: 'USDC',
|
||||
}
|
||||
|
@ -63,14 +63,14 @@ export const getFee = (activity: any, mangoAccountAddress: string) => {
|
|||
if (side === 'liqee') {
|
||||
fee = {
|
||||
value: formatNumericValue(
|
||||
Math.abs(liab_amount) - Math.abs(asset_amount * asset_price)
|
||||
Math.abs(liab_amount) - Math.abs(asset_amount * asset_price),
|
||||
),
|
||||
symbol: liab_symbol,
|
||||
}
|
||||
} else {
|
||||
fee = {
|
||||
value: formatNumericValue(
|
||||
Math.abs(asset_amount * asset_price) - Math.abs(liab_amount)
|
||||
Math.abs(asset_amount * asset_price) - Math.abs(liab_amount),
|
||||
),
|
||||
symbol: liab_symbol,
|
||||
}
|
||||
|
@ -81,14 +81,14 @@ export const getFee = (activity: any, mangoAccountAddress: string) => {
|
|||
if (base_transfer > 0) {
|
||||
fee = {
|
||||
value: formatNumericValue(
|
||||
Math.abs(base_transfer * price) - Math.abs(quote_transfer)
|
||||
Math.abs(base_transfer * price) - Math.abs(quote_transfer),
|
||||
),
|
||||
symbol: 'USDC',
|
||||
}
|
||||
} else {
|
||||
fee = {
|
||||
value: formatNumericValue(
|
||||
Math.abs(quote_transfer) - Math.abs(base_transfer * price)
|
||||
Math.abs(quote_transfer) - Math.abs(base_transfer * price),
|
||||
),
|
||||
symbol: 'USDC',
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ export const getFee = (activity: any, mangoAccountAddress: string) => {
|
|||
|
||||
export const getCreditAndDebit = (
|
||||
activity: any,
|
||||
mangoAccountAddress: string
|
||||
mangoAccountAddress: string,
|
||||
) => {
|
||||
const { activity_type } = activity
|
||||
let credit = { value: '0', symbol: '' }
|
||||
|
@ -270,7 +270,7 @@ const ActivityFeedTable = () => {
|
|||
const { connected } = useWallet()
|
||||
const [preferredExplorer] = useLocalStorageState(
|
||||
PREFERRED_EXPLORER_KEY,
|
||||
EXPLORERS[0]
|
||||
EXPLORERS[0],
|
||||
)
|
||||
const { width } = useViewport()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
|
@ -285,7 +285,7 @@ const ActivityFeedTable = () => {
|
|||
actions.fetchActivityFeed(
|
||||
mangoAccountAddress,
|
||||
offset + PAGINATION_PAGE_LENGTH,
|
||||
queryParams
|
||||
queryParams,
|
||||
)
|
||||
}, [actions, offset, queryParams, mangoAccountAddress])
|
||||
|
||||
|
@ -376,7 +376,7 @@ const ActivityFeedTable = () => {
|
|||
<div className="flex items-center justify-end">
|
||||
<Tooltip
|
||||
content={`View on ${t(
|
||||
`settings:${preferredExplorer.name}`
|
||||
`settings:${preferredExplorer.name}`,
|
||||
)}`}
|
||||
placement="top-end"
|
||||
>
|
||||
|
@ -515,7 +515,7 @@ const MobileActivityFeedItem = ({
|
|||
const { t } = useTranslation(['common', 'activity'])
|
||||
const [preferredExplorer] = useLocalStorageState(
|
||||
PREFERRED_EXPLORER_KEY,
|
||||
EXPLORERS[0]
|
||||
EXPLORERS[0],
|
||||
)
|
||||
const { mangoAccountAddress } = useMangoAccount()
|
||||
const { activity_type, block_datetime } = activity
|
||||
|
|
|
@ -50,7 +50,7 @@ const ActivityFilters = () => {
|
|||
const actions = mangoStore((s) => s.actions)
|
||||
const { mangoAccountAddress } = useMangoAccount()
|
||||
const [advancedFilters, setAdvancedFilters] = useState<AdvancedFilters>(
|
||||
DEFAULT_ADVANCED_FILTERS
|
||||
DEFAULT_ADVANCED_FILTERS,
|
||||
)
|
||||
const [params, setParams] = useState<string[]>([])
|
||||
const { t } = useTranslation(['common', 'activity'])
|
||||
|
@ -64,7 +64,7 @@ const ActivityFilters = () => {
|
|||
if (entry[1].length) {
|
||||
// ETH should be renamed to ETH (Portal) in the database
|
||||
const alignSymbolsToBackend = entry[1].map((e: string) =>
|
||||
e === 'ETH (Portal)' ? 'ETH' : e
|
||||
e === 'ETH (Portal)' ? 'ETH' : e,
|
||||
)
|
||||
advancedParams =
|
||||
advancedParams + `&${entry[0]}=${alignSymbolsToBackend}`
|
||||
|
|
|
@ -2,7 +2,6 @@ import { toUiDecimalsForQuote } from '@blockworks-foundation/mango-v4'
|
|||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useMemo } from 'react'
|
||||
import { Cell, Pie, PieChart } from 'recharts'
|
||||
import { COLORS } from 'styles/colors'
|
||||
|
@ -11,15 +10,16 @@ import FlipNumbers from 'react-flip-numbers'
|
|||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
|
||||
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
|
||||
import useThemeWrapper from 'hooks/useThemeWrapper'
|
||||
|
||||
const AssetsLiabilities = ({ isMobile }: { isMobile: boolean }) => {
|
||||
const { t } = useTranslation('account')
|
||||
const { group } = useMangoGroup()
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const { theme } = useTheme()
|
||||
const { theme } = useThemeWrapper()
|
||||
const [animationSettings] = useLocalStorageState(
|
||||
ANIMATION_SETTINGS_KEY,
|
||||
INITIAL_ANIMATION_SETTINGS
|
||||
INITIAL_ANIMATION_SETTINGS,
|
||||
)
|
||||
|
||||
const [assetsValue, assetsRatio, liabsValue, liabsRatio] = useMemo(() => {
|
||||
|
|
|
@ -54,16 +54,16 @@ const CreateAccountForm = ({
|
|||
group,
|
||||
newAccountNum,
|
||||
name || `Account ${newAccountNum + 1}`,
|
||||
16 // tokenCount
|
||||
16, // tokenCount
|
||||
)
|
||||
if (tx) {
|
||||
const pk = wallet.adapter.publicKey
|
||||
const mangoAccounts = await client.getMangoAccountsForOwner(group, pk!)
|
||||
const reloadedMangoAccounts = await Promise.all(
|
||||
mangoAccounts.map((ma) => ma.reloadSerum3OpenOrders(client))
|
||||
mangoAccounts.map((ma) => ma.reloadSerum3OpenOrders(client)),
|
||||
)
|
||||
const newAccount = mangoAccounts.find(
|
||||
(acc) => acc.accountNum === newAccountNum
|
||||
(acc) => acc.accountNum === newAccountNum,
|
||||
)
|
||||
if (newAccount) {
|
||||
await newAccount.reloadSerum3OpenOrders(client)
|
||||
|
|
|
@ -21,7 +21,6 @@ import {
|
|||
ReferenceLine,
|
||||
ResponsiveContainer,
|
||||
} from 'recharts'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { COLORS } from 'styles/colors'
|
||||
import { formatDateAxis } from '@components/shared/DetailedAreaOrBarChart'
|
||||
import { formatYAxis } from 'utils/formatting'
|
||||
|
@ -32,11 +31,12 @@ import { ArrowLeftIcon, NoSymbolIcon } from '@heroicons/react/20/solid'
|
|||
import { FadeInFadeOut } from '@components/shared/Transitions'
|
||||
import ContentBox from '@components/shared/ContentBox'
|
||||
import SheenLoader from '@components/shared/SheenLoader'
|
||||
import useThemeWrapper from 'hooks/useThemeWrapper'
|
||||
|
||||
const fetchHourlyFunding = async (mangoAccountPk: string) => {
|
||||
try {
|
||||
const data = await fetch(
|
||||
`${MANGO_DATA_API_URL}/stats/funding-account-hourly?mango-account=${mangoAccountPk}`
|
||||
`${MANGO_DATA_API_URL}/stats/funding-account-hourly?mango-account=${mangoAccountPk}`,
|
||||
)
|
||||
const res = await data.json()
|
||||
if (res) {
|
||||
|
@ -65,7 +65,7 @@ const FundingChart = ({ hideChart }: { hideChart: () => void }) => {
|
|||
const { t } = useTranslation('common')
|
||||
const { mangoAccountAddress } = useMangoAccount()
|
||||
const [daysToShow, setDaysToShow] = useState('30')
|
||||
const { theme } = useTheme()
|
||||
const { theme } = useThemeWrapper()
|
||||
const {
|
||||
data: fundingData,
|
||||
isLoading: loadingFunding,
|
||||
|
@ -80,7 +80,7 @@ const FundingChart = ({ hideChart }: { hideChart: () => void }) => {
|
|||
retry: 3,
|
||||
refetchOnWindowFocus: false,
|
||||
enabled: !!mangoAccountAddress,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -100,7 +100,7 @@ const FundingChart = ({ hideChart }: { hideChart: () => void }) => {
|
|||
}
|
||||
const data = rawData.reduce((a: HourlyFundingChartData[], c) => {
|
||||
const found: HourlyFundingChartData | undefined = a.find(
|
||||
(item) => item['time'] === c.time
|
||||
(item) => item['time'] === c.time,
|
||||
)
|
||||
const marketKey = Object.keys(c)[0]
|
||||
const marketFunding = Object.values(c)[0]
|
||||
|
@ -123,7 +123,7 @@ const FundingChart = ({ hideChart }: { hideChart: () => void }) => {
|
|||
if (active && payload && payload.length) {
|
||||
const load = payload[0].payload
|
||||
const data: [string, any][] = Object.entries(load).filter(
|
||||
(p) => p[0] !== 'time' && p[0] !== 'total'
|
||||
(p) => p[0] !== 'time' && p[0] !== 'total',
|
||||
)
|
||||
return (
|
||||
<div className="rounded-md bg-th-bkg-2 p-4">
|
||||
|
|
|
@ -2,7 +2,6 @@ import HealthContributionsChart from './HealthContributionsChart'
|
|||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { HealthType } from '@blockworks-foundation/mango-v4'
|
||||
import {
|
||||
ArrowLeftIcon,
|
||||
NoSymbolIcon,
|
||||
|
@ -16,33 +15,31 @@ import mangoStore from '@store/mangoStore'
|
|||
import TokensHealthTable from './TokensHealthTable'
|
||||
import MarketsHealthTable from './MarketsHealthTable'
|
||||
import { HealthContribution, PerpMarketContribution } from 'types'
|
||||
import useHealthContributions from 'hooks/useHealthContributions'
|
||||
|
||||
const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
||||
const { t } = useTranslation(['common', 'account', 'trade'])
|
||||
const { group } = useMangoGroup()
|
||||
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
|
||||
const [initActiveIndex, setInitActiveIndex] = useState<number | undefined>(
|
||||
undefined
|
||||
undefined,
|
||||
)
|
||||
const [maintActiveIndex, setMaintActiveIndex] = useState<number | undefined>(
|
||||
undefined
|
||||
undefined,
|
||||
)
|
||||
const { initContributions, maintContributions } = useHealthContributions()
|
||||
|
||||
const [initHealthContributions, maintHealthContributions] = useMemo(() => {
|
||||
if (!group || !mangoAccount) return [[], []]
|
||||
const initAssets = mangoAccount.getHealthContributionPerAssetUi(
|
||||
group,
|
||||
HealthType.init
|
||||
)
|
||||
const initContributions = []
|
||||
for (const item of initAssets) {
|
||||
const initHealthContributions = []
|
||||
for (const item of initContributions) {
|
||||
const contribution = item.contribution
|
||||
if (item.asset === 'USDC') {
|
||||
const hasPerp =
|
||||
!!item.contributionDetails?.perpMarketContributions.find(
|
||||
(perp: PerpMarketContribution) => Math.abs(perp.contributionUi) > 0
|
||||
(perp: PerpMarketContribution) => Math.abs(perp.contributionUi) > 0,
|
||||
)
|
||||
initContributions.push({
|
||||
initHealthContributions.push({
|
||||
...item,
|
||||
contribution: Math.abs(contribution),
|
||||
hasPerp: hasPerp,
|
||||
|
@ -53,7 +50,7 @@ const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
|||
.perpMarketContributions) {
|
||||
const contribution = Math.abs(perpMarket.contributionUi)
|
||||
if (contribution > 0) {
|
||||
initContributions.push({
|
||||
initHealthContributions.push({
|
||||
asset: perpMarket.market,
|
||||
contribution: contribution,
|
||||
isAsset: perpMarket.contributionUi > 0 ? true : false,
|
||||
|
@ -62,7 +59,7 @@ const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
initContributions.push({
|
||||
initHealthContributions.push({
|
||||
...item,
|
||||
isAsset: contribution > 0 ? true : false,
|
||||
contribution: Math.abs(contribution),
|
||||
|
@ -70,19 +67,15 @@ const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
|||
}
|
||||
}
|
||||
|
||||
const maintAssets = mangoAccount.getHealthContributionPerAssetUi(
|
||||
group,
|
||||
HealthType.maint
|
||||
)
|
||||
const maintContributions = []
|
||||
for (const item of maintAssets) {
|
||||
const maintHealthContributions = []
|
||||
for (const item of maintContributions) {
|
||||
const contribution = item.contribution
|
||||
if (item.asset === 'USDC') {
|
||||
const hasPerp =
|
||||
!!item.contributionDetails?.perpMarketContributions.find(
|
||||
(perp: PerpMarketContribution) => Math.abs(perp.contributionUi) > 0
|
||||
(perp: PerpMarketContribution) => Math.abs(perp.contributionUi) > 0,
|
||||
)
|
||||
maintContributions.push({
|
||||
maintHealthContributions.push({
|
||||
...item,
|
||||
hasPerp: hasPerp,
|
||||
isAsset: contribution > 0 ? true : false,
|
||||
|
@ -93,7 +86,7 @@ const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
|||
.perpMarketContributions) {
|
||||
const contribution = Math.abs(perpMarket.contributionUi)
|
||||
if (contribution > 0) {
|
||||
maintContributions.push({
|
||||
maintHealthContributions.push({
|
||||
asset: perpMarket.market,
|
||||
contribution: contribution,
|
||||
isAsset: perpMarket.contributionUi > 0 ? true : false,
|
||||
|
@ -102,7 +95,7 @@ const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
maintContributions.push({
|
||||
maintHealthContributions.push({
|
||||
...item,
|
||||
isAsset: contribution > 0 ? true : false,
|
||||
contribution: Math.abs(contribution),
|
||||
|
@ -110,15 +103,15 @@ const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
|||
}
|
||||
}
|
||||
|
||||
return [initContributions, maintContributions]
|
||||
}, [group, mangoAccount])
|
||||
return [initHealthContributions, maintHealthContributions]
|
||||
}, [group, mangoAccount, initContributions, maintContributions])
|
||||
|
||||
const [initHealthMarkets, initHealthTokens] = useMemo(() => {
|
||||
if (!initHealthContributions.length) return [[], []]
|
||||
const splitData = initHealthContributions.reduce(
|
||||
(
|
||||
acc: { market: HealthContribution[]; token: HealthContribution[] },
|
||||
obj: HealthContribution
|
||||
obj: HealthContribution,
|
||||
) => {
|
||||
const isPerp = obj.asset.includes('PERP')
|
||||
const isSpotMarket = obj.asset.includes('/')
|
||||
|
@ -130,7 +123,7 @@ const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
|||
}
|
||||
return acc
|
||||
},
|
||||
{ market: [], token: [] }
|
||||
{ market: [], token: [] },
|
||||
)
|
||||
return [splitData.market, splitData.token]
|
||||
}, [initHealthContributions])
|
||||
|
@ -140,7 +133,7 @@ const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
|||
const splitData = maintHealthContributions.reduce(
|
||||
(
|
||||
acc: { market: HealthContribution[]; token: HealthContribution[] },
|
||||
obj: HealthContribution
|
||||
obj: HealthContribution,
|
||||
) => {
|
||||
const isPerp = obj.asset.includes('PERP')
|
||||
const isSpotMarket = obj.asset.includes('/')
|
||||
|
@ -152,7 +145,7 @@ const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
|||
}
|
||||
return acc
|
||||
},
|
||||
{ market: [], token: [] }
|
||||
{ market: [], token: [] },
|
||||
)
|
||||
const markets = splitData.market.filter((d) => d.contribution > 0)
|
||||
const tokens = splitData.token
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import { useTheme } from 'next-themes'
|
||||
import {
|
||||
Cell,
|
||||
Pie,
|
||||
|
@ -14,6 +13,7 @@ import { formatCurrencyValue } from 'utils/numbers'
|
|||
import { useViewport } from 'hooks/useViewport'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import { HealthContribution } from 'types'
|
||||
import useThemeWrapper from 'hooks/useThemeWrapper'
|
||||
|
||||
const HealthContributionsChart = ({
|
||||
data,
|
||||
|
@ -25,7 +25,7 @@ const HealthContributionsChart = ({
|
|||
setActiveIndex: (i: number | undefined) => void
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'account'])
|
||||
const { theme } = useTheme()
|
||||
const { theme } = useThemeWrapper()
|
||||
const { width } = useViewport()
|
||||
const isMobile = width ? width < breakpoints.sm : false
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ const HistoryTabs = () => {
|
|||
paid: `${formatNumericValue(swap_in_amount, inDecimals)} ${inSymbol}`,
|
||||
received: `${formatNumericValue(
|
||||
swap_out_amount,
|
||||
outDecimals
|
||||
outDecimals,
|
||||
)} ${outSymbol}`,
|
||||
value: formatCurrencyValue(swap_out_price_usd * swap_out_amount),
|
||||
borrow: `${borrowAmount} ${inSymbol}`,
|
||||
|
@ -135,7 +135,7 @@ const HistoryTabs = () => {
|
|||
let market
|
||||
if ('market' in trade) {
|
||||
market = group.getSerum3MarketByExternalMarket(
|
||||
new PublicKey(trade.market)
|
||||
new PublicKey(trade.market),
|
||||
)
|
||||
} else if ('perp_market' in trade) {
|
||||
market = group.getPerpMarketByMarketIndex(trade.market_index)
|
||||
|
|
|
@ -22,7 +22,7 @@ const LiquidationDetails = ({
|
|||
const { t } = useTranslation(['common', 'activity', 'settings'])
|
||||
const [preferredExplorer] = useLocalStorageState(
|
||||
PREFERRED_EXPLORER_KEY,
|
||||
EXPLORERS[0]
|
||||
EXPLORERS[0],
|
||||
)
|
||||
|
||||
const getAssetLiquidatedReturned = (details: SpotOrPerpLiquidationItem) => {
|
||||
|
@ -209,7 +209,7 @@ const LiquidationDetails = ({
|
|||
>
|
||||
<span className="mr-1.5">
|
||||
{abbreviateAddress(
|
||||
new PublicKey(activity.activity_details.counterparty)
|
||||
new PublicKey(activity.activity_details.counterparty),
|
||||
)}
|
||||
</span>
|
||||
<ArrowTopRightOnSquareIcon className="h-3 w-3" />
|
||||
|
|
|
@ -38,13 +38,13 @@ const MangoAccountSummary = () => {
|
|||
const [accountPnl, accountValue, freeCollateral, health] = useMemo(() => {
|
||||
if (!group || !mangoAccount) return [0, 0, 0, 0]
|
||||
const accountPnl = toUiDecimalsForQuote(
|
||||
mangoAccount.getPnl(group).toNumber()
|
||||
mangoAccount.getPnl(group).toNumber(),
|
||||
)
|
||||
const accountValue = toUiDecimalsForQuote(
|
||||
mangoAccount.getEquity(group).toNumber()
|
||||
mangoAccount.getEquity(group).toNumber(),
|
||||
)
|
||||
const freeCollateral = toUiDecimalsForQuote(
|
||||
mangoAccount.getCollateralValue(group).toNumber()
|
||||
mangoAccount.getCollateralValue(group).toNumber(),
|
||||
)
|
||||
const health = mangoAccount.getHealthRatioUi(group, HealthType.maint)
|
||||
return [accountPnl, accountValue, freeCollateral, health]
|
||||
|
@ -53,7 +53,7 @@ const MangoAccountSummary = () => {
|
|||
const leverage = useMemo(() => {
|
||||
if (!group || !mangoAccount) return 0
|
||||
const assetsValue = toUiDecimalsForQuote(
|
||||
mangoAccount.getAssetsValue(group).toNumber()
|
||||
mangoAccount.getAssetsValue(group).toNumber(),
|
||||
)
|
||||
|
||||
if (isNaN(assetsValue / accountValue)) {
|
||||
|
|
|
@ -63,7 +63,7 @@ const MarketsHealthTable = ({
|
|||
const { asset, contribution, isAsset } = cont
|
||||
const market = group.getSerum3MarketByName(asset)
|
||||
const bank = group.banksMapByTokenIndex.get(
|
||||
market.baseTokenIndex
|
||||
market.baseTokenIndex,
|
||||
)?.[0]
|
||||
|
||||
let initAssetWeight = 0
|
||||
|
@ -155,7 +155,7 @@ const MarketsHealthTable = ({
|
|||
const { asset, contribution, isAsset } = cont
|
||||
const market = group.getSerum3MarketByName(asset)
|
||||
const bank = group.banksMapByTokenIndex.get(
|
||||
market.baseTokenIndex
|
||||
market.baseTokenIndex,
|
||||
)?.[0]
|
||||
|
||||
let initAssetWeight = 0
|
||||
|
|
|
@ -19,7 +19,7 @@ const PerpTradeDetails = ({ activity }: { activity: PerpTradeActivity }) => {
|
|||
const { mangoAccountAddress } = useMangoAccount()
|
||||
const [preferredExplorer] = useLocalStorageState(
|
||||
PREFERRED_EXPLORER_KEY,
|
||||
EXPLORERS[0]
|
||||
EXPLORERS[0],
|
||||
)
|
||||
const {
|
||||
maker,
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
ReferenceLine,
|
||||
ResponsiveContainer,
|
||||
} from 'recharts'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { COLORS } from 'styles/colors'
|
||||
import { formatDateAxis } from '@components/shared/DetailedAreaOrBarChart'
|
||||
import { formatYAxis } from 'utils/formatting'
|
||||
|
@ -27,6 +26,7 @@ import SheenLoader from '@components/shared/SheenLoader'
|
|||
import useAccountHourlyVolumeStats from 'hooks/useAccountHourlyVolumeStats'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import { DAILY_MILLISECONDS } from 'utils/constants'
|
||||
import useThemeWrapper from 'hooks/useThemeWrapper'
|
||||
|
||||
const VolumeChart = ({ hideChart }: { hideChart: () => void }) => {
|
||||
const { t } = useTranslation(['account', 'common', 'stats'])
|
||||
|
@ -34,7 +34,7 @@ const VolumeChart = ({ hideChart }: { hideChart: () => void }) => {
|
|||
const { hourlyVolumeData: chartData, loadingHourlyVolume: loading } =
|
||||
useAccountHourlyVolumeStats()
|
||||
const [daysToShow, setDaysToShow] = useState('30')
|
||||
const { theme } = useTheme()
|
||||
const { theme } = useThemeWrapper()
|
||||
|
||||
const CustomTooltip = ({
|
||||
active,
|
||||
|
|
|
@ -44,7 +44,7 @@ const BorrowPage = () => {
|
|||
}
|
||||
const [animationSettings] = useLocalStorageState(
|
||||
ANIMATION_SETTINGS_KEY,
|
||||
INITIAL_ANIMATION_SETTINGS
|
||||
INITIAL_ANIMATION_SETTINGS,
|
||||
)
|
||||
|
||||
const filteredBanks = useMemo(() => {
|
||||
|
@ -58,7 +58,7 @@ const BorrowPage = () => {
|
|||
if (!filteredBanks.length) return 0
|
||||
return filteredBanks.reduce(
|
||||
(a, c) => a + Math.abs(c.borrowedAmount) * c.bank.uiPrice,
|
||||
0
|
||||
0,
|
||||
)
|
||||
}, [filteredBanks])
|
||||
|
||||
|
@ -75,7 +75,7 @@ const BorrowPage = () => {
|
|||
group,
|
||||
usdcBank,
|
||||
mangoAccount,
|
||||
true
|
||||
true,
|
||||
).toNumber()
|
||||
|
||||
if (borrowValue) {
|
||||
|
|
|
@ -26,7 +26,7 @@ import TokenLogo from '@components/shared/TokenLogo'
|
|||
|
||||
export const getAvailableToBorrow = (
|
||||
bankWithBal: BankWithBalance,
|
||||
group: Group
|
||||
group: Group,
|
||||
) => {
|
||||
const { balance, bank, maxBorrow } = bankWithBal
|
||||
const { mint, mintDecimals, minVaultToDepositsRatio } = bankWithBal.bank
|
||||
|
@ -40,7 +40,7 @@ export const getAvailableToBorrow = (
|
|||
|
||||
const available = Decimal.min(
|
||||
availableAccountBorrow.toFixed(bank.mintDecimals),
|
||||
Decimal.max(0, availableVaultBalance.toFixed(mintDecimals))
|
||||
Decimal.max(0, availableVaultBalance.toFixed(mintDecimals)),
|
||||
)
|
||||
return available
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ const YourBorrowsTable = ({ banks }: { banks: BankWithBalance[] }) => {
|
|||
setSelectedToken(token)
|
||||
action === 'borrow' ? setShowBorrowModal(true) : setShowRepayModal(true)
|
||||
},
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
|
@ -17,7 +17,7 @@ const ChatForm = ({
|
|||
const validateMessageText = useCallback(async (text: string) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://www.purgomalum.com/service/json?text=${text}&fill_char=*`
|
||||
`https://www.purgomalum.com/service/json?text=${text}&fill_char=*`,
|
||||
)
|
||||
const profanityCheck = await response.json()
|
||||
|
||||
|
@ -45,7 +45,7 @@ const ChatForm = ({
|
|||
setLatestMessages(newMessages)
|
||||
setMessageText('')
|
||||
},
|
||||
[messageText, messages, publicKey, validateMessageText, setLatestMessages]
|
||||
[messageText, messages, publicKey, validateMessageText, setLatestMessages],
|
||||
)
|
||||
|
||||
const callbackRef = useCallback((inputElement: HTMLInputElement) => {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import { ChangeEvent, useEffect, useRef, useState } from 'react'
|
||||
|
||||
const Slider = ({
|
||||
amount,
|
||||
max,
|
||||
min,
|
||||
onChange,
|
||||
step,
|
||||
}: {
|
||||
amount: number
|
||||
max?: string
|
||||
min?: string
|
||||
onChange: (x: string) => void
|
||||
step: number
|
||||
}) => {
|
||||
const [value, setValue] = useState(0)
|
||||
const inputEl = useRef<HTMLInputElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (inputEl.current) {
|
||||
const target = inputEl.current
|
||||
const min = parseFloat(target.min)
|
||||
const max = parseFloat(target.max)
|
||||
|
||||
target.style.backgroundSize =
|
||||
max - min === 0
|
||||
? '0% 100%'
|
||||
: ((value - min) * 100) / (max - min) + '% 100%'
|
||||
}
|
||||
}, [value])
|
||||
|
||||
useEffect(() => {
|
||||
if (amount) {
|
||||
setValue(amount)
|
||||
} else {
|
||||
setValue(0)
|
||||
}
|
||||
}, [amount])
|
||||
|
||||
const handleSliderChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const target = e.target
|
||||
const min = parseFloat(target.min)
|
||||
const max = parseFloat(target.max)
|
||||
const val = parseFloat(target.value)
|
||||
|
||||
target.style.backgroundSize = ((val - min) * 100) / (max - min) + '% 100%'
|
||||
|
||||
onChange(e.target.value)
|
||||
setValue(parseFloat(e.target.value))
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<label htmlFor="default-range" className="block text-sm"></label>
|
||||
<input
|
||||
ref={inputEl}
|
||||
id="default-range"
|
||||
type="range"
|
||||
min={min || '0'}
|
||||
max={max || '100'}
|
||||
step={step}
|
||||
className="w-full focus:outline-none"
|
||||
onChange={handleSliderChange}
|
||||
value={value}
|
||||
></input>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Slider
|
|
@ -6,6 +6,7 @@ interface SwitchProps {
|
|||
onChange: (x: boolean) => void
|
||||
children?: ReactNode
|
||||
disabled?: boolean
|
||||
small?: boolean
|
||||
}
|
||||
|
||||
const Switch: FunctionComponent<SwitchProps> = ({
|
||||
|
@ -14,6 +15,7 @@ const Switch: FunctionComponent<SwitchProps> = ({
|
|||
children,
|
||||
onChange,
|
||||
disabled,
|
||||
small,
|
||||
}) => {
|
||||
const handleClick = () => {
|
||||
onChange(!checked)
|
||||
|
@ -26,7 +28,9 @@ const Switch: FunctionComponent<SwitchProps> = ({
|
|||
type="button"
|
||||
className={`${
|
||||
checked ? 'bg-th-success' : 'bg-th-bkg-4'
|
||||
} relative inline-flex h-5 w-10 flex-shrink-0 cursor-pointer rounded-full
|
||||
} relative inline-flex ${
|
||||
small ? 'h-4 w-8' : 'h-5 w-10'
|
||||
} flex-shrink-0 cursor-pointer rounded-full
|
||||
border-2 border-transparent transition-colors duration-200 ease-in-out focus-visible:outline-1 focus-visible:outline-offset-2 focus-visible:outline-th-fgd-4 ${
|
||||
disabled ? 'opacity-60' : ''
|
||||
}`}
|
||||
|
@ -39,8 +43,14 @@ const Switch: FunctionComponent<SwitchProps> = ({
|
|||
<span
|
||||
aria-hidden="true"
|
||||
className={`${
|
||||
checked ? 'translate-x-5' : 'translate-x-0'
|
||||
} pointer-events-none inline-block h-4 w-4 rounded-full
|
||||
checked
|
||||
? small
|
||||
? 'translate-x-4'
|
||||
: 'translate-x-5'
|
||||
: 'translate-x-0'
|
||||
} pointer-events-none inline-block ${
|
||||
small ? 'h-3 w-3' : 'h-4 w-4'
|
||||
} rounded-full
|
||||
bg-white shadow ring-0 transition duration-200 ease-in-out`}
|
||||
></span>
|
||||
</button>
|
||||
|
|
|
@ -17,7 +17,7 @@ const GovernanceDelegate = () => {
|
|||
const getCurrentVotingPower = GovernanceStore((s) => s.getCurrentVotingPower)
|
||||
|
||||
const [selectedDelegatePk, setSelectedDelegatePk] = useLocalStorageState(
|
||||
`${publicKey?.toBase58()}${GOVERNANCE_DELEGATE_KEY}`
|
||||
`${publicKey?.toBase58()}${GOVERNANCE_DELEGATE_KEY}`,
|
||||
)
|
||||
const currentDelegate = delegates
|
||||
.find((x) => x.pubkey.toBase58() === selectedDelegatePk)
|
||||
|
|
|
@ -30,7 +30,6 @@ import ListingSuccess from '../ListingSuccess'
|
|||
import { formatTokenSymbol } from 'utils/tokens'
|
||||
import OnBoarding from '../OnBoarding'
|
||||
import { calculateTradingParameters } from 'utils/governance/listingTools'
|
||||
import { useEnhancedWallet } from '@components/wallet/EnhancedWalletProvider'
|
||||
import { tryGetPubKey } from 'utils/governance/tools'
|
||||
|
||||
type FormErrors = Partial<Record<keyof ListMarketForm, string>>
|
||||
|
@ -58,8 +57,8 @@ const defaultFormValues: ListMarketForm = {
|
|||
}
|
||||
|
||||
const ListMarket = ({ goBack }: { goBack: () => void }) => {
|
||||
//do not deconstruct wallet is used for anchor to sign
|
||||
const wallet = useWallet()
|
||||
const { handleConnect } = useEnhancedWallet()
|
||||
const { t } = useTranslation(['governance', 'trade'])
|
||||
const { group } = useMangoGroup()
|
||||
const connection = mangoStore((s) => s.connection)
|
||||
|
@ -91,7 +90,7 @@ const ListMarket = ({ goBack }: { goBack: () => void }) => {
|
|||
const [baseTokens, quoteTokens] = useMemo(() => {
|
||||
if (!group) return [[], []]
|
||||
const allTokens = [...group.banksMapByName.keys()].sort((a, b) =>
|
||||
a.localeCompare(b)
|
||||
a.localeCompare(b),
|
||||
)
|
||||
return [
|
||||
allTokens.filter((t) => t !== quoteToken),
|
||||
|
@ -153,7 +152,7 @@ const ListMarket = ({ goBack }: { goBack: () => void }) => {
|
|||
}
|
||||
return invalidFields
|
||||
},
|
||||
[t]
|
||||
[t],
|
||||
)
|
||||
const handlePropose = useCallback(async () => {
|
||||
const invalidFields = isFormValid(advForm)
|
||||
|
@ -190,7 +189,7 @@ const ListMarket = ({ goBack }: { goBack: () => void }) => {
|
|||
advForm.proposalDescription,
|
||||
index,
|
||||
proposalTx,
|
||||
vsrClient!
|
||||
vsrClient!,
|
||||
)
|
||||
setProposalPk(proposalAddress.toBase58())
|
||||
setCurrentView(VIEWS.SUCCESS)
|
||||
|
@ -256,7 +255,7 @@ const ListMarket = ({ goBack }: { goBack: () => void }) => {
|
|||
baseBank.uiPrice,
|
||||
quoteBank.uiPrice,
|
||||
baseBank.mintDecimals,
|
||||
quoteBank.mintDecimals
|
||||
quoteBank.mintDecimals,
|
||||
)
|
||||
}
|
||||
return {
|
||||
|
@ -290,7 +289,7 @@ const ListMarket = ({ goBack }: { goBack: () => void }) => {
|
|||
{t('market-pair')}{' '}
|
||||
{baseToken && quoteToken
|
||||
? `- ${formatTokenSymbol(baseToken)}/${formatTokenSymbol(
|
||||
quoteToken
|
||||
quoteToken,
|
||||
)}`
|
||||
: null}
|
||||
</h2>
|
||||
|
@ -507,7 +506,7 @@ const ListMarket = ({ goBack }: { goBack: () => void }) => {
|
|||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
handleSetAdvForm(
|
||||
'openBookMarketExternalPk',
|
||||
e.target.value
|
||||
e.target.value,
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
@ -550,7 +549,7 @@ const ListMarket = ({ goBack }: { goBack: () => void }) => {
|
|||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
handleSetAdvForm(
|
||||
'proposalDescription',
|
||||
e.target.value
|
||||
e.target.value,
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
@ -592,7 +591,7 @@ const ListMarket = ({ goBack }: { goBack: () => void }) => {
|
|||
)}
|
||||
</Button>
|
||||
) : (
|
||||
<Button onClick={handleConnect} size="large">
|
||||
<Button onClick={wallet.connect} size="large">
|
||||
{t('connect-wallet')}
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
@ -32,7 +32,6 @@ import Loading from '@components/shared/Loading'
|
|||
import ListingSuccess from '../ListingSuccess'
|
||||
import InlineNotification from '@components/shared/InlineNotification'
|
||||
import { Disclosure } from '@headlessui/react'
|
||||
import { useEnhancedWallet } from '@components/wallet/EnhancedWalletProvider'
|
||||
import { abbreviateAddress } from 'utils/formatting'
|
||||
import { formatNumericValue } from 'utils/numbers'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
|
@ -86,12 +85,12 @@ const defaultTokenListFormValues: TokenListForm = {
|
|||
const TWENTY_K_USDC_BASE = '20000000000'
|
||||
|
||||
const ListToken = ({ goBack }: { goBack: () => void }) => {
|
||||
//do not deconstruct wallet is used for anchor to sign
|
||||
const wallet = useWallet()
|
||||
const { jupiterTokens } = useJupiterMints()
|
||||
const connection = mangoStore((s) => s.connection)
|
||||
const client = mangoStore((s) => s.client)
|
||||
const { group } = useMangoGroup()
|
||||
const { handleConnect } = useEnhancedWallet()
|
||||
const voter = GovernanceStore((s) => s.voter)
|
||||
const vsrClient = GovernanceStore((s) => s.vsrClient)
|
||||
const governances = GovernanceStore((s) => s.governances)
|
||||
|
@ -129,7 +128,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
? governances[MANGO_DAO_WALLET_GOVERNANCE.toBase58()].account.config
|
||||
.minCommunityTokensToCreateProposal
|
||||
: new BN(0),
|
||||
[governances]
|
||||
[governances],
|
||||
) as BN
|
||||
const mintVoterWeightNumber = governances
|
||||
? fmtTokenAmount(minVoterWeight, MANGO_MINT_DECIMALS)
|
||||
|
@ -140,7 +139,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
baseTokenPrice,
|
||||
quoteBank.uiPrice,
|
||||
currentTokenInfo.decimals,
|
||||
quoteBank.mintDecimals
|
||||
quoteBank.mintDecimals,
|
||||
)
|
||||
}
|
||||
return {
|
||||
|
@ -191,7 +190,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
new BN(index).toArrayLike(Buffer, 'le', 2),
|
||||
new BN(bankNum).toArrayLike(Buffer, 'le', 4),
|
||||
],
|
||||
client.programId
|
||||
client.programId,
|
||||
)
|
||||
setAdvForm({
|
||||
...advForm,
|
||||
|
@ -211,7 +210,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
})
|
||||
setLoadingListingParams(false)
|
||||
},
|
||||
[advForm, client.programId, connection, group, mint, proposals]
|
||||
[advForm, client.programId, connection, group, mint, proposals],
|
||||
)
|
||||
|
||||
const handleGetRoutesWithFixedArgs = useCallback(
|
||||
|
@ -230,10 +229,10 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
mode,
|
||||
FEE,
|
||||
walletForCheck,
|
||||
'JUPITER'
|
||||
'JUPITER',
|
||||
)
|
||||
},
|
||||
[wallet.publicKey]
|
||||
[wallet.publicKey],
|
||||
)
|
||||
|
||||
const handleLiqudityCheck = useCallback(
|
||||
|
@ -257,7 +256,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
(acc: { amount: string; priceImpactPct: number }[], val) => {
|
||||
if (val.swapMode === 'ExactIn') {
|
||||
const exactOutRoute = bestRoutesSwaps.find(
|
||||
(x) => x.amount === val.amount && x.swapMode === 'ExactOut'
|
||||
(x) => x.amount === val.amount && x.swapMode === 'ExactOut',
|
||||
)
|
||||
acc.push({
|
||||
amount: val.amount.toString(),
|
||||
|
@ -268,14 +267,14 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
}
|
||||
return acc
|
||||
},
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const midTierCheck = averageSwaps.find(
|
||||
(x) => x.amount === TWENTY_K_USDC_BASE
|
||||
(x) => x.amount === TWENTY_K_USDC_BASE,
|
||||
)
|
||||
const indexForTierFromSwaps = averageSwaps.findIndex(
|
||||
(x) => x?.priceImpactPct && x?.priceImpactPct * 100 < 1
|
||||
(x) => x?.priceImpactPct && x?.priceImpactPct * 100 < 1,
|
||||
)
|
||||
const tier =
|
||||
indexForTierFromSwaps > -1
|
||||
|
@ -286,8 +285,8 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
|
||||
handleGetPoolParams(
|
||||
swaps.find(
|
||||
(x) => x.bestRoute!.amount.toString() === TWENTY_K_USDC_BASE
|
||||
)!.routes
|
||||
(x) => x.bestRoute!.amount.toString() === TWENTY_K_USDC_BASE,
|
||||
)!.routes,
|
||||
)
|
||||
return tier
|
||||
} catch (e) {
|
||||
|
@ -298,7 +297,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
})
|
||||
}
|
||||
},
|
||||
[t, wallet.publicKey]
|
||||
[t, handleGetRoutesWithFixedArgs],
|
||||
)
|
||||
|
||||
const handleGetPoolParams = (routes: never[] | RouteInfo[]) => {
|
||||
|
@ -379,7 +378,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
}
|
||||
return invalidFields
|
||||
},
|
||||
[t]
|
||||
[t],
|
||||
)
|
||||
|
||||
const propose = useCallback(async () => {
|
||||
|
@ -387,7 +386,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
if (Object.keys(invalidFields).length) {
|
||||
return
|
||||
}
|
||||
if (!wallet?.publicKey || !vsrClient || !connectionContext) return
|
||||
if (!wallet.publicKey || !vsrClient || !connectionContext) return
|
||||
await getCurrentVotingPower(wallet.publicKey, vsrClient, connectionContext)
|
||||
|
||||
if (voter.voteWeight.cmp(minVoterWeight) === -1) {
|
||||
|
@ -406,7 +405,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
group!.publicKey.toBuffer(),
|
||||
new PublicKey(advForm.mintPk).toBuffer(),
|
||||
],
|
||||
client.programId
|
||||
client.programId,
|
||||
)
|
||||
|
||||
const proposalTx = []
|
||||
|
@ -437,7 +436,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
Number(tierPreset.liquidationFee),
|
||||
Number(tierPreset.minVaultToDepositsRatio),
|
||||
new BN(tierPreset.netBorrowLimitWindowSizeTs),
|
||||
new BN(tierPreset.netBorrowLimitPerWindowQuote)
|
||||
new BN(tierPreset.netBorrowLimitPerWindowQuote),
|
||||
)
|
||||
.accounts({
|
||||
admin: MANGO_DAO_WALLET,
|
||||
|
@ -475,7 +474,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
false,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
null,
|
||||
)
|
||||
.accounts({
|
||||
oracle: new PublicKey(advForm.oraclePk),
|
||||
|
@ -493,7 +492,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
.instruction()
|
||||
proposalTx.push(editIx)
|
||||
} else {
|
||||
await client!.program.methods
|
||||
const trustlessIx = await client!.program.methods
|
||||
.tokenRegisterTrustless(Number(advForm.tokenIndex), advForm.name)
|
||||
.accounts({
|
||||
mint: new PublicKey(advForm.mintPk),
|
||||
|
@ -504,6 +503,8 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
group: group!.publicKey,
|
||||
})
|
||||
.instruction()
|
||||
|
||||
proposalTx.push(trustlessIx)
|
||||
}
|
||||
|
||||
const registerMarketix = await client!.program.methods
|
||||
|
@ -532,7 +533,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
advForm.proposalDescription,
|
||||
advForm.tokenIndex,
|
||||
proposalTx,
|
||||
vsrClient
|
||||
vsrClient,
|
||||
)
|
||||
setProposalPk(proposalAddress.toBase58())
|
||||
} catch (e) {
|
||||
|
@ -649,7 +650,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
<p>{t('mint')}</p>
|
||||
<p className="flex items-center">
|
||||
{abbreviateAddress(
|
||||
new PublicKey(currentTokenInfo?.address)
|
||||
new PublicKey(currentTokenInfo?.address),
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -740,7 +741,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
handleSetAdvForm(
|
||||
'openBookMarketExternalPk',
|
||||
e.target.value
|
||||
e.target.value,
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
@ -802,7 +803,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
handleSetAdvForm(
|
||||
'openBookProgram',
|
||||
e.target.value
|
||||
e.target.value,
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
@ -843,7 +844,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
handleSetAdvForm(
|
||||
'proposalTitle',
|
||||
e.target.value
|
||||
e.target.value,
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
@ -867,7 +868,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
handleSetAdvForm(
|
||||
'proposalDescription',
|
||||
e.target.value
|
||||
e.target.value,
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
@ -982,7 +983,7 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
)}
|
||||
</Button>
|
||||
) : (
|
||||
<Button onClick={handleConnect} size="large">
|
||||
<Button onClick={wallet.connect} size="large">
|
||||
{t('connect-wallet')}
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
@ -23,7 +23,7 @@ const OnBoarding = ({ minVotes }: { minVotes?: BN }) => {
|
|||
? new BN(
|
||||
governances[
|
||||
MANGO_DAO_WALLET_GOVERNANCE.toBase58()
|
||||
].account.config.minCommunityTokensToCreateProposal.toString()
|
||||
].account.config.minCommunityTokensToCreateProposal.toString(),
|
||||
)
|
||||
: new BN(0)
|
||||
const mintVoterWeightNumber = governances
|
||||
|
@ -50,7 +50,7 @@ const OnBoarding = ({ minVotes }: { minVotes?: BN }) => {
|
|||
</>
|
||||
}
|
||||
desc={`${t('on-boarding-deposit-info')} ${t(
|
||||
'on-boarding-description-1'
|
||||
'on-boarding-description-1',
|
||||
)}`}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -58,7 +58,7 @@ const ProposalCard = ({
|
|||
const [voteType, setVoteType] = useState<VoteKind | undefined>(undefined)
|
||||
|
||||
const [voteRecordAddress, setVoteRecordAddress] = useState<PublicKey | null>(
|
||||
null
|
||||
null,
|
||||
)
|
||||
const [isVoteCast, setIsVoteCast] = useState(false)
|
||||
const [description, setDescription] = useState('')
|
||||
|
@ -73,7 +73,7 @@ const ProposalCard = ({
|
|||
setProcessedVoteType(
|
||||
voteType === VoteKind.Approve
|
||||
? PROCESSED_VOTE_TYPE.APPROVE
|
||||
: PROCESSED_VOTE_TYPE.DENY
|
||||
: PROCESSED_VOTE_TYPE.DENY,
|
||||
)
|
||||
try {
|
||||
await castVote(
|
||||
|
@ -83,7 +83,7 @@ const ProposalCard = ({
|
|||
voter.tokenOwnerRecord!,
|
||||
voteType,
|
||||
vsrClient!,
|
||||
client
|
||||
client,
|
||||
)
|
||||
await updateProposals(proposal.pubkey)
|
||||
} catch (e) {
|
||||
|
@ -106,7 +106,7 @@ const ProposalCard = ({
|
|||
proposal,
|
||||
voter.tokenOwnerRecord!,
|
||||
client,
|
||||
voteRecordAddress!
|
||||
voteRecordAddress!,
|
||||
)
|
||||
await updateProposals(proposal.pubkey)
|
||||
} catch (e) {
|
||||
|
@ -140,14 +140,14 @@ const ProposalCard = ({
|
|||
const voteRecordAddress = await getVoteRecordAddress(
|
||||
MANGO_GOVERNANCE_PROGRAM,
|
||||
proposal.pubkey,
|
||||
voter.tokenOwnerRecord!.pubkey!
|
||||
voter.tokenOwnerRecord!.pubkey!,
|
||||
)
|
||||
setVoteRecordAddress(voteRecordAddress)
|
||||
try {
|
||||
const governanceAccount = await getGovernanceAccount(
|
||||
connection,
|
||||
voteRecordAddress,
|
||||
VoteRecord
|
||||
VoteRecord,
|
||||
)
|
||||
setIsVoteCast(true)
|
||||
setVoteType(governanceAccount.account.vote?.voteType)
|
||||
|
|
|
@ -89,7 +89,7 @@ const Vote = () => {
|
|||
proposal={x}
|
||||
mangoMint={mangoMint}
|
||||
></ProposalCard>
|
||||
)
|
||||
),
|
||||
)
|
||||
) : (
|
||||
<div className="flex h-56 items-center justify-center rounded-lg border border-th-bkg-3 p-6">
|
||||
|
|
|
@ -26,7 +26,7 @@ const QuorumProgress = ({ governance, proposal, communityMint }: Props) => {
|
|||
realm &&
|
||||
getMintMaxVoteWeight(
|
||||
communityMint,
|
||||
realm.account.config.communityMintMaxVoteWeightSource
|
||||
realm.account.config.communityMintMaxVoteWeightSource,
|
||||
)
|
||||
|
||||
const minimumYesVotes =
|
||||
|
@ -35,7 +35,7 @@ const QuorumProgress = ({ governance, proposal, communityMint }: Props) => {
|
|||
|
||||
const yesVoteCount = fmtTokenAmount(
|
||||
new BN(proposal.account.getYesVoteCount().toString()),
|
||||
communityMint.decimals
|
||||
communityMint.decimals,
|
||||
)
|
||||
|
||||
const rawYesVotesRequired = minimumYesVotes - yesVoteCount
|
||||
|
|
|
@ -15,11 +15,11 @@ const VoteResults = ({ proposal, communityMint }: VoteResultsProps) => {
|
|||
|
||||
const yesVoteCount = fmtTokenAmount(
|
||||
proposal.getYesVoteCount() as BN,
|
||||
communityMint.decimals
|
||||
communityMint.decimals,
|
||||
)
|
||||
const noVoteCount = fmtTokenAmount(
|
||||
proposal.getNoVoteCount() as BN,
|
||||
communityMint.decimals
|
||||
communityMint.decimals,
|
||||
)
|
||||
const totalVoteCount = yesVoteCount + noVoteCount
|
||||
const getRelativeVoteCount = (voteCount: number) =>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useTheme } from 'next-themes'
|
||||
import useThemeWrapper from 'hooks/useThemeWrapper'
|
||||
import { COLORS } from 'styles/colors'
|
||||
|
||||
const OrderbookIcon = ({
|
||||
|
@ -8,7 +8,7 @@ const OrderbookIcon = ({
|
|||
side: 'buy' | 'sell'
|
||||
className?: string
|
||||
}) => {
|
||||
const { theme } = useTheme()
|
||||
const { theme } = useThemeWrapper()
|
||||
const buyColor = side === 'buy' ? COLORS.UP[theme] : COLORS.FGD4[theme]
|
||||
const sellColor = side === 'sell' ? COLORS.DOWN[theme] : COLORS.FGD4[theme]
|
||||
return (
|
||||
|
|
|
@ -23,7 +23,7 @@ export interface LeaderboardRes {
|
|||
type DaysToShow = '1DAY' | '1WEEK' | 'ALLTIME'
|
||||
|
||||
const isLeaderboard = (
|
||||
response: null | EmptyObject | LeaderboardRes[]
|
||||
response: null | EmptyObject | LeaderboardRes[],
|
||||
): response is LeaderboardRes[] => {
|
||||
if (response && Array.isArray(response)) {
|
||||
return true
|
||||
|
@ -33,10 +33,10 @@ const isLeaderboard = (
|
|||
|
||||
const fetchLeaderboard = async (
|
||||
daysToShow: DaysToShow,
|
||||
offset = 0
|
||||
offset = 0,
|
||||
): Promise<Array<LeaderboardRes>> => {
|
||||
const data = await fetch(
|
||||
`${MANGO_DATA_API_URL}/leaderboard-pnl?over_period=${daysToShow}&offset=${offset}`
|
||||
`${MANGO_DATA_API_URL}/leaderboard-pnl?over_period=${daysToShow}&offset=${offset}`,
|
||||
)
|
||||
const parsedData: null | EmptyObject | LeaderboardRes[] = await data.json()
|
||||
|
||||
|
@ -63,7 +63,7 @@ const LeaderboardPage = () => {
|
|||
refetchOnWindowFocus: false,
|
||||
keepPreviousData: true,
|
||||
getNextPageParam: (_lastPage, pages) => pages.length * 20,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const leaderboardData = useMemo(() => {
|
||||
|
|
|
@ -27,11 +27,11 @@ const CloseAccountModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
const mangoAccount = mangoStore((s) => s.mangoAccount)
|
||||
const perpPositions = mangoStore((s) => s.mangoAccount.perpPositions)
|
||||
const openPerpPositions = Object.values(perpPositions).filter((p) =>
|
||||
p.basePositionLots.toNumber()
|
||||
p.basePositionLots.toNumber(),
|
||||
)
|
||||
const group = mangoStore.getState().group
|
||||
const unsettledBalances = Object.values(mangoAccount.spotBalances).filter(
|
||||
(x) => x.unsettled && x.unsettled > 0
|
||||
(x) => x.unsettled && x.unsettled > 0,
|
||||
)
|
||||
const unsettledPerpPositions = useUnsettledPerpPositions()
|
||||
const [hasBorrows, setHasBorrows] = useState(false)
|
||||
|
@ -49,7 +49,7 @@ const CloseAccountModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
const tx = await client.emptyAndCloseMangoAccount(group, mangoAccount)
|
||||
if (tx) {
|
||||
const newMangoAccounts = mangoAccounts.filter(
|
||||
(ma) => !ma.publicKey.equals(mangoAccount.publicKey)
|
||||
(ma) => !ma.publicKey.equals(mangoAccount.publicKey),
|
||||
)
|
||||
let newCurrentAccount: MangoAccount
|
||||
if (newMangoAccounts[0]) {
|
||||
|
@ -99,7 +99,7 @@ const CloseAccountModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
.filter((token: TokenPosition) =>
|
||||
token
|
||||
.balance(group.getFirstBankByTokenIndex(token.tokenIndex))
|
||||
.isNeg()
|
||||
.isNeg(),
|
||||
).length
|
||||
) {
|
||||
setHasBorrows(true)
|
||||
|
@ -137,8 +137,8 @@ const CloseAccountModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
mangoAccount && group
|
||||
? formatCurrencyValue(
|
||||
toUiDecimalsForQuote(
|
||||
mangoAccount!.current!.getEquity(group)
|
||||
)
|
||||
mangoAccount!.current!.getEquity(group),
|
||||
),
|
||||
)
|
||||
: 0,
|
||||
})}
|
||||
|
|
|
@ -13,7 +13,6 @@ import { LAMPORTS_PER_SOL, PublicKey, Transaction } from '@solana/web3.js'
|
|||
import { MARKET_STATE_LAYOUT_V2 } from '@project-serum/serum'
|
||||
import { notify } from 'utils/notifications'
|
||||
import InlineNotification from '@components/shared/InlineNotification'
|
||||
import { useEnhancedWallet } from '@components/wallet/EnhancedWalletProvider'
|
||||
|
||||
type CreateObMarketForm = {
|
||||
programId: string
|
||||
|
@ -63,8 +62,7 @@ const CreateOpenbookMarketModal = ({
|
|||
}: ModalProps & CreateOpenbookMarketModalProps) => {
|
||||
const { t } = useTranslation(['governance'])
|
||||
const connection = mangoStore((s) => s.connection)
|
||||
const wallet = useWallet()
|
||||
const { handleConnect } = useEnhancedWallet()
|
||||
const { connect, signAllTransactions, connected, publicKey } = useWallet()
|
||||
|
||||
const [form, setForm] = useState({ ...defaultFormValues })
|
||||
const [formErrors, setFormErrors] = useState<FormErrors>({})
|
||||
|
@ -77,7 +75,7 @@ const CreateOpenbookMarketModal = ({
|
|||
}
|
||||
|
||||
const handleCreate = async () => {
|
||||
if (!wallet || !wallet.signAllTransactions) {
|
||||
if (!publicKey || !signAllTransactions) {
|
||||
return
|
||||
}
|
||||
let sig = ''
|
||||
|
@ -85,7 +83,7 @@ const CreateOpenbookMarketModal = ({
|
|||
try {
|
||||
const ixObj = await makeCreateOpenBookMarketInstructionSimple({
|
||||
connection,
|
||||
wallet: wallet.publicKey!,
|
||||
wallet: publicKey,
|
||||
baseInfo: {
|
||||
mint: new PublicKey(baseMint),
|
||||
decimals: baseDecimals,
|
||||
|
@ -107,11 +105,11 @@ const CreateOpenbookMarketModal = ({
|
|||
tx.add(...chunk.instructions)
|
||||
tx.lastValidBlockHeight = latestBlockhash.lastValidBlockHeight
|
||||
tx.recentBlockhash = latestBlockhash.blockhash
|
||||
tx.feePayer = wallet.publicKey!
|
||||
tx.feePayer = publicKey
|
||||
tx.sign(...chunk.signers)
|
||||
transactions.push(tx)
|
||||
}
|
||||
const signedTransactions = await wallet.signAllTransactions(transactions)
|
||||
const signedTransactions = await signAllTransactions(transactions)
|
||||
|
||||
for (const tx of signedTransactions) {
|
||||
const rawTransaction = tx.serialize()
|
||||
|
@ -159,11 +157,11 @@ const CreateOpenbookMarketModal = ({
|
|||
const getMinLamportsToCreateMarket = async () => {
|
||||
const accountsSpace = 84522 + MARKET_STATE_LAYOUT_V2.span
|
||||
const minLamports = await connection.getMinimumBalanceForRentExemption(
|
||||
accountsSpace
|
||||
accountsSpace,
|
||||
)
|
||||
setSolNeededToCreateMarket(
|
||||
Math.round((minLamports / LAMPORTS_PER_SOL + Number.EPSILON) * 100) /
|
||||
100
|
||||
100,
|
||||
)
|
||||
}
|
||||
getMinLamportsToCreateMarket()
|
||||
|
@ -257,7 +255,7 @@ const CreateOpenbookMarketModal = ({
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
{wallet.connected ? (
|
||||
{connected ? (
|
||||
<Button
|
||||
className="mt-6 w-full"
|
||||
onClick={handleCreate}
|
||||
|
@ -267,7 +265,7 @@ const CreateOpenbookMarketModal = ({
|
|||
{t('create')}
|
||||
</Button>
|
||||
) : (
|
||||
<Button className="mt-6 w-full" onClick={handleConnect} size="large">
|
||||
<Button className="mt-6 w-full" onClick={connect} size="large">
|
||||
{t('connect-wallet')}
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
@ -12,7 +12,7 @@ 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 chunk from 'lodash/chunk'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { notify } from 'utils/notifications'
|
||||
import { isMangoError } from 'types'
|
||||
|
@ -135,7 +135,7 @@ const CreateSwitchboardOracleModal = ({
|
|||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}),
|
||||
).finish(),
|
||||
},
|
||||
{
|
||||
|
@ -201,7 +201,7 @@ const CreateSwitchboardOracleModal = ({
|
|||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}),
|
||||
).finish(),
|
||||
},
|
||||
],
|
||||
|
|
|
@ -22,7 +22,7 @@ const DelegateModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
const [delegateAddress, setDelegateAddress] = useState(
|
||||
mangoAccount?.delegate?.toString() !== DEFAULT_DELEGATE
|
||||
? mangoAccount!.delegate.toString()
|
||||
: ''
|
||||
: '',
|
||||
)
|
||||
|
||||
const handleDelegateAccount = async (address: string) => {
|
||||
|
@ -44,7 +44,7 @@ const DelegateModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
group,
|
||||
mangoAccount,
|
||||
undefined,
|
||||
delegateAddress ? new PublicKey(address) : undefined
|
||||
delegateAddress ? new PublicKey(address) : undefined,
|
||||
)
|
||||
onClose()
|
||||
notify({
|
||||
|
@ -82,7 +82,7 @@ const DelegateModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
<InlineNotification
|
||||
type="info"
|
||||
desc={`Account is delegated to ${abbreviateAddress(
|
||||
mangoAccount.delegate
|
||||
mangoAccount.delegate,
|
||||
)}`}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -117,11 +117,11 @@ const MangoAccountsListModal = ({
|
|||
acc = mangoAccount
|
||||
}
|
||||
const accountValue = toUiDecimalsForQuote(
|
||||
Number(acc.getEquity(group!))
|
||||
Number(acc.getEquity(group!)),
|
||||
).toFixed(2)
|
||||
const maintHealth = acc.getHealthRatioUi(
|
||||
group!,
|
||||
HealthType.maint
|
||||
HealthType.maint,
|
||||
)
|
||||
return (
|
||||
<div
|
||||
|
@ -207,7 +207,7 @@ const MangoAccountsListModal = ({
|
|||
acc,
|
||||
t('copy-address-success', {
|
||||
pk: abbreviateAddress(acc.publicKey),
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
hideBg
|
||||
|
|
|
@ -51,12 +51,12 @@ const ModifyTvOrderModal = ({
|
|||
const { t } = useTranslation(['common', 'trade'])
|
||||
const [modifiedOrderPrice, setModifiedOrderPrice] = useState(price)
|
||||
const [modifiedOrderSize, setModifiedOrderSize] = useState(
|
||||
order.size.toString()
|
||||
order.size.toString(),
|
||||
)
|
||||
const { baseSymbol, selectedMarket, serumOrPerpMarket } = useSelectedMarket()
|
||||
const [savedCheckboxSettings] = useLocalStorageState(
|
||||
TRADE_CHECKBOXES_KEY,
|
||||
DEFAULT_CHECKBOX_SETTINGS
|
||||
DEFAULT_CHECKBOX_SETTINGS,
|
||||
)
|
||||
|
||||
const tickDecimals = useMemo(() => {
|
||||
|
@ -99,13 +99,13 @@ const ModifyTvOrderModal = ({
|
|||
Date.now(),
|
||||
PerpOrderType.limit,
|
||||
undefined,
|
||||
undefined
|
||||
undefined,
|
||||
)
|
||||
} else {
|
||||
const marketPk = findSerum3MarketPkInOpenOrders(o)
|
||||
if (!marketPk) return
|
||||
const market = group.getSerum3MarketByExternalMarket(
|
||||
new PublicKey(marketPk)
|
||||
new PublicKey(marketPk),
|
||||
)
|
||||
tx = await client.modifySerum3Order(
|
||||
group,
|
||||
|
@ -118,7 +118,7 @@ const ModifyTvOrderModal = ({
|
|||
Serum3SelfTradeBehavior.decrementTake,
|
||||
Serum3OrderType.limit,
|
||||
Date.now(),
|
||||
10
|
||||
10,
|
||||
)
|
||||
}
|
||||
actions.fetchOpenOrders()
|
||||
|
@ -139,7 +139,7 @@ const ModifyTvOrderModal = ({
|
|||
})
|
||||
}
|
||||
},
|
||||
[findSerum3MarketPkInOpenOrders, modifiedOrderPrice, modifiedOrderSize]
|
||||
[findSerum3MarketPkInOpenOrders, modifiedOrderPrice, modifiedOrderSize],
|
||||
)
|
||||
|
||||
return selectedMarket ? (
|
||||
|
|
|
@ -129,7 +129,7 @@ const SharePositionModal = ({
|
|||
<p className="ml-2 font-mono text-base text-white">
|
||||
{formatNumericValue(
|
||||
entryPrice,
|
||||
getDecimalCount(market.tickSize)
|
||||
getDecimalCount(market.tickSize),
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -140,7 +140,7 @@ const SharePositionModal = ({
|
|||
<p className="ml-2 font-mono text-base text-white">
|
||||
{formatNumericValue(
|
||||
market.uiPrice,
|
||||
getDecimalCount(market.tickSize)
|
||||
getDecimalCount(market.tickSize),
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -26,11 +26,11 @@ const TradeVolumeAlertModal = ({ isOpen, onClose }: ModalProps) => {
|
|||
const { t } = useTranslation(['common', 'trade'])
|
||||
const [soundSettings, setSoundSettings] = useLocalStorageState(
|
||||
SOUND_SETTINGS_KEY,
|
||||
INITIAL_SOUND_SETTINGS
|
||||
INITIAL_SOUND_SETTINGS,
|
||||
)
|
||||
const [alertSettings, setAlertSettings] = useLocalStorageState(
|
||||
TRADE_VOLUME_ALERT_KEY,
|
||||
DEFAULT_VOLUME_ALERT_SETTINGS
|
||||
DEFAULT_VOLUME_ALERT_SETTINGS,
|
||||
)
|
||||
const [formValues, setFormValues] = useState(alertSettings)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
ArrowDownTrayIcon,
|
||||
ArrowTopRightOnSquareIcon,
|
||||
CheckCircleIcon,
|
||||
ChevronDownIcon,
|
||||
ExclamationCircleIcon,
|
||||
PencilIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
|
@ -26,7 +27,6 @@ import ActionTokenList from '../account/ActionTokenList'
|
|||
import ButtonGroup from '../forms/ButtonGroup'
|
||||
import Input from '../forms/Input'
|
||||
import Label from '../forms/Label'
|
||||
import WalletIcon from '../icons/WalletIcon'
|
||||
// import ParticlesBackground from '../ParticlesBackground'
|
||||
// import EditNftProfilePic from '../profile/EditNftProfilePic'
|
||||
// import EditProfileForm from '../profile/EditProfileForm'
|
||||
|
@ -35,7 +35,6 @@ import InlineNotification from '../shared/InlineNotification'
|
|||
import Loading from '../shared/Loading'
|
||||
import MaxAmountButton from '../shared/MaxAmountButton'
|
||||
import SolBalanceWarnings from '../shared/SolBalanceWarnings'
|
||||
import { useEnhancedWallet } from '../wallet/EnhancedWalletProvider'
|
||||
import Modal from '../shared/Modal'
|
||||
import NumberFormat, { NumberFormatValues } from 'react-number-format'
|
||||
import { withValueLimit } from '@components/swap/SwapForm'
|
||||
|
@ -46,6 +45,7 @@ import ColorBlur from '@components/ColorBlur'
|
|||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import { ACCEPT_TERMS_KEY } from 'utils/constants'
|
||||
import { ACCOUNT_ACTIONS_NUMBER_FORMAT_CLASSES } from '@components/BorrowForm'
|
||||
import { WalletReadyState } from '@solana/wallet-adapter-base'
|
||||
|
||||
const UserSetupModal = ({
|
||||
isOpen,
|
||||
|
@ -55,7 +55,7 @@ const UserSetupModal = ({
|
|||
onClose: () => void
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'onboarding', 'swap'])
|
||||
const { connected, select, wallet, wallets, publicKey } = useWallet()
|
||||
const { connected, select, wallet, wallets, publicKey, connect } = useWallet()
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const mangoAccountLoading = mangoStore((s) => s.mangoAccount.initialLoad)
|
||||
const [accountName, setAccountName] = useState('')
|
||||
|
@ -66,10 +66,29 @@ const UserSetupModal = ({
|
|||
const [submitDeposit, setSubmitDeposit] = useState(false)
|
||||
const [sizePercentage, setSizePercentage] = useState('')
|
||||
// const [showEditProfilePic, setShowEditProfilePic] = useState(false)
|
||||
const { handleConnect } = useEnhancedWallet()
|
||||
const { maxSolDeposit } = useSolBalance()
|
||||
const banks = useBanksWithBalances('walletBalance')
|
||||
const [, setAcceptTerms] = useLocalStorageState(ACCEPT_TERMS_KEY, '')
|
||||
const [walletsToDisplay, setWalletstoDisplay] = useState<'default' | 'all'>(
|
||||
'default',
|
||||
)
|
||||
|
||||
const walletsDisplayed = useMemo(() => {
|
||||
const firstFive = wallets.slice(0, 5)
|
||||
const detectedWallets = wallets.filter(
|
||||
(w) =>
|
||||
w.readyState === WalletReadyState.Installed ||
|
||||
w.readyState === WalletReadyState.Loadable,
|
||||
)
|
||||
|
||||
if (walletsToDisplay === 'default') {
|
||||
return detectedWallets.length > firstFive.length
|
||||
? detectedWallets
|
||||
: firstFive
|
||||
} else {
|
||||
return wallets
|
||||
}
|
||||
}, [walletsToDisplay, wallets])
|
||||
|
||||
useEffect(() => {
|
||||
if (connected) {
|
||||
|
@ -88,7 +107,7 @@ const UserSetupModal = ({
|
|||
group,
|
||||
0,
|
||||
accountName || 'Account 1',
|
||||
16 // tokenCount
|
||||
16, // tokenCount
|
||||
)
|
||||
actions.fetchMangoAccounts(publicKey)
|
||||
if (tx) {
|
||||
|
@ -128,7 +147,7 @@ const UserSetupModal = ({
|
|||
group,
|
||||
mangoAccount,
|
||||
bank.mint,
|
||||
parseFloat(depositAmount)
|
||||
parseFloat(depositAmount),
|
||||
)
|
||||
notify({
|
||||
title: 'Transaction confirmed',
|
||||
|
@ -178,7 +197,7 @@ const UserSetupModal = ({
|
|||
const setMax = useCallback(() => {
|
||||
const max = new Decimal(tokenMax.amount).toDecimalPlaces(
|
||||
tokenMax.decimals,
|
||||
Decimal.ROUND_FLOOR
|
||||
Decimal.ROUND_FLOOR,
|
||||
)
|
||||
setDepositAmount(max.toString())
|
||||
setSizePercentage('100')
|
||||
|
@ -193,7 +212,7 @@ const UserSetupModal = ({
|
|||
.toDecimalPlaces(tokenMax.decimals, Decimal.ROUND_FLOOR)
|
||||
setDepositAmount(amount.toString())
|
||||
},
|
||||
[tokenMax]
|
||||
[tokenMax],
|
||||
)
|
||||
|
||||
const handleNextStep = () => {
|
||||
|
@ -279,44 +298,49 @@ const UserSetupModal = ({
|
|||
{t('onboarding:choose-wallet')}
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
{wallets?.map((w) => (
|
||||
{walletsDisplayed?.map((w) => (
|
||||
<button
|
||||
className={`col-span-1 w-full rounded-md border py-3 px-4 text-base font-normal focus:outline-none md:hover:cursor-pointer md:hover:border-th-fgd-4 ${
|
||||
w.adapter.name === wallet?.adapter.name
|
||||
? 'border-th-active text-th-fgd-1 md:hover:border-th-active'
|
||||
: 'border-th-bkg-4 text-th-fgd-4'
|
||||
: 'border-th-bkg-4 text-th-fgd-2'
|
||||
}`}
|
||||
onClick={() => {
|
||||
if (wallet) {
|
||||
connect()
|
||||
}
|
||||
select(w.adapter.name)
|
||||
}}
|
||||
key={w.adapter.name}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
src={w.adapter.icon}
|
||||
className="mr-2 h-5 w-5"
|
||||
alt={`${w.adapter.name} icon`}
|
||||
/>
|
||||
{w.adapter.name}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
src={w.adapter.icon}
|
||||
className="mr-2 h-5 w-5"
|
||||
alt={`${w.adapter.name} icon`}
|
||||
/>
|
||||
<div className="ml-2">{w.adapter.name}</div>
|
||||
</div>
|
||||
{w.readyState === WalletReadyState.Installed ||
|
||||
w.readyState === WalletReadyState.Loadable ? (
|
||||
<div className="text-xs">Detected</div>
|
||||
) : null}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<Button
|
||||
className="mt-10 flex items-center justify-center"
|
||||
onClick={handleConnect}
|
||||
size="large"
|
||||
>
|
||||
{connected && mangoAccountLoading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<div className="flex items-center justify-center">
|
||||
<WalletIcon className="mr-2 h-5 w-5" />
|
||||
|
||||
{t('onboarding:connect-wallet')}
|
||||
{walletsToDisplay !== 'all' ? (
|
||||
<button
|
||||
className="mt-4 flex w-full items-center justify-center text-base text-th-fgd-3 hover:text-th-fgd-1"
|
||||
onClick={() => setWalletstoDisplay('all')}
|
||||
>
|
||||
<div>More</div>
|
||||
<div>
|
||||
<ChevronDownIcon className={`h-5 w-5 flex-shrink-0`} />
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</UserSetupTransition>
|
||||
|
@ -435,7 +459,7 @@ const UserSetupModal = ({
|
|||
value={depositAmount}
|
||||
onValueChange={(e: NumberFormatValues) => {
|
||||
setDepositAmount(
|
||||
!Number.isNaN(Number(e.value)) ? e.value : ''
|
||||
!Number.isNaN(Number(e.value)) ? e.value : '',
|
||||
)
|
||||
}}
|
||||
isAllowed={withValueLimit}
|
||||
|
|
|
@ -22,7 +22,7 @@ import { notify } from 'utils/notifications'
|
|||
|
||||
export const createSolanaMessage = (
|
||||
wallet: WalletContextState,
|
||||
setCookie: (wallet: string, token: string) => void
|
||||
setCookie: (wallet: string, token: string) => void,
|
||||
) => {
|
||||
const payload = new Payload()
|
||||
payload.domain = window.location.host
|
||||
|
@ -121,7 +121,7 @@ const NotificationsDrawer = ({
|
|||
})
|
||||
}
|
||||
},
|
||||
[NOTIFICATION_API, headers]
|
||||
[NOTIFICATION_API, headers],
|
||||
)
|
||||
|
||||
const remove = useCallback(
|
||||
|
@ -136,7 +136,7 @@ const NotificationsDrawer = ({
|
|||
body: JSON.stringify({
|
||||
ids: ids,
|
||||
}),
|
||||
}
|
||||
},
|
||||
)
|
||||
const body = await resp.json()
|
||||
const error = body.error
|
||||
|
@ -158,7 +158,7 @@ const NotificationsDrawer = ({
|
|||
}
|
||||
setIsRemoving(false)
|
||||
},
|
||||
[NOTIFICATION_API, headers]
|
||||
[NOTIFICATION_API, headers],
|
||||
)
|
||||
|
||||
// Mark all notifications as seen when the inbox is opened
|
||||
|
@ -241,7 +241,7 @@ const NotificationsDrawer = ({
|
|||
<CalendarIcon className="mr-1 h-3.5 w-3.5" />
|
||||
<p className="text-xs">
|
||||
{dayjs(notification.createdAt).format(
|
||||
'DD MMM YYYY, h:mma'
|
||||
'DD MMM YYYY, h:mma',
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -22,6 +22,16 @@ import { EXPLORERS } from '@components/settings/PreferredExplorerSettings'
|
|||
|
||||
const setMangoStore = mangoStore.getState().set
|
||||
|
||||
function parseDescription(description: string | null | undefined) {
|
||||
if (
|
||||
description?.includes('{"err":{"InstructionError":[2,{"Custom":6001}]}}')
|
||||
) {
|
||||
return 'Your max slippage tolerance was exceeded'
|
||||
}
|
||||
|
||||
return description
|
||||
}
|
||||
|
||||
const TransactionNotificationList = () => {
|
||||
const { t } = useTranslation()
|
||||
const transactionNotifications = mangoStore((s) => s.transactionNotifications)
|
||||
|
@ -29,7 +39,7 @@ const TransactionNotificationList = () => {
|
|||
const notEnoughSoLMessage = t('deposit-more-sol')
|
||||
const [notificationPosition] = useLocalStorageState(
|
||||
NOTIFICATION_POSITION_KEY,
|
||||
'bottom-left'
|
||||
'bottom-left',
|
||||
)
|
||||
const [mounted, setMounted] = useState(false)
|
||||
const { maxSolDeposit } = useSolBalance()
|
||||
|
@ -39,10 +49,10 @@ const TransactionNotificationList = () => {
|
|||
useEffect(() => {
|
||||
if (transactionNotifications.length) {
|
||||
const customErrorNotification = transactionNotifications.find(
|
||||
(n) => n.description && n.description.includes('"Custom":1')
|
||||
(n) => n.description && n.description.includes('"Custom":1'),
|
||||
)
|
||||
const notEnoughSolNotification = transactionNotifications.find(
|
||||
(n) => n.title && n.title.includes(notEnoughSoLMessage)
|
||||
(n) => n.title && n.title.includes(notEnoughSoLMessage),
|
||||
)
|
||||
|
||||
if (
|
||||
|
@ -115,11 +125,11 @@ const TransactionNotification = ({
|
|||
}) => {
|
||||
const [notificationPosition] = useLocalStorageState(
|
||||
NOTIFICATION_POSITION_KEY,
|
||||
'Bottom-Left'
|
||||
'Bottom-Left',
|
||||
)
|
||||
const [preferredExplorer] = useLocalStorageState(
|
||||
PREFERRED_EXPLORER_KEY,
|
||||
EXPLORERS[0]
|
||||
EXPLORERS[0],
|
||||
)
|
||||
const { type, title, description, txid, show, id } = notification
|
||||
|
||||
|
@ -134,54 +144,51 @@ const TransactionNotification = ({
|
|||
}
|
||||
}
|
||||
|
||||
let parsedDescription = description
|
||||
if (
|
||||
description?.includes('{"err":{"InstructionError":[2,{"Custom":6001}]}}')
|
||||
) {
|
||||
parsedDescription = 'Your max slippage tolerance was exceeded'
|
||||
}
|
||||
const parsedDescription = parseDescription(description)
|
||||
|
||||
// if the notification is a success, then hide the confirming tx notification with the same txid
|
||||
useEffect(() => {
|
||||
if ((type === 'error' || type === 'success') && txid) {
|
||||
setMangoStore((s) => {
|
||||
const newNotifications = s.transactionNotifications.map((n) =>
|
||||
n.txid === txid && n.type === 'confirm' ? { ...n, show: false } : n
|
||||
n.txid === txid && n.type === 'confirm' ? { ...n, show: false } : n,
|
||||
)
|
||||
s.transactionNotifications = newNotifications
|
||||
})
|
||||
}
|
||||
}, [type, txid])
|
||||
|
||||
const hideNotification = () => {
|
||||
const hideNotification = useCallback(() => {
|
||||
setMangoStore((s) => {
|
||||
const newNotifications = s.transactionNotifications.map((n) =>
|
||||
n.id === id ? { ...n, show: false } : n
|
||||
n.id === id ? { ...n, show: false } : n,
|
||||
)
|
||||
s.transactionNotifications = newNotifications
|
||||
})
|
||||
}
|
||||
}, [id])
|
||||
|
||||
// auto hide a notification after 8 seconds unless it is a confirming or time out notification
|
||||
// auto hide a notification
|
||||
// if no status is provided for a tx notification after 90s, hide it
|
||||
useEffect(() => {
|
||||
const id = setTimeout(
|
||||
() => {
|
||||
if (show) {
|
||||
hideNotification()
|
||||
}
|
||||
},
|
||||
const timeoutInterval =
|
||||
parsedTitle || type === 'confirm'
|
||||
? CLIENT_TX_TIMEOUT
|
||||
: type === 'error'
|
||||
? 30000
|
||||
: type === 'info'
|
||||
? 4000
|
||||
: 10000
|
||||
)
|
||||
|
||||
const id = setTimeout(() => {
|
||||
if (show) {
|
||||
hideNotification()
|
||||
}
|
||||
}, timeoutInterval)
|
||||
|
||||
return () => {
|
||||
clearInterval(id)
|
||||
}
|
||||
})
|
||||
}, [hideNotification, parsedTitle, show, type])
|
||||
|
||||
const getTransformClasses = (position: string) => {
|
||||
const fromLeft = {
|
||||
|
|
|
@ -73,7 +73,7 @@ const EditNftProfilePic = ({ onClose }: { onClose: () => void }) => {
|
|||
}
|
||||
const response = await fetch(
|
||||
`${MANGO_DATA_API_URL}/user-data/profile-details`,
|
||||
requestOptions
|
||||
requestOptions,
|
||||
)
|
||||
if (response.status === 200) {
|
||||
await actions.fetchProfileDetails(publicKey.toString())
|
||||
|
@ -119,7 +119,7 @@ const EditNftProfilePic = ({ onClose }: { onClose: () => void }) => {
|
|||
}
|
||||
const response = await fetch(
|
||||
`${MANGO_DATA_API_URL}/user-data/profile-details`,
|
||||
requestOptions
|
||||
requestOptions,
|
||||
)
|
||||
if (response.status === 200) {
|
||||
await actions.fetchProfileDetails(publicKey.toString())
|
||||
|
|
|
@ -31,7 +31,7 @@ const EditProfileForm = ({
|
|||
const profile = mangoStore((s) => s.profile.details)
|
||||
const { publicKey, signMessage } = useWallet()
|
||||
const [profileName, setProfileName] = useState(
|
||||
startCase(profile?.profile_name) || ''
|
||||
startCase(profile?.profile_name) || '',
|
||||
)
|
||||
const [inputError, setInputError] = useState('')
|
||||
const [loadUniquenessCheck, setLoadUniquenessCheck] = useState(false)
|
||||
|
@ -43,7 +43,7 @@ const EditProfileForm = ({
|
|||
try {
|
||||
setLoadUniquenessCheck(true)
|
||||
const response = await fetch(
|
||||
`${MANGO_DATA_API_URL}/user-data/check-profile-name-unique?profile-name=${name}`
|
||||
`${MANGO_DATA_API_URL}/user-data/check-profile-name-unique?profile-name=${name}`,
|
||||
)
|
||||
const uniquenessCheck = await response.json()
|
||||
|
||||
|
@ -105,7 +105,7 @@ const EditProfileForm = ({
|
|||
}
|
||||
const response = await fetch(
|
||||
`${MANGO_DATA_API_URL}/user-data/profile-details`,
|
||||
requestOptions
|
||||
requestOptions,
|
||||
)
|
||||
if (response.status === 200) {
|
||||
setLoadUpdateProfile(false)
|
||||
|
|
|
@ -54,7 +54,7 @@ const Leaderboards = ({
|
|||
staleTime: 1000 * 60,
|
||||
retry: 3,
|
||||
refetchOnWindowFocus: false,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const isLoading =
|
||||
|
@ -96,7 +96,7 @@ const Leaderboards = ({
|
|||
rewardsLeaderboardData.map(
|
||||
(wallet: RewardsLeaderboardItem, i: number) => (
|
||||
<LeaderboardCard rank={i + 1} key={i} wallet={wallet} />
|
||||
)
|
||||
),
|
||||
)
|
||||
) : (
|
||||
<div className="flex justify-center rounded-lg border border-th-bkg-3 p-8">
|
||||
|
|
|
@ -66,7 +66,7 @@ export const tiers = ['seed', 'mango', 'whale', 'bot']
|
|||
const fetchRewardsPoints = async (walletPk: string | undefined) => {
|
||||
try {
|
||||
const data = await fetch(
|
||||
`${MANGO_DATA_API_URL}/user-data/campaign-total-points-wallet?wallet-pk=${walletPk}`
|
||||
`${MANGO_DATA_API_URL}/user-data/campaign-total-points-wallet?wallet-pk=${walletPk}`,
|
||||
)
|
||||
const res = await data.json()
|
||||
return res
|
||||
|
@ -78,7 +78,7 @@ const fetchRewardsPoints = async (walletPk: string | undefined) => {
|
|||
export const fetchLeaderboard = async (tier: string | undefined) => {
|
||||
try {
|
||||
const data = await fetch(
|
||||
`${MANGO_DATA_API_URL}/user-data/campaign-leaderboard?tier=${tier}`
|
||||
`${MANGO_DATA_API_URL}/user-data/campaign-leaderboard?tier=${tier}`,
|
||||
)
|
||||
const res = await data.json()
|
||||
return res
|
||||
|
@ -195,7 +195,7 @@ const Season = ({
|
|||
retry: 3,
|
||||
refetchOnWindowFocus: false,
|
||||
enabled: !!wallet?.adapter,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const {
|
||||
|
@ -210,7 +210,7 @@ const Season = ({
|
|||
staleTime: 1000 * 60,
|
||||
retry: 3,
|
||||
refetchOnWindowFocus: false,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -41,7 +41,7 @@ const SearchPage = () => {
|
|||
try {
|
||||
setLoading(true)
|
||||
const response = await fetch(
|
||||
`${MANGO_DATA_API_URL}/user-data/profile-search?search-string=${searchString}&search-method=${searchType}`
|
||||
`${MANGO_DATA_API_URL}/user-data/profile-search?search-string=${searchString}&search-method=${searchType}`,
|
||||
)
|
||||
const data = await response.json()
|
||||
setSearchResults(data)
|
||||
|
@ -115,7 +115,7 @@ const SearchPage = () => {
|
|||
<MangoAccountItem item={r} type={searchType} />
|
||||
) : (
|
||||
<WalletItem item={r} />
|
||||
)
|
||||
),
|
||||
)
|
||||
) : showNoResults ? (
|
||||
<div className="flex flex-col items-center rounded-md border border-th-bkg-3 p-4">
|
||||
|
|
|
@ -13,7 +13,7 @@ const AnimationSettings = () => {
|
|||
const { t } = useTranslation(['common', 'settings'])
|
||||
const [animationSettings, setAnimationSettings] = useLocalStorageState(
|
||||
ANIMATION_SETTINGS_KEY,
|
||||
INITIAL_ANIMATION_SETTINGS
|
||||
INITIAL_ANIMATION_SETTINGS,
|
||||
)
|
||||
|
||||
const handleToggleAnimationSetting = (settingName: string) => {
|
||||
|
|
|
@ -17,6 +17,7 @@ import { useTheme } from 'next-themes'
|
|||
import { useCallback } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import {
|
||||
AUTO_CONNECT_WALLET,
|
||||
NOTIFICATION_POSITION_KEY,
|
||||
SIZE_INPUT_UI_KEY,
|
||||
TRADE_CHART_UI_KEY,
|
||||
|
@ -24,6 +25,7 @@ import {
|
|||
} from 'utils/constants'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import Switch from '@components/forms/Switch'
|
||||
|
||||
const NOTIFICATION_POSITIONS = [
|
||||
'bottom-left',
|
||||
|
@ -72,23 +74,27 @@ const DisplaySettings = () => {
|
|||
|
||||
const [savedLanguage, setSavedLanguage] = useLocalStorageState(
|
||||
'language',
|
||||
'en'
|
||||
'en',
|
||||
)
|
||||
const router = useRouter()
|
||||
const { pathname, asPath, query } = router
|
||||
const [notificationPosition, setNotificationPosition] = useLocalStorageState(
|
||||
NOTIFICATION_POSITION_KEY,
|
||||
'bottom-left'
|
||||
'bottom-left',
|
||||
)
|
||||
const [tradeFormUi, setTradeFormUi] = useLocalStorageState(
|
||||
SIZE_INPUT_UI_KEY,
|
||||
'slider'
|
||||
'slider',
|
||||
)
|
||||
const [tradeChartUi, setTradeChartUi] = useLocalStorageState(
|
||||
TRADE_CHART_UI_KEY,
|
||||
'trading-view'
|
||||
'trading-view',
|
||||
)
|
||||
const [, setTradeLayout] = useLocalStorageState(TRADE_LAYOUT_KEY, 'chartLeft')
|
||||
const [autoConnect, setAutoConnect] = useLocalStorageState(
|
||||
AUTO_CONNECT_WALLET,
|
||||
true,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (connection && publicKey) {
|
||||
|
@ -105,7 +111,7 @@ const DisplaySettings = () => {
|
|||
router.push({ pathname, query }, asPath, { locale: l })
|
||||
dayjs.locale(l == 'zh_tw' ? 'zh-tw' : l)
|
||||
},
|
||||
[router, pathname, query, asPath, setSavedLanguage]
|
||||
[router, pathname, query, asPath, setSavedLanguage],
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -209,6 +215,13 @@ const DisplaySettings = () => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-t border-th-bkg-3 p-4">
|
||||
<p className="">Auto Connect Wallet</p>
|
||||
<Switch
|
||||
checked={autoConnect}
|
||||
onChange={() => setAutoConnect(!autoConnect)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ const HotKeysSettings = () => {
|
|||
{Object.entries(options).map((e) => {
|
||||
return e[1]
|
||||
? `${e[0] !== 'margin' ? ', ' : ''}${t(
|
||||
`trade:${e[0]}`
|
||||
`trade:${e[0]}`,
|
||||
)}`
|
||||
: ''
|
||||
})}
|
||||
|
|
|
@ -49,7 +49,7 @@ const NotificationSettings = () => {
|
|||
onChange={() =>
|
||||
handleSettingChange(
|
||||
'fillsNotifications',
|
||||
!data?.fillsNotifications
|
||||
!data?.fillsNotifications,
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -15,7 +15,7 @@ const PreferredExplorerSettings = () => {
|
|||
const { t } = useTranslation('settings')
|
||||
const [preferredExplorer, setPreferredExplorer] = useLocalStorageState(
|
||||
PREFERRED_EXPLORER_KEY,
|
||||
EXPLORERS[0]
|
||||
EXPLORERS[0],
|
||||
)
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -14,18 +14,18 @@ import {
|
|||
} from 'utils/constants'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
|
||||
export const TRITON_DEDICATED_URL = process.env.NEXT_PUBLIC_TRITON_TOKEN
|
||||
? `https://mango.rpcpool.com/${process.env.NEXT_PUBLIC_TRITON_TOKEN}`
|
||||
: 'https://mango.rpcpool.com/946ef7337da3f5b8d3e4a34e7f88'
|
||||
|
||||
const RPC_URLS = [
|
||||
{
|
||||
label: 'Triton Shared',
|
||||
value:
|
||||
process.env.NEXT_PUBLIC_ENDPOINT ||
|
||||
'https://mango.rpcpool.com/946ef7337da3f5b8d3e4a34e7f88',
|
||||
value: process.env.NEXT_PUBLIC_ENDPOINT || TRITON_DEDICATED_URL,
|
||||
},
|
||||
{
|
||||
label: 'Triton Dedicated',
|
||||
value: process.env.NEXT_PUBLIC_TRITON_TOKEN
|
||||
? `https://mango.rpcpool.com/${process.env.NEXT_PUBLIC_TRITON_TOKEN}`
|
||||
: 'https://mango.rpcpool.com/946ef7337da3f5b8d3e4a34e7f88',
|
||||
value: TRITON_DEDICATED_URL,
|
||||
},
|
||||
// {
|
||||
// label: 'Genesys Go',
|
||||
|
@ -50,11 +50,11 @@ const RpcSettings = () => {
|
|||
const [showCustomForm, setShowCustomForm] = useState(false)
|
||||
const [rpcEndpointProvider, setRpcEndpointProvider] = useLocalStorageState(
|
||||
RPC_PROVIDER_KEY,
|
||||
RPC_URLS[0].value
|
||||
RPC_URLS[0].value,
|
||||
)
|
||||
const [storedPriorityFee, setStoredPriorityFee] = useLocalStorageState(
|
||||
PRIORITY_FEE_KEY,
|
||||
DEFAULT_PRIORITY_FEE.value
|
||||
DEFAULT_PRIORITY_FEE.value,
|
||||
)
|
||||
const [storedUseOrderbookFeed, setStoredUseOrderbookFeed] =
|
||||
useLocalStorageState(USE_ORDERBOOK_FEED_KEY, true)
|
||||
|
@ -77,7 +77,7 @@ const RpcSettings = () => {
|
|||
|
||||
const handleSetEndpointProvider = (provider: string) => {
|
||||
const endpointProvider = RPC_URLS.find(
|
||||
(node) => node.label === provider
|
||||
(node) => node.label === provider,
|
||||
) || { label: 'Custom', value: rpcEndpointProvider }
|
||||
setRpcEndpointProvider(endpointProvider.value)
|
||||
if (provider !== 'Custom') {
|
||||
|
@ -96,7 +96,7 @@ const RpcSettings = () => {
|
|||
}
|
||||
}
|
||||
},
|
||||
[setStoredPriorityFee, actions, wallet]
|
||||
[setStoredPriorityFee, actions, wallet],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -14,7 +14,7 @@ const SoundSettings = () => {
|
|||
const { t } = useTranslation(['common', 'settings'])
|
||||
const [soundSettings, setSoundSettings] = useLocalStorageState(
|
||||
SOUND_SETTINGS_KEY,
|
||||
INITIAL_SOUND_SETTINGS
|
||||
INITIAL_SOUND_SETTINGS,
|
||||
)
|
||||
|
||||
const handleToggleSoundSetting = (settingName: string) => {
|
||||
|
|
|
@ -22,18 +22,21 @@ import useBanksWithBalances, {
|
|||
import useUnownedAccount from 'hooks/useUnownedAccount'
|
||||
import { Disclosure, Transition } from '@headlessui/react'
|
||||
import TokenLogo from './TokenLogo'
|
||||
import useHealthContributions from 'hooks/useHealthContributions'
|
||||
import Tooltip from './Tooltip'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import { USDC_MINT } from 'utils/constants'
|
||||
import { WRAPPED_SOL_MINT } from '@project-serum/serum/lib/token-instructions'
|
||||
|
||||
const BalancesTable = () => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const { t } = useTranslation(['common', 'account', 'trade'])
|
||||
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
|
||||
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
|
||||
const { width } = useViewport()
|
||||
const { connected } = useWallet()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
const banks = useBanksWithBalances('balance')
|
||||
const { initContributions } = useHealthContributions()
|
||||
|
||||
const filteredBanks = useMemo(() => {
|
||||
if (banks.length) {
|
||||
|
@ -56,6 +59,15 @@ const BalancesTable = () => {
|
|||
<TrHead>
|
||||
<Th className="bg-th-bkg-1 text-left">{t('token')}</Th>
|
||||
<Th className="bg-th-bkg-1 text-right">{t('balance')}</Th>
|
||||
<Th>
|
||||
<div className="flex justify-end">
|
||||
<Tooltip content={t('account:tooltip-collateral-value')}>
|
||||
<span className="tooltip-underline">
|
||||
{t('collateral-value')}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Th>
|
||||
<Th className="bg-th-bkg-1 text-right">{t('trade:in-orders')}</Th>
|
||||
<Th className="bg-th-bkg-1 text-right" id="trade-step-ten">
|
||||
{t('trade:unsettled')}
|
||||
|
@ -70,6 +82,15 @@ const BalancesTable = () => {
|
|||
const inOrders = spotBalances[bank.mint.toString()]?.inOrders || 0
|
||||
const unsettled = spotBalances[bank.mint.toString()]?.unsettled || 0
|
||||
|
||||
const collateralValue =
|
||||
initContributions.find((val) => val.asset === bank.name)
|
||||
?.contribution || 0
|
||||
|
||||
const assetWeight = bank
|
||||
.scaledInitAssetWeight(bank.price)
|
||||
.toFixed(2)
|
||||
const liabWeight = bank.scaledInitLiabWeight(bank.price).toFixed(2)
|
||||
|
||||
return (
|
||||
<TrBody key={bank.name} className="text-sm">
|
||||
<Td>
|
||||
|
@ -89,6 +110,23 @@ const BalancesTable = () => {
|
|||
/>
|
||||
</p>
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<p>
|
||||
<FormatNumericValue
|
||||
value={collateralValue}
|
||||
decimals={2}
|
||||
isUsd
|
||||
/>
|
||||
</p>
|
||||
<p className="text-sm text-th-fgd-4">
|
||||
<FormatNumericValue
|
||||
value={
|
||||
collateralValue <= -0.01 ? liabWeight : assetWeight
|
||||
}
|
||||
/>
|
||||
x
|
||||
</p>
|
||||
</Td>
|
||||
<Td className="text-right">
|
||||
<BankAmountWithValue amount={inOrders} bank={bank} stacked />
|
||||
</Td>
|
||||
|
@ -109,6 +147,13 @@ const BalancesTable = () => {
|
|||
const inOrders = spotBalances[bank.mint.toString()]?.inOrders || 0
|
||||
const unsettled = spotBalances[bank.mint.toString()]?.unsettled || 0
|
||||
|
||||
const collateralValue =
|
||||
initContributions.find((val) => val.asset === bank.name)
|
||||
?.contribution || 0
|
||||
|
||||
const assetWeight = bank.scaledInitAssetWeight(bank.price).toFixed(2)
|
||||
const liabWeight = bank.scaledInitLiabWeight(bank.price).toFixed(2)
|
||||
|
||||
return (
|
||||
<Disclosure key={bank.name}>
|
||||
{({ open }) => (
|
||||
|
@ -152,6 +197,33 @@ const BalancesTable = () => {
|
|||
>
|
||||
<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">
|
||||
<Tooltip
|
||||
content={t('account:tooltip-collateral-value')}
|
||||
>
|
||||
<p className="tooltip-underline text-xs text-th-fgd-3">
|
||||
{t('collateral-value')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
<FormatNumericValue
|
||||
value={collateralValue}
|
||||
decimals={2}
|
||||
isUsd
|
||||
/>
|
||||
<span className="text-th-fgd-3">
|
||||
{' '}
|
||||
<FormatNumericValue
|
||||
value={
|
||||
collateralValue <= -0.01
|
||||
? liabWeight
|
||||
: assetWeight
|
||||
}
|
||||
/>
|
||||
x
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
{t('trade:in-orders')}
|
||||
|
@ -221,7 +293,7 @@ const Balance = ({ bank }: { bank: BankWithBalance }) => {
|
|||
let tickDecimals: number
|
||||
if (selectedMarket instanceof Serum3Market) {
|
||||
const market = group.getSerum3ExternalMarket(
|
||||
selectedMarket.serumMarketExternal
|
||||
selectedMarket.serumMarketExternal,
|
||||
)
|
||||
minOrderDecimals = getDecimalCount(market.minOrderSize)
|
||||
tickDecimals = getDecimalCount(market.tickSize)
|
||||
|
@ -234,7 +306,7 @@ const Balance = ({ bank }: { bank: BankWithBalance }) => {
|
|||
const floorBalance = floorToDecimal(balance, tickDecimals).toNumber()
|
||||
const baseSize = floorToDecimal(
|
||||
floorBalance / price,
|
||||
minOrderDecimals
|
||||
minOrderDecimals,
|
||||
).toNumber()
|
||||
const quoteSize = floorToDecimal(baseSize * price, tickDecimals)
|
||||
set((s) => {
|
||||
|
@ -250,7 +322,7 @@ const Balance = ({ bank }: { bank: BankWithBalance }) => {
|
|||
})
|
||||
}
|
||||
},
|
||||
[selectedMarket]
|
||||
[selectedMarket],
|
||||
)
|
||||
|
||||
const handleSwapFormBalanceClick = useCallback(
|
||||
|
@ -284,7 +356,7 @@ const Balance = ({ bank }: { bank: BankWithBalance }) => {
|
|||
})
|
||||
}
|
||||
},
|
||||
[bank]
|
||||
[bank],
|
||||
)
|
||||
|
||||
const balance = bank.balance
|
||||
|
@ -321,7 +393,7 @@ const Balance = ({ bank }: { bank: BankWithBalance }) => {
|
|||
className="font-normal underline underline-offset-2 md:underline-offset-4 md:hover:no-underline"
|
||||
onClick={() =>
|
||||
handleSwapFormBalanceClick(
|
||||
Number(floorToDecimal(balance, tokenBank.mintDecimals))
|
||||
Number(floorToDecimal(balance, tokenBank.mintDecimals)),
|
||||
)
|
||||
}
|
||||
>
|
||||
|
|
|
@ -1,19 +1,35 @@
|
|||
import WalletIcon from '@components/icons/WalletIcon'
|
||||
import { useEnhancedWallet } from '@components/wallet/EnhancedWalletProvider'
|
||||
import { LinkIcon } from '@heroicons/react/20/solid'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Button from './Button'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { useCallback } from 'react'
|
||||
import { AUTO_CONNECT_WALLET } from 'utils/constants'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
|
||||
const set = mangoStore.getState().set
|
||||
|
||||
const ConnectEmptyState = ({ text }: { text: string }) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { handleConnect } = useEnhancedWallet()
|
||||
const groupLoaded = mangoStore((s) => s.groupLoaded)
|
||||
const { connect } = useWallet()
|
||||
const [autoConnect] = useLocalStorageState(AUTO_CONNECT_WALLET, true)
|
||||
|
||||
const handleConnect = useCallback(() => {
|
||||
if (!autoConnect) {
|
||||
set((s) => {
|
||||
s.showUserSetup = true
|
||||
})
|
||||
} else {
|
||||
connect()
|
||||
}
|
||||
}, [autoConnect, connect])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center">
|
||||
<WalletIcon className="mb-2 h-6 w-6 text-th-fgd-4" />
|
||||
<p className="mb-4">{text}</p>
|
||||
<Button onClick={handleConnect} disabled={!groupLoaded}>
|
||||
<Button onClick={handleConnect}>
|
||||
<div className="flex items-center">
|
||||
<LinkIcon className="mr-2 h-5 w-5" />
|
||||
{t('connect')}
|
||||
|
|
|
@ -18,7 +18,6 @@ import FlipNumbers from 'react-flip-numbers'
|
|||
import ContentBox from './ContentBox'
|
||||
import SheenLoader from './SheenLoader'
|
||||
import { COLORS } from '../../styles/colors'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { IconButton } from './Button'
|
||||
import { ArrowLeftIcon, NoSymbolIcon } from '@heroicons/react/20/solid'
|
||||
import { FadeInFadeOut } from './Transitions'
|
||||
|
@ -33,6 +32,7 @@ import { useTranslation } from 'next-i18next'
|
|||
import FormatNumericValue from './FormatNumericValue'
|
||||
import { ContentType } from 'recharts/types/component/Tooltip'
|
||||
import Tooltip from './Tooltip'
|
||||
import useThemeWrapper from 'hooks/useThemeWrapper'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
|
@ -97,10 +97,10 @@ const DetailedAreaOrBarChart: FunctionComponent<
|
|||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const [mouseData, setMouseData] = useState<any>(null)
|
||||
const { theme } = useTheme()
|
||||
const { theme } = useThemeWrapper()
|
||||
const [animationSettings] = useLocalStorageState(
|
||||
ANIMATION_SETTINGS_KEY,
|
||||
INITIAL_ANIMATION_SETTINGS
|
||||
INITIAL_ANIMATION_SETTINGS,
|
||||
)
|
||||
|
||||
const handleMouseMove = (coords: any) => {
|
||||
|
@ -134,7 +134,7 @@ const DetailedAreaOrBarChart: FunctionComponent<
|
|||
if (filteredData.length) {
|
||||
if (mouseData) {
|
||||
const index = filteredData.findIndex(
|
||||
(d: any) => d[xKey] === mouseData[xKey]
|
||||
(d: any) => d[xKey] === mouseData[xKey],
|
||||
)
|
||||
const change =
|
||||
index >= 0 ? filteredData[index][yKey] - filteredData[0][yKey] : 0
|
||||
|
@ -216,7 +216,7 @@ const DetailedAreaOrBarChart: FunctionComponent<
|
|||
mouseData[yKey] < 0 ? '-' : ''
|
||||
}${prefix}${formatNumericValue(
|
||||
Math.abs(mouseData[yKey]),
|
||||
yDecimals
|
||||
yDecimals,
|
||||
)}${suffix}`}
|
||||
/>
|
||||
) : (
|
||||
|
@ -249,7 +249,7 @@ const DetailedAreaOrBarChart: FunctionComponent<
|
|||
{dayjs(mouseData[xKey]).format(
|
||||
tooltipDateFormat
|
||||
? tooltipDateFormat
|
||||
: 'DD MMM YY, h:mma'
|
||||
: 'DD MMM YY, h:mma',
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -273,9 +273,9 @@ const DetailedAreaOrBarChart: FunctionComponent<
|
|||
: ''
|
||||
}${prefix}${formatNumericValue(
|
||||
Math.abs(
|
||||
filteredData[filteredData.length - 1][yKey]
|
||||
filteredData[filteredData.length - 1][yKey],
|
||||
),
|
||||
yDecimals
|
||||
yDecimals,
|
||||
)}${suffix}`}
|
||||
/>
|
||||
) : (
|
||||
|
@ -308,11 +308,11 @@ const DetailedAreaOrBarChart: FunctionComponent<
|
|||
} text-th-fgd-4`}
|
||||
>
|
||||
{dayjs(
|
||||
filteredData[filteredData.length - 1][xKey]
|
||||
filteredData[filteredData.length - 1][xKey],
|
||||
).format(
|
||||
tooltipDateFormat
|
||||
? tooltipDateFormat
|
||||
: 'DD MMM YY, h:mma'
|
||||
: 'DD MMM YY, h:mma',
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -341,7 +341,7 @@ const DetailedAreaOrBarChart: FunctionComponent<
|
|||
<linearGradient
|
||||
id={`gradientArea-${title?.replace(
|
||||
/[^a-zA-Z]/g,
|
||||
''
|
||||
'',
|
||||
)}`}
|
||||
x1="0"
|
||||
y1={flipGradientCoords ? '1' : '0'}
|
||||
|
@ -380,7 +380,7 @@ const DetailedAreaOrBarChart: FunctionComponent<
|
|||
strokeWidth={1.5}
|
||||
fill={`url(#gradientArea-${title?.replace(
|
||||
/[^a-zA-Z]/g,
|
||||
''
|
||||
'',
|
||||
)})`}
|
||||
/>
|
||||
<XAxis
|
||||
|
|
|
@ -11,7 +11,7 @@ const FavoriteMarketButton = ({
|
|||
}) => {
|
||||
const [favoriteMarkets, setFavoriteMarkets] = useLocalStorageState<string[]>(
|
||||
FAVORITE_MARKETS_KEY,
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const addToFavorites = (marketName: string) => {
|
||||
|
@ -24,7 +24,7 @@ const FavoriteMarketButton = ({
|
|||
}
|
||||
|
||||
return favoriteMarkets.find(
|
||||
(marketName: string) => marketName === market.name
|
||||
(marketName: string) => marketName === market.name,
|
||||
) ? (
|
||||
<button
|
||||
className="flex items-center justify-center text-th-active focus-visible:text-th-fgd-4 md:hover:text-th-fgd-3"
|
||||
|
|
|
@ -63,7 +63,7 @@ const SolBalanceWarnings = ({
|
|||
<div className={className}>
|
||||
<InlineNotification
|
||||
type="info"
|
||||
desc={`SOL deposits are restricted to leave ${MIN_SOL_BALANCE} SOL in your wallet for sending transactions`}
|
||||
desc={`Max SOL deposits are reduced to leave ${MIN_SOL_BALANCE} SOL in your wallet for sending transactions`}
|
||||
/>
|
||||
</div>
|
||||
) : null
|
||||
|
|
|
@ -18,7 +18,7 @@ const SuccessParticles = () => {
|
|||
const set = mangoStore((s) => s.set)
|
||||
const [animationSettings] = useLocalStorageState(
|
||||
ANIMATION_SETTINGS_KEY,
|
||||
INITIAL_ANIMATION_SETTINGS
|
||||
INITIAL_ANIMATION_SETTINGS,
|
||||
)
|
||||
const { theme } = useTheme()
|
||||
|
||||
|
@ -77,7 +77,7 @@ const SuccessParticles = () => {
|
|||
set((s) => {
|
||||
s.successAnimation.swap = false
|
||||
}),
|
||||
8000
|
||||
8000,
|
||||
)
|
||||
}
|
||||
if (showForTheme) {
|
||||
|
@ -86,7 +86,7 @@ const SuccessParticles = () => {
|
|||
set((s) => {
|
||||
s.successAnimation.theme = false
|
||||
}),
|
||||
6000
|
||||
6000,
|
||||
)
|
||||
}
|
||||
if (showForTrade) {
|
||||
|
@ -95,7 +95,7 @@ const SuccessParticles = () => {
|
|||
set((s) => {
|
||||
s.successAnimation.trade = false
|
||||
}),
|
||||
8000
|
||||
8000,
|
||||
)
|
||||
}
|
||||
}, [showForSwap, showForTheme, showForTrade])
|
||||
|
|
|
@ -62,7 +62,7 @@ export const TrBody = forwardRef<HTMLTableRowElement, TrBodyProps>(
|
|||
{children}
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
TrBody.displayName = 'TrBody'
|
||||
|
|
|
@ -22,7 +22,7 @@ const TokenLogo = ({
|
|||
let jupiterLogoURI
|
||||
if (mangoTokens?.length) {
|
||||
jupiterLogoURI = mangoTokens.find(
|
||||
(t) => t.address === bank?.mint.toString()
|
||||
(t) => t.address === bank?.mint.toString(),
|
||||
)?.logoURI
|
||||
}
|
||||
return jupiterLogoURI
|
||||
|
|
|
@ -26,7 +26,7 @@ const TokenVaultWarnings = ({
|
|||
const maxWithdraw = getMaxWithdrawForBank(group, bank, mangoAccount)
|
||||
const maxBorrow = mangoAccount.getMaxWithdrawWithBorrowForTokenUi(
|
||||
group,
|
||||
bank.mint
|
||||
bank.mint,
|
||||
)
|
||||
|
||||
return [maxWithdraw, maxBorrow]
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'
|
|||
|
||||
export default function useDebounce<T>(
|
||||
value: T,
|
||||
delay: number
|
||||
delay: number,
|
||||
): [T, (value: T) => void] {
|
||||
// State and setters for debounced value
|
||||
const [debouncedValue, setDebouncedValue] = useState(value)
|
||||
|
@ -19,7 +19,7 @@ export default function useDebounce<T>(
|
|||
clearTimeout(handler)
|
||||
}
|
||||
},
|
||||
[value, delay] // Only re-call effect if value or delay changes
|
||||
[value, delay], // Only re-call effect if value or delay changes
|
||||
)
|
||||
return [debouncedValue, setDebouncedValue]
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useRef, useEffect } from 'react'
|
|||
export default function useInterval(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
callback: (...args: any[]) => any,
|
||||
delay: number
|
||||
delay: number,
|
||||
) {
|
||||
const savedCallback = useRef<() => void>()
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ const AverageFundingChart = ({
|
|||
|
||||
const groupByHourlyInterval = (
|
||||
data: PerpStatsItem[],
|
||||
intervalDurationHours: number
|
||||
intervalDurationHours: number,
|
||||
) => {
|
||||
const intervalMillis = intervalDurationHours * 60 * 60 * 1000
|
||||
const groupedData = []
|
||||
|
|
|
@ -3,6 +3,7 @@ import { useMemo, useState } from 'react'
|
|||
import mangoStore from '@store/mangoStore'
|
||||
import { PerpStatsItem } from 'types'
|
||||
import DetailedAreaOrBarChart from '@components/shared/DetailedAreaOrBarChart'
|
||||
import { formatYAxis } from 'utils/formatting'
|
||||
|
||||
interface OiValueItem {
|
||||
date: string
|
||||
|
@ -14,97 +15,143 @@ interface FeeValueItem {
|
|||
feeValue: number
|
||||
}
|
||||
|
||||
interface VolumeValueItem {
|
||||
date: string
|
||||
volume: number
|
||||
}
|
||||
|
||||
interface PerpStatsData {
|
||||
feeValues: FeeValueItem[]
|
||||
openInterestValues: OiValueItem[]
|
||||
volumeValues: VolumeValueItem[]
|
||||
}
|
||||
|
||||
const MangoPerpStatsCharts = () => {
|
||||
const { t } = useTranslation(['common', 'token', 'trade'])
|
||||
const loadingPerpStats = mangoStore((s) => s.perpStats.loading)
|
||||
const perpStats = mangoStore((s) => s.perpStats.data)
|
||||
const [oiDaysToShow, setOiDaysToShow] = useState('30')
|
||||
const [feesDaysToShow, setFeesDaysToShow] = useState('30')
|
||||
// const perpMarkets = mangoStore((s) => s.perpMarkets)
|
||||
const [oiDaysToShow, setOiDaysToShow] = useState('30')
|
||||
const [volumeDaysToShow, setVolumeDaysToShow] = useState('30')
|
||||
|
||||
// const currentTotalOpenInterestValue = useMemo(() => {
|
||||
// if (!perpMarkets.length) return 0
|
||||
// return perpMarkets.reduce((a: number, c: PerpMarket) => {
|
||||
// const value = a + c.openInterest.toNumber() * c.uiPrice
|
||||
// return value
|
||||
// }, 0)
|
||||
// }, [perpMarkets])
|
||||
const [feeValues, openInterestValues, volumeValues] = useMemo(() => {
|
||||
if (!perpStats || !perpStats.length) return [[], [], []]
|
||||
const data = perpStats.reduce(
|
||||
(a: PerpStatsData, c: PerpStatsItem) => {
|
||||
const hasDateFee = a.feeValues.find(
|
||||
(d: FeeValueItem) => d.date === c.date_hour,
|
||||
)
|
||||
|
||||
const totalFeeValues = useMemo(() => {
|
||||
if (!perpStats || !perpStats.length) return []
|
||||
const values = perpStats.reduce((a: FeeValueItem[], c: PerpStatsItem) => {
|
||||
const hasDate = a.find((d: FeeValueItem) => d.date === c.date_hour)
|
||||
if (!hasDate) {
|
||||
a.push({
|
||||
date: c.date_hour,
|
||||
feeValue: c.total_fees,
|
||||
})
|
||||
} else {
|
||||
hasDate.feeValue = hasDate.feeValue + c.total_fees
|
||||
}
|
||||
return a.sort(
|
||||
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()
|
||||
)
|
||||
}, [])
|
||||
return values
|
||||
}, [perpStats])
|
||||
const hasDateOpenInterest = a.openInterestValues.find(
|
||||
(d: OiValueItem) => d.date === c.date_hour,
|
||||
)
|
||||
|
||||
const totalOpenInterestValues = useMemo(() => {
|
||||
if (!perpStats || !perpStats.length) return []
|
||||
const values = perpStats.reduce((a: OiValueItem[], c: PerpStatsItem) => {
|
||||
const hasDate = a.find((d: OiValueItem) => d.date === c.date_hour)
|
||||
if (!hasDate) {
|
||||
a.push({
|
||||
date: c.date_hour,
|
||||
openInterest: Math.floor(c.open_interest * c.price),
|
||||
})
|
||||
} else {
|
||||
hasDate.openInterest =
|
||||
hasDate.openInterest + Math.floor(c.open_interest * c.price)
|
||||
}
|
||||
return a.sort(
|
||||
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()
|
||||
)
|
||||
}, [])
|
||||
return values
|
||||
const hasDateVolume = a.volumeValues.find(
|
||||
(d: VolumeValueItem) => d.date === c.date_hour,
|
||||
)
|
||||
|
||||
if (!hasDateFee) {
|
||||
a.feeValues.push({
|
||||
date: c.date_hour,
|
||||
feeValue: c.total_fees,
|
||||
})
|
||||
} else {
|
||||
hasDateFee.feeValue += c.total_fees
|
||||
}
|
||||
|
||||
if (!hasDateOpenInterest) {
|
||||
a.openInterestValues.push({
|
||||
date: c.date_hour,
|
||||
openInterest: Math.floor(c.open_interest * c.price),
|
||||
})
|
||||
} else {
|
||||
hasDateOpenInterest.openInterest += Math.floor(
|
||||
c.open_interest * c.price,
|
||||
)
|
||||
}
|
||||
|
||||
if (!hasDateVolume) {
|
||||
a.volumeValues.push({
|
||||
date: c.date_hour,
|
||||
volume: c.cumulative_quote_volume,
|
||||
})
|
||||
} else {
|
||||
hasDateVolume.volume += c.cumulative_quote_volume
|
||||
}
|
||||
|
||||
return a
|
||||
},
|
||||
{ feeValues: [], openInterestValues: [], volumeValues: [] },
|
||||
)
|
||||
|
||||
const { feeValues, openInterestValues, volumeValues } = data
|
||||
|
||||
const sortedFeeValues = feeValues.sort(
|
||||
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(),
|
||||
)
|
||||
const sortedOpenInterestValues = openInterestValues.sort(
|
||||
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(),
|
||||
)
|
||||
const sortedVolumeValues = volumeValues.sort(
|
||||
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(),
|
||||
)
|
||||
|
||||
return [sortedFeeValues, sortedOpenInterestValues, sortedVolumeValues]
|
||||
}, [perpStats])
|
||||
|
||||
return (
|
||||
<>
|
||||
{totalFeeValues.length ? (
|
||||
{feeValues.length ? (
|
||||
<div className="col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1 md:pl-6">
|
||||
<DetailedAreaOrBarChart
|
||||
data={totalFeeValues}
|
||||
data={feeValues}
|
||||
daysToShow={feesDaysToShow}
|
||||
setDaysToShow={setFeesDaysToShow}
|
||||
heightClass="h-64"
|
||||
loading={loadingPerpStats}
|
||||
loaderHeightClass="h-[350px]"
|
||||
prefix="$"
|
||||
tickFormat={(x) => `$${x.toFixed(2)}`}
|
||||
tickFormat={(x) => `$${formatYAxis(x)}`}
|
||||
title="Perp Fees"
|
||||
xKey="date"
|
||||
yKey={'feeValue'}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{totalOpenInterestValues.length ? (
|
||||
{openInterestValues.length ? (
|
||||
<div className="col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-r">
|
||||
<DetailedAreaOrBarChart
|
||||
data={totalOpenInterestValues}
|
||||
data={openInterestValues}
|
||||
daysToShow={oiDaysToShow}
|
||||
setDaysToShow={setOiDaysToShow}
|
||||
heightClass="h-64"
|
||||
loading={loadingPerpStats}
|
||||
loaderHeightClass="h-[350px]"
|
||||
prefix="$"
|
||||
tickFormat={(x) => `$${Math.floor(x)}`}
|
||||
tickFormat={(x) => `$${formatYAxis(x)}`}
|
||||
title={t('trade:open-interest')}
|
||||
xKey="date"
|
||||
yKey={'openInterest'}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{volumeValues.length ? (
|
||||
<div className="col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1 md:pl-6">
|
||||
<DetailedAreaOrBarChart
|
||||
data={volumeValues}
|
||||
daysToShow={volumeDaysToShow}
|
||||
setDaysToShow={setVolumeDaysToShow}
|
||||
heightClass="h-64"
|
||||
loading={loadingPerpStats}
|
||||
loaderHeightClass="h-[350px]"
|
||||
prefix="$"
|
||||
tickFormat={(x) => `$${formatYAxis(x)}`}
|
||||
title={t('stats:perp-volume')}
|
||||
xKey="date"
|
||||
yKey={'volume'}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { I80F48, PerpMarket } from '@blockworks-foundation/mango-v4'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useViewport } from '../../hooks/useViewport'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { COLORS } from '../../styles/colors'
|
||||
|
@ -25,10 +24,11 @@ import { Disclosure, Transition } from '@headlessui/react'
|
|||
import { LinkButton } from '@components/shared/Button'
|
||||
import SoonBadge from '@components/shared/SoonBadge'
|
||||
import { DAILY_SECONDS } from 'utils/constants'
|
||||
import useThemeWrapper from 'hooks/useThemeWrapper'
|
||||
|
||||
export const getOneDayPerpStats = (
|
||||
stats: PerpStatsItem[] | null,
|
||||
marketName: string
|
||||
marketName: string,
|
||||
) => {
|
||||
return stats
|
||||
? stats
|
||||
|
@ -46,7 +46,7 @@ export const getOneDayPerpStats = (
|
|||
|
||||
export const goToPerpMarketDetails = (
|
||||
market: PerpMarket,
|
||||
router: NextRouter
|
||||
router: NextRouter,
|
||||
) => {
|
||||
const query = { ...router.query, ['market']: market.name }
|
||||
router.push({ pathname: router.pathname, query })
|
||||
|
@ -57,7 +57,7 @@ const PerpMarketsOverviewTable = () => {
|
|||
const perpMarkets = mangoStore((s) => s.perpMarkets)
|
||||
const loadingPerpStats = mangoStore((s) => s.perpStats.loading)
|
||||
const perpStats = mangoStore((s) => s.perpStats.data)
|
||||
const { theme } = useTheme()
|
||||
const { theme } = useThemeWrapper()
|
||||
const { width } = useViewport()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
const rate = usePerpFundingRate()
|
||||
|
@ -107,14 +107,14 @@ const PerpMarketsOverviewTable = () => {
|
|||
let fundingRateApr
|
||||
if (rate.isSuccess) {
|
||||
const marketRate = rate?.data?.find(
|
||||
(r) => r.market_index === market.perpMarketIndex
|
||||
(r) => r.market_index === market.perpMarketIndex,
|
||||
)
|
||||
if (marketRate) {
|
||||
fundingRate = formatFunding.format(
|
||||
marketRate.funding_rate_hourly
|
||||
marketRate.funding_rate_hourly,
|
||||
)
|
||||
fundingRateApr = formatFunding.format(
|
||||
marketRate.funding_rate_hourly * 8760
|
||||
marketRate.funding_rate_hourly * 8760,
|
||||
)
|
||||
} else {
|
||||
fundingRate = '–'
|
||||
|
@ -190,9 +190,9 @@ const PerpMarketsOverviewTable = () => {
|
|||
<FormatNumericValue
|
||||
value={group.toUiPrice(
|
||||
I80F48.fromNumber(
|
||||
market.stablePriceModel.stablePrice
|
||||
market.stablePriceModel.stablePrice,
|
||||
),
|
||||
market.baseDecimals
|
||||
market.baseDecimals,
|
||||
)}
|
||||
isUsd
|
||||
/>
|
||||
|
@ -260,7 +260,7 @@ const PerpMarketsOverviewTable = () => {
|
|||
<p className="text-th-fgd-4">
|
||||
$
|
||||
{numberCompacter.format(
|
||||
openInterest * market.uiPrice
|
||||
openInterest * market.uiPrice,
|
||||
)}
|
||||
</p>
|
||||
</>
|
||||
|
@ -304,7 +304,7 @@ const MobilePerpMarketItem = ({ market }: { market: PerpMarket }) => {
|
|||
const { t } = useTranslation('common')
|
||||
const loadingPerpStats = mangoStore((s) => s.perpStats.loading)
|
||||
const perpStats = mangoStore((s) => s.perpStats.data)
|
||||
const { theme } = useTheme()
|
||||
const { theme } = useThemeWrapper()
|
||||
const router = useRouter()
|
||||
const rate = usePerpFundingRate()
|
||||
|
||||
|
@ -328,12 +328,12 @@ const MobilePerpMarketItem = ({ market }: { market: PerpMarket }) => {
|
|||
let fundingRateApr: string
|
||||
if (rate.isSuccess) {
|
||||
const marketRate = rate?.data?.find(
|
||||
(r) => r.market_index === market.perpMarketIndex
|
||||
(r) => r.market_index === market.perpMarketIndex,
|
||||
)
|
||||
if (marketRate) {
|
||||
fundingRate = formatFunding.format(marketRate.funding_rate_hourly)
|
||||
fundingRateApr = formatFunding.format(
|
||||
marketRate.funding_rate_hourly * 8760
|
||||
marketRate.funding_rate_hourly * 8760,
|
||||
)
|
||||
} else {
|
||||
fundingRate = '–'
|
||||
|
|
|
@ -41,7 +41,7 @@ const PerpPositionsStatsTable = ({
|
|||
<tbody>
|
||||
{positions.map(({ account, perpPosition, mangoAccount }, i) => {
|
||||
const market = group.getPerpMarketByMarketIndex(
|
||||
perpPosition.marketIndex
|
||||
perpPosition.marketIndex,
|
||||
)
|
||||
const basePosition = perpPosition.getBasePositionUi(market)
|
||||
|
||||
|
@ -49,7 +49,7 @@ const PerpPositionsStatsTable = ({
|
|||
|
||||
const floorBasePosition = floorToDecimal(
|
||||
basePosition,
|
||||
getDecimalCount(market.minOrderSize)
|
||||
getDecimalCount(market.minOrderSize),
|
||||
).toNumber()
|
||||
|
||||
const isLong = basePosition > 0
|
||||
|
@ -179,7 +179,7 @@ const PerpPositionsStatsTable = ({
|
|||
<div className="border-b border-th-bkg-3">
|
||||
{positions.map(({ account, perpPosition, mangoAccount }) => {
|
||||
const market = group.getPerpMarketByMarketIndex(
|
||||
perpPosition.marketIndex
|
||||
perpPosition.marketIndex,
|
||||
)
|
||||
const basePosition = perpPosition.getBasePositionUi(market)
|
||||
|
||||
|
@ -187,7 +187,7 @@ const PerpPositionsStatsTable = ({
|
|||
|
||||
const floorBasePosition = floorToDecimal(
|
||||
basePosition,
|
||||
getDecimalCount(market.minOrderSize)
|
||||
getDecimalCount(market.minOrderSize),
|
||||
).toNumber()
|
||||
|
||||
const isLong = basePosition > 0
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useMemo } from 'react'
|
||||
import { useViewport } from '../../hooks/useViewport'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
|
@ -21,12 +20,13 @@ import { TickerData } from 'types'
|
|||
import { Disclosure, Transition } from '@headlessui/react'
|
||||
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||
import MarketChange from '@components/shared/MarketChange'
|
||||
import useThemeWrapper from 'hooks/useThemeWrapper'
|
||||
|
||||
const SpotMarketsTable = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const { group } = useMangoGroup()
|
||||
const serumMarkets = mangoStore((s) => s.serumMarkets)
|
||||
const { theme } = useTheme()
|
||||
const { theme } = useThemeWrapper()
|
||||
const { width } = useViewport()
|
||||
const showTableView = width ? width > breakpoints.md : false
|
||||
const { data: birdeyePrices, isLoading: loadingPrices } =
|
||||
|
@ -40,7 +40,7 @@ const SpotMarketsTable = () => {
|
|||
staleTime: 1000 * 60,
|
||||
retry: 3,
|
||||
refetchOnWindowFocus: false,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -62,30 +62,30 @@ const SpotMarketsTable = () => {
|
|||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map((mkt) => {
|
||||
const baseBank = group?.getFirstBankByTokenIndex(
|
||||
mkt.baseTokenIndex
|
||||
mkt.baseTokenIndex,
|
||||
)
|
||||
const quoteBank = group?.getFirstBankByTokenIndex(
|
||||
mkt.quoteTokenIndex
|
||||
mkt.quoteTokenIndex,
|
||||
)
|
||||
const market = group?.getSerum3ExternalMarket(
|
||||
mkt.serumMarketExternal
|
||||
mkt.serumMarketExternal,
|
||||
)
|
||||
let price
|
||||
if (baseBank && market && quoteBank) {
|
||||
price = floorToDecimal(
|
||||
baseBank.uiPrice / quoteBank.uiPrice,
|
||||
getDecimalCount(market.tickSize)
|
||||
getDecimalCount(market.tickSize),
|
||||
).toNumber()
|
||||
}
|
||||
let tickerData: TickerData | undefined
|
||||
if (spotVolumeData && spotVolumeData.length) {
|
||||
tickerData = spotVolumeData.find(
|
||||
(m: TickerData) => m.ticker_id === mkt.name
|
||||
(m: TickerData) => m.ticker_id === mkt.name,
|
||||
)
|
||||
}
|
||||
|
||||
const birdeyeData = birdeyePrices.find(
|
||||
(m) => m.mint === mkt.serumMarketExternal.toString()
|
||||
(m) => m.mint === mkt.serumMarketExternal.toString(),
|
||||
)
|
||||
|
||||
const birdeyeChange =
|
||||
|
@ -163,7 +163,7 @@ const SpotMarketsTable = () => {
|
|||
{tickerData ? (
|
||||
<span>
|
||||
{numberCompacter.format(
|
||||
parseFloat(tickerData.target_volume)
|
||||
parseFloat(tickerData.target_volume),
|
||||
)}{' '}
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{quoteBank?.name}
|
||||
|
@ -213,7 +213,7 @@ const MobileSpotMarketItem = ({
|
|||
const { data: birdeyePrices, isLoading: loadingPrices } =
|
||||
useBirdeyeMarketPrices()
|
||||
const { group } = useMangoGroup()
|
||||
const { theme } = useTheme()
|
||||
const { theme } = useThemeWrapper()
|
||||
const baseBank = group?.getFirstBankByTokenIndex(market.baseTokenIndex)
|
||||
const quoteBank = group?.getFirstBankByTokenIndex(market.quoteTokenIndex)
|
||||
const serumMarket = group?.getSerum3ExternalMarket(market.serumMarketExternal)
|
||||
|
@ -222,14 +222,14 @@ const MobileSpotMarketItem = ({
|
|||
if (!baseBank || !quoteBank || !serumMarket) return 0
|
||||
return floorToDecimal(
|
||||
baseBank.uiPrice / quoteBank.uiPrice,
|
||||
getDecimalCount(serumMarket.tickSize)
|
||||
getDecimalCount(serumMarket.tickSize),
|
||||
).toNumber()
|
||||
}, [baseBank, quoteBank, serumMarket])
|
||||
|
||||
const birdeyeData = useMemo(() => {
|
||||
if (!loadingPrices) {
|
||||
return birdeyePrices.find(
|
||||
(m) => m.mint === market.serumMarketExternal.toString()
|
||||
(m) => m.mint === market.serumMarketExternal.toString(),
|
||||
)
|
||||
}
|
||||
return null
|
||||
|
@ -254,7 +254,7 @@ const MobileSpotMarketItem = ({
|
|||
let tickerData: TickerData | undefined
|
||||
if (spotVolumeData && spotVolumeData.length) {
|
||||
tickerData = spotVolumeData.find(
|
||||
(m: TickerData) => m.ticker_id === market.name
|
||||
(m: TickerData) => m.ticker_id === market.name,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -336,7 +336,7 @@ const MobileSpotMarketItem = ({
|
|||
{tickerData ? (
|
||||
<span>
|
||||
{numberCompacter.format(
|
||||
parseFloat(tickerData.target_volume)
|
||||
parseFloat(tickerData.target_volume),
|
||||
)}{' '}
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{quoteBank?.name}
|
||||
|
|
|
@ -19,7 +19,7 @@ const StatsPage = () => {
|
|||
const actions = mangoStore.getState().actions
|
||||
const perpStats = mangoStore((s) => s.perpStats.data)
|
||||
const perpPositionsStatsNotLoaded = mangoStore(
|
||||
(s) => s.perpStats.positions.initialLoad
|
||||
(s) => s.perpStats.positions.initialLoad,
|
||||
)
|
||||
const { group } = useMangoGroup()
|
||||
const { width } = useViewport()
|
||||
|
|
|
@ -98,7 +98,7 @@ const TokenDetailsTable = () => {
|
|||
const [oracleProvider, oracleLinkPath] = getOracleProvider(bank)
|
||||
|
||||
const mintInfo = group.mintInfosMapByMint.get(
|
||||
bank.mint.toString()
|
||||
bank.mint.toString(),
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -131,7 +131,7 @@ const TokenDetailsTable = () => {
|
|||
<Td>
|
||||
<p className="text-right">
|
||||
{(100 * bank.loanOriginationFeeRate.toNumber()).toFixed(
|
||||
2
|
||||
2,
|
||||
)}
|
||||
%
|
||||
</p>
|
||||
|
@ -262,7 +262,7 @@ const TokenDetailsTable = () => {
|
|||
</Tooltip>
|
||||
<p className="font-mono text-th-fgd-1">
|
||||
{(bank.liquidationFee.toNumber() * 100).toFixed(
|
||||
2
|
||||
2,
|
||||
)}
|
||||
%
|
||||
</p>
|
||||
|
|
|
@ -84,11 +84,11 @@ const TokenOverviewTable = () => {
|
|||
deposits * bank.minVaultToDepositsRatio
|
||||
const available = Decimal.max(
|
||||
0,
|
||||
availableVaultBalance.toFixed(bank.mintDecimals)
|
||||
availableVaultBalance.toFixed(bank.mintDecimals),
|
||||
)
|
||||
const feesEarned = toUiDecimals(
|
||||
bank.collectedFeesNative,
|
||||
bank.mintDecimals
|
||||
bank.mintDecimals,
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -201,11 +201,11 @@ const TokenOverviewTable = () => {
|
|||
deposits * bank.minVaultToDepositsRatio
|
||||
const available = Decimal.max(
|
||||
0,
|
||||
availableVaultBalance.toFixed(bank.mintDecimals)
|
||||
availableVaultBalance.toFixed(bank.mintDecimals),
|
||||
)
|
||||
const feesEarned = toUiDecimals(
|
||||
bank.collectedFeesNative,
|
||||
bank.mintDecimals
|
||||
bank.mintDecimals,
|
||||
)
|
||||
return (
|
||||
<Disclosure key={bank.name}>
|
||||
|
|
|
@ -39,7 +39,7 @@ const TokenStatsCharts = () => {
|
|||
a +
|
||||
c.bank.uiPrice *
|
||||
toUiDecimals(c.bank.collectedFeesNative, c.bank.mintDecimals),
|
||||
0
|
||||
0,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ const RoutesModal = ({
|
|||
{
|
||||
jupiterTokens.find(
|
||||
(item) =>
|
||||
item?.address === r?.outputMint?.toString()
|
||||
item?.address === r?.outputMint?.toString(),
|
||||
)?.symbol
|
||||
}
|
||||
</span>
|
||||
|
|
|
@ -15,7 +15,6 @@ import mangoStore from '@store/mangoStore'
|
|||
import ContentBox from '../shared/ContentBox'
|
||||
import SwapReviewRouteInfo from './SwapReviewRouteInfo'
|
||||
import TokenSelect from './TokenSelect'
|
||||
import useDebounce from '../shared/useDebounce'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import SwapFormTokenList from './SwapFormTokenList'
|
||||
import { Transition } from '@headlessui/react'
|
||||
|
@ -43,7 +42,6 @@ import TokenVaultWarnings from '@components/shared/TokenVaultWarnings'
|
|||
import MaxSwapAmount from './MaxSwapAmount'
|
||||
import PercentageSelectButtons from './PercentageSelectButtons'
|
||||
import useIpAddress from 'hooks/useIpAddress'
|
||||
import { useEnhancedWallet } from '@components/wallet/EnhancedWalletProvider'
|
||||
import SwapSettings from './SwapSettings'
|
||||
import InlineNotification from '@components/shared/InlineNotification'
|
||||
import useUnownedAccount from 'hooks/useUnownedAccount'
|
||||
|
@ -86,27 +84,25 @@ const SwapForm = () => {
|
|||
amountOut: amountOutFormValue,
|
||||
swapMode,
|
||||
} = mangoStore((s) => s.swap)
|
||||
const [debouncedAmountIn] = useDebounce(amountInFormValue, 300)
|
||||
const [debouncedAmountOut] = useDebounce(amountOutFormValue, 300)
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const { connected, publicKey } = useWallet()
|
||||
|
||||
const amountInAsDecimal: Decimal | null = useMemo(() => {
|
||||
return Number(debouncedAmountIn)
|
||||
? new Decimal(debouncedAmountIn)
|
||||
return Number(amountInFormValue)
|
||||
? new Decimal(amountInFormValue)
|
||||
: new Decimal(0)
|
||||
}, [debouncedAmountIn])
|
||||
}, [amountInFormValue])
|
||||
|
||||
const amountOutAsDecimal: Decimal | null = useMemo(() => {
|
||||
return Number(debouncedAmountOut)
|
||||
? new Decimal(debouncedAmountOut)
|
||||
return Number(amountOutFormValue)
|
||||
? new Decimal(amountOutFormValue)
|
||||
: new Decimal(0)
|
||||
}, [debouncedAmountOut])
|
||||
}, [amountOutFormValue])
|
||||
|
||||
const { bestRoute, routes } = useQuoteRoutes({
|
||||
inputMint: inputBank?.mint.toString() || USDC_MINT,
|
||||
outputMint: outputBank?.mint.toString() || MANGO_MINT,
|
||||
amount: swapMode === 'ExactIn' ? debouncedAmountIn : debouncedAmountOut,
|
||||
amount: swapMode === 'ExactIn' ? amountInFormValue : amountOutFormValue,
|
||||
slippage,
|
||||
swapMode,
|
||||
wallet: publicKey?.toBase58(),
|
||||
|
@ -125,7 +121,14 @@ const SwapForm = () => {
|
|||
}
|
||||
})
|
||||
},
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const setAmountFromSlider = useCallback(
|
||||
(amount: string) => {
|
||||
setAmountInFormValue(amount, true)
|
||||
},
|
||||
[setAmountInFormValue],
|
||||
)
|
||||
|
||||
const setAmountOutFormValue = useCallback((amountOut: string) => {
|
||||
|
@ -146,7 +149,7 @@ const SwapForm = () => {
|
|||
}
|
||||
setAmountOutFormValue(borrowAmount.toString())
|
||||
},
|
||||
[setAmountOutFormValue]
|
||||
[setAmountOutFormValue],
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -189,7 +192,7 @@ const SwapForm = () => {
|
|||
}
|
||||
setAmountInFormValue(e.value)
|
||||
},
|
||||
[swapMode, setAmountInFormValue]
|
||||
[swapMode, setAmountInFormValue],
|
||||
)
|
||||
|
||||
const handleAmountOutChange = useCallback(
|
||||
|
@ -202,7 +205,7 @@ const SwapForm = () => {
|
|||
}
|
||||
setAmountOutFormValue(e.value)
|
||||
},
|
||||
[swapMode, setAmountOutFormValue]
|
||||
[swapMode, setAmountOutFormValue],
|
||||
)
|
||||
|
||||
const handleTokenInSelect = useCallback((mintAddress: string) => {
|
||||
|
@ -227,20 +230,23 @@ const SwapForm = () => {
|
|||
setShowTokenSelect(undefined)
|
||||
}, [])
|
||||
|
||||
const handleSwitchTokens = useCallback(() => {
|
||||
if (amountInAsDecimal?.gt(0) && amountOutAsDecimal.gte(0)) {
|
||||
setAmountInFormValue(amountOutAsDecimal.toString())
|
||||
}
|
||||
const inputBank = mangoStore.getState().swap.inputBank
|
||||
const outputBank = mangoStore.getState().swap.outputBank
|
||||
set((s) => {
|
||||
s.swap.inputBank = outputBank
|
||||
s.swap.outputBank = inputBank
|
||||
})
|
||||
setAnimateSwitchArrow(
|
||||
(prevanimateSwitchArrow) => prevanimateSwitchArrow + 1
|
||||
)
|
||||
}, [setAmountInFormValue, amountOutAsDecimal, amountInAsDecimal])
|
||||
const handleSwitchTokens = useCallback(
|
||||
(amountIn: Decimal, amountOut: Decimal) => {
|
||||
if (amountIn?.gt(0) && amountOut.gte(0)) {
|
||||
setAmountInFormValue(amountOut.toString())
|
||||
}
|
||||
const inputBank = mangoStore.getState().swap.inputBank
|
||||
const outputBank = mangoStore.getState().swap.outputBank
|
||||
set((s) => {
|
||||
s.swap.inputBank = outputBank
|
||||
s.swap.outputBank = inputBank
|
||||
})
|
||||
setAnimateSwitchArrow(
|
||||
(prevanimateSwitchArrow) => prevanimateSwitchArrow + 1,
|
||||
)
|
||||
},
|
||||
[setAmountInFormValue],
|
||||
)
|
||||
|
||||
const maintProjectedHealth = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
|
@ -266,7 +272,7 @@ const SwapForm = () => {
|
|||
uiTokenAmount: amountOutAsDecimal.toNumber(),
|
||||
},
|
||||
],
|
||||
HealthType.maint
|
||||
HealthType.maint,
|
||||
)
|
||||
return simulatedHealthRatio > 100
|
||||
? 100
|
||||
|
@ -327,7 +333,7 @@ const SwapForm = () => {
|
|||
/>
|
||||
</Transition>
|
||||
<EnterBottomExitBottom
|
||||
className="thin-scroll absolute bottom-0 left-0 z-10 h-full w-full overflow-auto bg-th-bkg-1 p-6 pb-0"
|
||||
className="thin-scroll absolute bottom-0 left-0 z-10 h-full w-full overflow-hidden bg-th-bkg-1 p-6 pb-0"
|
||||
show={!!showTokenSelect}
|
||||
>
|
||||
<SwapFormTokenList
|
||||
|
@ -395,7 +401,7 @@ const SwapForm = () => {
|
|||
<span className="absolute right-3 bottom-1.5 text-xxs text-th-fgd-4">
|
||||
{inputBank
|
||||
? formatCurrencyValue(
|
||||
inputBank.uiPrice * Number(amountInFormValue)
|
||||
inputBank.uiPrice * Number(amountInFormValue),
|
||||
)
|
||||
: '–'}
|
||||
</span>
|
||||
|
@ -404,7 +410,9 @@ const SwapForm = () => {
|
|||
<div className="-mb-2 flex justify-center">
|
||||
<button
|
||||
className="rounded-full border border-th-bkg-4 p-1.5 text-th-fgd-3 focus-visible:border-th-fgd-4 md:hover:text-th-active"
|
||||
onClick={handleSwitchTokens}
|
||||
onClick={() =>
|
||||
handleSwitchTokens(amountInAsDecimal, amountOutAsDecimal)
|
||||
}
|
||||
>
|
||||
<ArrowDownIcon
|
||||
className="h-5 w-5"
|
||||
|
@ -426,8 +434,8 @@ const SwapForm = () => {
|
|||
onClick={() =>
|
||||
setBorrowAmountOut(
|
||||
outputTokenBalanceBorrow.toFixed(
|
||||
outputBank?.mintDecimals || 9
|
||||
)
|
||||
outputBank?.mintDecimals || 9,
|
||||
),
|
||||
)
|
||||
}
|
||||
value={outputTokenBalanceBorrow}
|
||||
|
@ -468,7 +476,7 @@ const SwapForm = () => {
|
|||
<span className="absolute right-3 bottom-1.5 text-xxs text-th-fgd-4">
|
||||
{outputBank
|
||||
? formatCurrencyValue(
|
||||
outputBank.uiPrice * Number(amountOutFormValue)
|
||||
outputBank.uiPrice * Number(amountOutFormValue),
|
||||
)
|
||||
: '–'}
|
||||
</span>
|
||||
|
@ -480,13 +488,13 @@ const SwapForm = () => {
|
|||
<SwapSlider
|
||||
useMargin={useMargin}
|
||||
amount={amountInAsDecimal.toNumber()}
|
||||
onChange={(v) => setAmountInFormValue(v, true)}
|
||||
onChange={setAmountFromSlider}
|
||||
step={1 / 10 ** (inputBank?.mintDecimals || 6)}
|
||||
/>
|
||||
) : (
|
||||
<PercentageSelectButtons
|
||||
amountIn={amountInAsDecimal.toString()}
|
||||
setAmountIn={(v) => setAmountInFormValue(v, true)}
|
||||
setAmountIn={setAmountFromSlider}
|
||||
useMargin={useMargin}
|
||||
/>
|
||||
)}
|
||||
|
@ -554,6 +562,7 @@ const SwapForm = () => {
|
|||
className="text-th-fgd-3"
|
||||
checked={useMargin}
|
||||
onChange={handleSetMargin}
|
||||
small
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
|
@ -592,9 +601,8 @@ const SwapFormSubmitButton = ({
|
|||
useMargin: boolean
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { connected } = useWallet()
|
||||
const { connected, connect } = useWallet()
|
||||
const { amount: tokenMax, amountWithBorrow } = useTokenMax(useMargin)
|
||||
const { handleConnect } = useEnhancedWallet()
|
||||
|
||||
const showInsufficientBalance = useMargin
|
||||
? amountWithBorrow.lt(amountIn)
|
||||
|
@ -607,7 +615,7 @@ const SwapFormSubmitButton = ({
|
|||
!amountOut ||
|
||||
!selectedRoute)
|
||||
|
||||
const onClick = connected ? () => setShowConfirm(true) : handleConnect
|
||||
const onClick = connected ? () => setShowConfirm(true) : connect
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { memo, useMemo, useEffect, useRef } from 'react'
|
||||
import { memo, useMemo, useEffect, useRef, useState, ChangeEvent } from 'react'
|
||||
import { Token } from '../../types/jupiter'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { IconButton } from '../shared/Button'
|
||||
import { XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import { MagnifyingGlassIcon, XMarkIcon } from '@heroicons/react/20/solid'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Decimal from 'decimal.js'
|
||||
import { getTokenInMax } from './useTokenMax'
|
||||
|
@ -13,32 +13,33 @@ import { PublicKey } from '@solana/web3.js'
|
|||
import FormatNumericValue from '@components/shared/FormatNumericValue'
|
||||
import { formatTokenSymbol } from 'utils/tokens'
|
||||
import TokenLogo from '@components/shared/TokenLogo'
|
||||
import Input from '@components/forms/Input'
|
||||
|
||||
// const generateSearchTerm = (item: Token, searchValue: string) => {
|
||||
// const normalizedSearchValue = searchValue.toLowerCase()
|
||||
// const values = `${item.symbol} ${item.name}`.toLowerCase()
|
||||
const generateSearchTerm = (item: Token, searchValue: string) => {
|
||||
const normalizedSearchValue = searchValue.toLowerCase()
|
||||
const values = `${item.symbol} ${item.name}`.toLowerCase()
|
||||
|
||||
// const isMatchingWithSymbol =
|
||||
// item.symbol.toLowerCase().indexOf(normalizedSearchValue) >= 0
|
||||
// const matchingSymbolPercent = isMatchingWithSymbol
|
||||
// ? normalizedSearchValue.length / item.symbol.length
|
||||
// : 0
|
||||
const isMatchingWithSymbol =
|
||||
item.symbol.toLowerCase().indexOf(normalizedSearchValue) >= 0
|
||||
const matchingSymbolPercent = isMatchingWithSymbol
|
||||
? normalizedSearchValue.length / item.symbol.length
|
||||
: 0
|
||||
|
||||
// return {
|
||||
// token: item,
|
||||
// matchingIdx: values.indexOf(normalizedSearchValue),
|
||||
// matchingSymbolPercent,
|
||||
// }
|
||||
// }
|
||||
return {
|
||||
token: item,
|
||||
matchingIdx: values.indexOf(normalizedSearchValue),
|
||||
matchingSymbolPercent,
|
||||
}
|
||||
}
|
||||
|
||||
// const startSearch = (items: Token[], searchValue: string) => {
|
||||
// return items
|
||||
// .map((item) => generateSearchTerm(item, searchValue))
|
||||
// .filter((item) => item.matchingIdx >= 0)
|
||||
// .sort((i1, i2) => i1.matchingIdx - i2.matchingIdx)
|
||||
// .sort((i1, i2) => i2.matchingSymbolPercent - i1.matchingSymbolPercent)
|
||||
// .map((item) => item.token)
|
||||
// }
|
||||
const startSearch = (items: Token[], searchValue: string) => {
|
||||
return items
|
||||
.map((item) => generateSearchTerm(item, searchValue))
|
||||
.filter((item) => item.matchingIdx >= 0)
|
||||
.sort((i1, i2) => i1.matchingIdx - i2.matchingIdx)
|
||||
.sort((i1, i2) => i2.matchingSymbolPercent - i1.matchingSymbolPercent)
|
||||
.map((item) => item.token)
|
||||
}
|
||||
|
||||
const TokenItem = ({
|
||||
token,
|
||||
|
@ -114,8 +115,6 @@ const TokenItem = ({
|
|||
)
|
||||
}
|
||||
|
||||
// const popularTokenSymbols = ['USDC', 'SOL', 'USDT', 'MNGO', 'BTC']
|
||||
|
||||
interface TokenInfoWithAmounts extends Token {
|
||||
amount?: Decimal
|
||||
amountWithBorrow?: Decimal
|
||||
|
@ -132,22 +131,14 @@ const SwapFormTokenList = ({
|
|||
type: 'input' | 'output' | undefined
|
||||
useMargin: boolean
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'swap'])
|
||||
// const [search, setSearch] = useState('')
|
||||
const { t } = useTranslation(['common', 'search', 'swap'])
|
||||
const [search, setSearch] = useState('')
|
||||
const { mangoTokens } = useJupiterMints()
|
||||
const inputBank = mangoStore((s) => s.swap.inputBank)
|
||||
const outputBank = mangoStore((s) => s.swap.outputBank)
|
||||
const { group } = useMangoGroup()
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const focusRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
// const popularTokens = useMemo(() => {
|
||||
// return tokens.filter((token) => {
|
||||
// return !token?.name || !token?.symbol
|
||||
// ? false
|
||||
// : popularTokenSymbols.includes(token.symbol)
|
||||
// })
|
||||
// }, [tokens])
|
||||
const focusRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
function onEscape(e: KeyboardEvent) {
|
||||
|
@ -175,7 +166,7 @@ const SwapFormTokenList = ({
|
|||
new PublicKey(token.address),
|
||||
outputBank.mint,
|
||||
group,
|
||||
useMargin
|
||||
useMargin,
|
||||
)
|
||||
return { ...token, ...max }
|
||||
})
|
||||
|
@ -183,7 +174,7 @@ const SwapFormTokenList = ({
|
|||
.sort((a, b) =>
|
||||
useMargin
|
||||
? Number(b.amountWithBorrow) - Number(a.amountWithBorrow)
|
||||
: Number(b.amount) - Number(a.amount)
|
||||
: Number(b.amount) - Number(a.amount),
|
||||
)
|
||||
|
||||
return filteredSortedTokens
|
||||
|
@ -202,12 +193,11 @@ const SwapFormTokenList = ({
|
|||
}
|
||||
}, [mangoTokens, inputBank, outputBank, mangoAccount, group, useMargin, type])
|
||||
|
||||
// const handleUpdateSearch = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
// setSearch(e.target.value)
|
||||
// }
|
||||
const handleUpdateSearch = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setSearch(e.target.value)
|
||||
}
|
||||
|
||||
// const sortedTokens = search ? startSearch(tokenInfos, search) : tokenInfos
|
||||
const sortedTokens = tokenInfos
|
||||
const sortedTokens = search ? startSearch(tokenInfos, search) : tokenInfos
|
||||
|
||||
useEffect(() => {
|
||||
if (focusRef?.current) {
|
||||
|
@ -228,71 +218,43 @@ const SwapFormTokenList = ({
|
|||
className="absolute top-2 right-2 text-th-fgd-3 hover:text-th-fgd-2"
|
||||
onClick={onClose}
|
||||
hideBg
|
||||
ref={focusRef}
|
||||
>
|
||||
<XMarkIcon className="h-6 w-6" />
|
||||
</IconButton>
|
||||
{/* No need for search/popular tokens until we have more tokens */}
|
||||
|
||||
{/* <div className="flex items-center text-th-fgd-4">
|
||||
<div className="relative mb-4">
|
||||
<Input
|
||||
className="pl-10"
|
||||
type="text"
|
||||
placeholder="Search by token or paste address"
|
||||
prefix={<MagnifyingGlassIcon className="h-5 w-5" />}
|
||||
autoFocus
|
||||
value={search}
|
||||
onChange={handleUpdateSearch}
|
||||
ref={focusRef}
|
||||
/>
|
||||
<MagnifyingGlassIcon className="absolute left-3 top-3.5 h-5 w-5" />
|
||||
</div>
|
||||
{popularTokens.length ? (
|
||||
<div className="mt-4 flex flex-wrap">
|
||||
{popularTokens.map((token) => {
|
||||
let logoURI
|
||||
if (mangoTokens.length) {
|
||||
logoURI = mangoTokens.find(
|
||||
(t) => t.address === token.address
|
||||
)!.logoURI
|
||||
}
|
||||
const disabled =
|
||||
(type === 'input' && token.symbol === outputBank?.name) ||
|
||||
(type === 'output' && token.symbol === inputBank?.name)
|
||||
return (
|
||||
<button
|
||||
className={`${
|
||||
disabled ? 'opacity-20' : 'hover:border-th-fgd-3'
|
||||
} mx-1 mb-2 flex items-center rounded-md border border-th-bkg-4 py-1 px-3 focus:border-th-fgd-2`}
|
||||
onClick={() => onTokenSelect(token.address)}
|
||||
disabled={disabled}
|
||||
key={token.address}
|
||||
>
|
||||
{logoURI ? (
|
||||
<Image alt="" width="16" height="16" src={logoURI} />
|
||||
) : (
|
||||
<QuestionMarkCircleIcon className="h-5 w-5 text-th-fgd-3" />
|
||||
)}
|
||||
<span className="ml-1.5 text-th-fgd-1">{token.symbol}</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
) : null} */}
|
||||
{/* <div className="my-2 border-t border-th-bkg-4"></div> */}
|
||||
<div className="mb-2 flex justify-between rounded bg-th-bkg-2 p-2">
|
||||
<div className="flex justify-between rounded bg-th-bkg-2 p-2">
|
||||
<p className="text-xs text-th-fgd-4">{t('token')}</p>
|
||||
{type === 'input' ? (
|
||||
<p className="text-xs text-th-fgd-4">{t('max')}</p>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="overflow-auto pb-2">
|
||||
{sortedTokens.map((token) => (
|
||||
<TokenItem
|
||||
key={token.address}
|
||||
token={token}
|
||||
onSubmit={onTokenSelect}
|
||||
useMargin={useMargin}
|
||||
type={type}
|
||||
/>
|
||||
))}
|
||||
<div className="thin-scroll h-[calc(100%-128px)] overflow-auto py-2">
|
||||
{sortedTokens?.length ? (
|
||||
sortedTokens.map((token) => (
|
||||
<TokenItem
|
||||
key={token.address}
|
||||
token={token}
|
||||
onSubmit={onTokenSelect}
|
||||
useMargin={useMargin}
|
||||
type={type}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="mt-2 rounded-md border border-th-bkg-3 p-3">
|
||||
<p className="text-center">{t('search:no-results')}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -36,7 +36,7 @@ const SwapHistoryTable = () => {
|
|||
const showTableView = width ? width > breakpoints.md : false
|
||||
const [preferredExplorer] = useLocalStorageState(
|
||||
PREFERRED_EXPLORER_KEY,
|
||||
EXPLORERS[0]
|
||||
EXPLORERS[0],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -56,7 +56,7 @@ const SwapHistoryTable = () => {
|
|||
actions.fetchSwapHistory(
|
||||
mangoAccountAddress,
|
||||
0,
|
||||
offset + PAGINATION_PAGE_LENGTH
|
||||
offset + PAGINATION_PAGE_LENGTH,
|
||||
)
|
||||
}, [actions, offset, mangoAccountAddress])
|
||||
|
||||
|
@ -196,7 +196,7 @@ const SwapHistoryTable = () => {
|
|||
<div className="flex items-center justify-end">
|
||||
<Tooltip
|
||||
content={`View on ${t(
|
||||
`settings:${preferredExplorer.name}`
|
||||
`settings:${preferredExplorer.name}`,
|
||||
)}`}
|
||||
placement="top-end"
|
||||
>
|
||||
|
|
|
@ -19,7 +19,7 @@ const SwapPage = () => {
|
|||
const actions = mangoStore((s) => s.actions)
|
||||
const [showSwapIntro, setShowSwapIntro] = useLocalStorage(
|
||||
SHOW_SWAP_INTRO_MODAL,
|
||||
true
|
||||
true,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -59,17 +59,17 @@ type JupiterRouteInfoProps = {
|
|||
|
||||
const deserializeJupiterIxAndAlt = async (
|
||||
connection: Connection,
|
||||
swapTransaction: string
|
||||
swapTransaction: string,
|
||||
): Promise<[TransactionInstruction[], AddressLookupTableAccount[]]> => {
|
||||
const parsedSwapTransaction = VersionedTransaction.deserialize(
|
||||
Buffer.from(swapTransaction, 'base64')
|
||||
Buffer.from(swapTransaction, 'base64'),
|
||||
)
|
||||
const message = parsedSwapTransaction.message
|
||||
// const lookups = message.addressTableLookups
|
||||
const addressLookupTablesResponses = await Promise.all(
|
||||
message.addressTableLookups.map((alt) =>
|
||||
connection.getAddressLookupTable(alt.accountKey)
|
||||
)
|
||||
connection.getAddressLookupTable(alt.accountKey),
|
||||
),
|
||||
)
|
||||
const addressLookupTables: AddressLookupTableAccount[] =
|
||||
addressLookupTablesResponses
|
||||
|
@ -87,7 +87,7 @@ const prepareMangoRouterInstructions = async (
|
|||
selectedRoute: RouteInfo,
|
||||
inputMint: PublicKey,
|
||||
outputMint: PublicKey,
|
||||
userPublicKey: PublicKey
|
||||
userPublicKey: PublicKey,
|
||||
): Promise<[TransactionInstruction[], AddressLookupTableAccount[]]> => {
|
||||
if (!selectedRoute || !selectedRoute.mints || !selectedRoute.instructions) {
|
||||
return [[], []]
|
||||
|
@ -97,8 +97,8 @@ const prepareMangoRouterInstructions = async (
|
|||
...selectedRoute.mints.filter(
|
||||
(routeMint) =>
|
||||
!mintsToFilterOut.find((filterOutMint) =>
|
||||
filterOutMint.equals(routeMint)
|
||||
)
|
||||
filterOutMint.equals(routeMint),
|
||||
),
|
||||
),
|
||||
]
|
||||
const additionalInstructions = []
|
||||
|
@ -106,7 +106,7 @@ const prepareMangoRouterInstructions = async (
|
|||
const ix = await createAssociatedTokenAccountIdempotentInstruction(
|
||||
userPublicKey,
|
||||
userPublicKey,
|
||||
mint
|
||||
mint,
|
||||
)
|
||||
additionalInstructions.push(ix)
|
||||
}
|
||||
|
@ -117,13 +117,13 @@ const prepareMangoRouterInstructions = async (
|
|||
return [instructions, []]
|
||||
}
|
||||
|
||||
const fetchJupiterTransaction = async (
|
||||
export const fetchJupiterTransaction = async (
|
||||
connection: Connection,
|
||||
selectedRoute: RouteInfo,
|
||||
userPublicKey: PublicKey,
|
||||
slippage: number,
|
||||
inputMint: PublicKey,
|
||||
outputMint: PublicKey
|
||||
outputMint: PublicKey,
|
||||
): Promise<[TransactionInstruction[], AddressLookupTableAccount[]]> => {
|
||||
const transactions = await (
|
||||
await fetch('https://quote-api.jup.ag/v4/swap', {
|
||||
|
@ -148,7 +148,7 @@ const fetchJupiterTransaction = async (
|
|||
|
||||
const [ixs, alts] = await deserializeJupiterIxAndAlt(
|
||||
connection,
|
||||
swapTransaction
|
||||
swapTransaction,
|
||||
)
|
||||
|
||||
const isSetupIx = (pk: PublicKey): boolean =>
|
||||
|
@ -202,14 +202,14 @@ const SwapReviewRouteInfo = ({
|
|||
const outputBank = mangoStore((s) => s.swap.outputBank)
|
||||
const [soundSettings] = useLocalStorageState(
|
||||
SOUND_SETTINGS_KEY,
|
||||
INITIAL_SOUND_SETTINGS
|
||||
INITIAL_SOUND_SETTINGS,
|
||||
)
|
||||
const focusRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
const amountOut = useMemo(() => {
|
||||
if (!selectedRoute || !outputTokenInfo) return
|
||||
return new Decimal(selectedRoute.outAmount.toString()).div(
|
||||
10 ** outputTokenInfo.decimals
|
||||
10 ** outputTokenInfo.decimals,
|
||||
)
|
||||
}, [selectedRoute, outputTokenInfo])
|
||||
|
||||
|
@ -228,7 +228,7 @@ const SwapReviewRouteInfo = ({
|
|||
if (inputId && outputId) {
|
||||
try {
|
||||
const results = await fetch(
|
||||
`https://api.coingecko.com/api/v3/simple/price?ids=${inputId},${outputId}&vs_currencies=usd`
|
||||
`https://api.coingecko.com/api/v3/simple/price?ids=${inputId},${outputId}&vs_currencies=usd`,
|
||||
)
|
||||
const json = await results.json()
|
||||
if (json[inputId]?.usd && json[outputId]?.usd) {
|
||||
|
@ -275,7 +275,7 @@ const SwapReviewRouteInfo = ({
|
|||
selectedRoute,
|
||||
inputBank.mint,
|
||||
outputBank.mint,
|
||||
mangoAccount.owner
|
||||
mangoAccount.owner,
|
||||
)
|
||||
: await fetchJupiterTransaction(
|
||||
connection,
|
||||
|
@ -283,7 +283,7 @@ const SwapReviewRouteInfo = ({
|
|||
wallet.publicKey,
|
||||
slippage,
|
||||
inputBank.mint,
|
||||
outputBank.mint
|
||||
outputBank.mint,
|
||||
)
|
||||
|
||||
try {
|
||||
|
@ -358,8 +358,8 @@ const SwapReviewRouteInfo = ({
|
|||
.div(amountOut)
|
||||
.minus(
|
||||
new Decimal(coingeckoPrices?.outputCoingeckoPrice).div(
|
||||
coingeckoPrices?.inputCoingeckoPrice
|
||||
)
|
||||
coingeckoPrices?.inputCoingeckoPrice,
|
||||
),
|
||||
)
|
||||
.div(amountIn.div(amountOut))
|
||||
.mul(100)
|
||||
|
@ -540,7 +540,7 @@ const SwapReviewRouteInfo = ({
|
|||
token: inputTokenInfo?.symbol,
|
||||
rate: formatNumericValue(
|
||||
inputBank!.getBorrowRateUi(),
|
||||
2
|
||||
2,
|
||||
),
|
||||
})
|
||||
: t('swap:tooltip-borrow-no-balance', {
|
||||
|
@ -548,7 +548,7 @@ const SwapReviewRouteInfo = ({
|
|||
token: inputTokenInfo?.symbol,
|
||||
rate: formatNumericValue(
|
||||
inputBank!.getBorrowRateUi(),
|
||||
2
|
||||
2,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
@ -667,7 +667,7 @@ const SwapReviewRouteInfo = ({
|
|||
) : (
|
||||
selectedRoute?.marketInfos.map((info, index) => {
|
||||
const feeToken = jupiterTokens.find(
|
||||
(item) => item?.address === info.lpFee?.mint
|
||||
(item) => item?.address === info.lpFee?.mint,
|
||||
)
|
||||
return (
|
||||
<div className="flex justify-between" key={index}>
|
||||
|
@ -690,7 +690,7 @@ const SwapReviewRouteInfo = ({
|
|||
undefined,
|
||||
{
|
||||
maximumSignificantDigits: 2,
|
||||
}
|
||||
},
|
||||
)}
|
||||
%)
|
||||
</p>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import { useCallback } from 'react'
|
||||
import LeverageSlider from '../shared/LeverageSlider'
|
||||
import { useTokenMax } from './useTokenMax'
|
||||
|
||||
|
@ -17,10 +16,6 @@ const SwapSlider = ({
|
|||
const { mangoAccount } = useMangoAccount()
|
||||
const { amount: tokenMax, amountWithBorrow } = useTokenMax(useMargin)
|
||||
|
||||
const handleChange = useCallback((x: string) => {
|
||||
onChange(x)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
{!mangoAccount ? (
|
||||
|
@ -36,7 +31,7 @@ const SwapSlider = ({
|
|||
leverageMax={
|
||||
useMargin ? amountWithBorrow.toNumber() : tokenMax.toNumber()
|
||||
}
|
||||
onChange={handleChange}
|
||||
onChange={onChange}
|
||||
step={step}
|
||||
/>
|
||||
)}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue