use jupiter api and hooks
This commit is contained in:
parent
5727d0bc7f
commit
203c571bc5
|
@ -1,8 +1,11 @@
|
||||||
export const fetchChartData = async (
|
export const fetchChartData = async (
|
||||||
baseTokenId: string,
|
baseTokenId: string | undefined,
|
||||||
quoteTokenId: string,
|
quoteTokenId: string | undefined,
|
||||||
daysToShow: number
|
daysToShow: number
|
||||||
) => {
|
) => {
|
||||||
|
console.log('fetching chart:', baseTokenId, quoteTokenId)
|
||||||
|
|
||||||
|
if (!baseTokenId || !quoteTokenId) return
|
||||||
try {
|
try {
|
||||||
const [inputResponse, outputResponse] = await Promise.all([
|
const [inputResponse, outputResponse] = await Promise.all([
|
||||||
fetch(
|
fetch(
|
||||||
|
|
|
@ -1,42 +1,20 @@
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import useInterval from '@components/shared/useInterval'
|
|
||||||
import { PublicKey } from '@solana/web3.js'
|
import { PublicKey } from '@solana/web3.js'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { MangoAccount } from '@blockworks-foundation/mango-v4'
|
import { MangoAccount } from '@blockworks-foundation/mango-v4'
|
||||||
import useMangoAccount from 'hooks/useMangoAccount'
|
import useMangoAccount from 'hooks/useMangoAccount'
|
||||||
|
|
||||||
const rehydrateStore = async () => {
|
|
||||||
const actions = mangoStore.getState().actions
|
|
||||||
actions.fetchGroup()
|
|
||||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
|
||||||
if (mangoAccount) {
|
|
||||||
// actions.reloadMangoAccount()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const HydrateStore = () => {
|
const HydrateStore = () => {
|
||||||
const actions = mangoStore((s) => s.actions)
|
const actions = mangoStore((s) => s.actions)
|
||||||
const { mangoAccount } = useMangoAccount()
|
const { mangoAccount } = useMangoAccount()
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
|
||||||
|
|
||||||
useInterval(() => {
|
|
||||||
rehydrateStore()
|
|
||||||
}, 5000)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
await actions.fetchGroup()
|
await actions.fetchGroup()
|
||||||
actions.fetchJupiterTokens()
|
|
||||||
}
|
}
|
||||||
fetchData()
|
fetchData()
|
||||||
}, [])
|
}, [actions])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (jupiterTokens.length) {
|
|
||||||
actions.fetchCoingeckoPrices()
|
|
||||||
}
|
|
||||||
}, [jupiterTokens])
|
|
||||||
|
|
||||||
// watch selected Mango Account for changes
|
// watch selected Mango Account for changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import Tooltip from './shared/Tooltip'
|
||||||
import { formatTokenSymbol } from 'utils/tokens'
|
import { formatTokenSymbol } from 'utils/tokens'
|
||||||
import RepayModal from './modals/RepayModal'
|
import RepayModal from './modals/RepayModal'
|
||||||
import useMangoAccount from 'hooks/useMangoAccount'
|
import useMangoAccount from 'hooks/useMangoAccount'
|
||||||
|
import useJupiterMints from '../hooks/useJupiterMints'
|
||||||
|
|
||||||
const TokenList = () => {
|
const TokenList = () => {
|
||||||
const { t } = useTranslation(['common', 'token', 'trade'])
|
const { t } = useTranslation(['common', 'token', 'trade'])
|
||||||
|
@ -38,7 +39,7 @@ const TokenList = () => {
|
||||||
const { mangoAccount } = useMangoAccount()
|
const { mangoAccount } = useMangoAccount()
|
||||||
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
|
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
|
||||||
const group = mangoStore((s) => s.group)
|
const group = mangoStore((s) => s.group)
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
const totalInterestData = mangoStore(
|
const totalInterestData = mangoStore(
|
||||||
(s) => s.mangoAccount.stats.interestTotals.data
|
(s) => s.mangoAccount.stats.interestTotals.data
|
||||||
)
|
)
|
||||||
|
@ -136,8 +137,8 @@ const TokenList = () => {
|
||||||
const oraclePrice = bank.uiPrice
|
const oraclePrice = bank.uiPrice
|
||||||
|
|
||||||
let logoURI
|
let logoURI
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens?.length) {
|
||||||
logoURI = jupiterTokens.find(
|
logoURI = mangoTokens.find(
|
||||||
(t) => t.address === bank.mint.toString()
|
(t) => t.address === bank.mint.toString()
|
||||||
)!.logoURI
|
)!.logoURI
|
||||||
}
|
}
|
||||||
|
@ -263,7 +264,7 @@ export default TokenList
|
||||||
const MobileTokenListItem = ({ bank }: { bank: Bank }) => {
|
const MobileTokenListItem = ({ bank }: { bank: Bank }) => {
|
||||||
const { t } = useTranslation(['common', 'token'])
|
const { t } = useTranslation(['common', 'token'])
|
||||||
const [showTokenDetails, setShowTokenDetails] = useState(false)
|
const [showTokenDetails, setShowTokenDetails] = useState(false)
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
|
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
|
||||||
const { mangoAccount } = useMangoAccount()
|
const { mangoAccount } = useMangoAccount()
|
||||||
const totalInterestData = mangoStore(
|
const totalInterestData = mangoStore(
|
||||||
|
@ -274,8 +275,8 @@ const MobileTokenListItem = ({ bank }: { bank: Bank }) => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
let logoURI
|
let logoURI
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens?.length) {
|
||||||
logoURI = jupiterTokens.find(
|
logoURI = mangoTokens.find(
|
||||||
(t) => t.address === bank.mint.toString()
|
(t) => t.address === bank.mint.toString()
|
||||||
)!.logoURI
|
)!.logoURI
|
||||||
}
|
}
|
||||||
|
@ -420,7 +421,7 @@ const ActionsMenu = ({
|
||||||
// const set = mangoStore.getState().set
|
// const set = mangoStore.getState().set
|
||||||
// const router = useRouter()
|
// const router = useRouter()
|
||||||
// const { asPath } = router
|
// const { asPath } = router
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
|
|
||||||
const handleShowActionModals = useCallback(
|
const handleShowActionModals = useCallback(
|
||||||
(token: string, action: 'borrow' | 'deposit' | 'withdraw' | 'repay') => {
|
(token: string, action: 'borrow' | 'deposit' | 'withdraw' | 'repay') => {
|
||||||
|
@ -437,7 +438,7 @@ const ActionsMenu = ({
|
||||||
)
|
)
|
||||||
|
|
||||||
// const handleBuy = useCallback(() => {
|
// const handleBuy = useCallback(() => {
|
||||||
// const outputTokenInfo = jupiterTokens.find(
|
// const outputTokenInfo = mangoTokens.find(
|
||||||
// (t: any) => t.address === bank.mint.toString()
|
// (t: any) => t.address === bank.mint.toString()
|
||||||
// )
|
// )
|
||||||
// set((s) => {
|
// set((s) => {
|
||||||
|
@ -447,10 +448,10 @@ const ActionsMenu = ({
|
||||||
// if (asPath === '/') {
|
// if (asPath === '/') {
|
||||||
// router.push('/swap', undefined, { shallow: true })
|
// router.push('/swap', undefined, { shallow: true })
|
||||||
// }
|
// }
|
||||||
// }, [bank, router, asPath, set, jupiterTokens])
|
// }, [bank, router, asPath, set, mangoTokens])
|
||||||
|
|
||||||
// const handleSell = useCallback(() => {
|
// const handleSell = useCallback(() => {
|
||||||
// const inputTokenInfo = jupiterTokens.find(
|
// const inputTokenInfo = mangoTokens.find(
|
||||||
// (t: any) => t.address === bank.mint.toString()
|
// (t: any) => t.address === bank.mint.toString()
|
||||||
// )
|
// )
|
||||||
// set((s) => {
|
// set((s) => {
|
||||||
|
@ -460,13 +461,12 @@ const ActionsMenu = ({
|
||||||
// if (asPath === '/') {
|
// if (asPath === '/') {
|
||||||
// router.push('/swap', undefined, { shallow: true })
|
// router.push('/swap', undefined, { shallow: true })
|
||||||
// }
|
// }
|
||||||
// }, [router, asPath, set, bank, jupiterTokens])
|
// }, [router, asPath, set, bank, mangoTokens])
|
||||||
|
|
||||||
const logoURI = useMemo(() => {
|
const logoURI = useMemo(() => {
|
||||||
if (!bank || !jupiterTokens.length) return ''
|
if (!bank || !mangoTokens?.length) return ''
|
||||||
return jupiterTokens.find((t) => t.address === bank.mint.toString())
|
return mangoTokens.find((t) => t.address === bank.mint.toString())?.logoURI
|
||||||
?.logoURI
|
}, [bank, mangoTokens])
|
||||||
}, [bank, jupiterTokens])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import Image from 'next/legacy/image'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import { formatDecimal } from '../../utils/numbers'
|
import { formatDecimal } from '../../utils/numbers'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
|
|
||||||
const ActionTokenItem = ({
|
const ActionTokenItem = ({
|
||||||
bank,
|
bank,
|
||||||
|
@ -18,17 +19,15 @@ const ActionTokenItem = ({
|
||||||
showDepositRates?: boolean
|
showDepositRates?: boolean
|
||||||
}) => {
|
}) => {
|
||||||
const { mint, name } = bank
|
const { mint, name } = bank
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
|
|
||||||
const logoUri = useMemo(() => {
|
const logoUri = useMemo(() => {
|
||||||
let logoURI
|
let logoURI
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens?.length) {
|
||||||
logoURI = jupiterTokens.find(
|
logoURI = mangoTokens.find((t) => t.address === mint.toString())!.logoURI
|
||||||
(t) => t.address === mint.toString()
|
|
||||||
)!.logoURI
|
|
||||||
}
|
}
|
||||||
return logoURI
|
return logoURI
|
||||||
}, [mint, jupiterTokens])
|
}, [mint, mangoTokens])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -28,6 +28,7 @@ import MaxAmountButton from '@components/shared/MaxAmountButton'
|
||||||
import HealthImpactTokenChange from '@components/HealthImpactTokenChange'
|
import HealthImpactTokenChange from '@components/HealthImpactTokenChange'
|
||||||
import Tooltip from '@components/shared/Tooltip'
|
import Tooltip from '@components/shared/Tooltip'
|
||||||
import useMangoAccount from 'hooks/useMangoAccount'
|
import useMangoAccount from 'hooks/useMangoAccount'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
|
|
||||||
interface BorrowModalProps {
|
interface BorrowModalProps {
|
||||||
token?: string
|
token?: string
|
||||||
|
@ -45,7 +46,7 @@ function BorrowModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
||||||
)
|
)
|
||||||
const [showTokenList, setShowTokenList] = useState(false)
|
const [showTokenList, setShowTokenList] = useState(false)
|
||||||
const [sizePercentage, setSizePercentage] = useState('')
|
const [sizePercentage, setSizePercentage] = useState('')
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
const { mangoAccount } = useMangoAccount()
|
const { mangoAccount } = useMangoAccount()
|
||||||
|
|
||||||
const bank = useMemo(() => {
|
const bank = useMemo(() => {
|
||||||
|
@ -55,13 +56,13 @@ function BorrowModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
||||||
|
|
||||||
const logoUri = useMemo(() => {
|
const logoUri = useMemo(() => {
|
||||||
let logoURI
|
let logoURI
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens?.length) {
|
||||||
logoURI = jupiterTokens.find(
|
logoURI = mangoTokens.find(
|
||||||
(t) => t.address === bank?.mint.toString()
|
(t) => t.address === bank?.mint.toString()
|
||||||
)!.logoURI
|
)!.logoURI
|
||||||
}
|
}
|
||||||
return logoURI
|
return logoURI
|
||||||
}, [jupiterTokens, bank])
|
}, [mangoTokens, bank])
|
||||||
|
|
||||||
const tokenMax = useMemo(() => {
|
const tokenMax = useMemo(() => {
|
||||||
const group = mangoStore.getState().group
|
const group = mangoStore.getState().group
|
||||||
|
|
|
@ -31,6 +31,7 @@ import Tooltip from '@components/shared/Tooltip'
|
||||||
import HealthImpactTokenChange from '@components/HealthImpactTokenChange'
|
import HealthImpactTokenChange from '@components/HealthImpactTokenChange'
|
||||||
import useSolBalance from 'hooks/useSolBalance'
|
import useSolBalance from 'hooks/useSolBalance'
|
||||||
import SolBalanceWarnings from '@components/shared/SolBalanceWarnings'
|
import SolBalanceWarnings from '@components/shared/SolBalanceWarnings'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
|
|
||||||
interface DepositModalProps {
|
interface DepositModalProps {
|
||||||
token?: string
|
token?: string
|
||||||
|
@ -69,7 +70,7 @@ function DepositModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
||||||
)
|
)
|
||||||
const [showTokenList, setShowTokenList] = useState(false)
|
const [showTokenList, setShowTokenList] = useState(false)
|
||||||
const [sizePercentage, setSizePercentage] = useState('')
|
const [sizePercentage, setSizePercentage] = useState('')
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
|
|
||||||
const bank = useMemo(() => {
|
const bank = useMemo(() => {
|
||||||
const group = mangoStore.getState().group
|
const group = mangoStore.getState().group
|
||||||
|
@ -78,13 +79,13 @@ function DepositModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
||||||
|
|
||||||
const logoUri = useMemo(() => {
|
const logoUri = useMemo(() => {
|
||||||
let logoURI
|
let logoURI
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens.length) {
|
||||||
logoURI = jupiterTokens.find(
|
logoURI = mangoTokens.find(
|
||||||
(t) => t.address === bank?.mint.toString()
|
(t) => t.address === bank?.mint.toString()
|
||||||
)!.logoURI
|
)!.logoURI
|
||||||
}
|
}
|
||||||
return logoURI
|
return logoURI
|
||||||
}, [bank?.mint, jupiterTokens])
|
}, [bank?.mint, mangoTokens])
|
||||||
|
|
||||||
const { wallet } = useWallet()
|
const { wallet } = useWallet()
|
||||||
const walletTokens = mangoStore((s) => s.wallet.tokens)
|
const walletTokens = mangoStore((s) => s.wallet.tokens)
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { walletBalanceForToken } from './DepositModal'
|
||||||
import SolBalanceWarnings from '@components/shared/SolBalanceWarnings'
|
import SolBalanceWarnings from '@components/shared/SolBalanceWarnings'
|
||||||
import useSolBalance from 'hooks/useSolBalance'
|
import useSolBalance from 'hooks/useSolBalance'
|
||||||
import useMangoAccount from 'hooks/useMangoAccount'
|
import useMangoAccount from 'hooks/useMangoAccount'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
|
|
||||||
interface RepayModalProps {
|
interface RepayModalProps {
|
||||||
token?: string
|
token?: string
|
||||||
|
@ -45,8 +46,8 @@ function RepayModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
||||||
const [selectedToken, setSelectedToken] = useState(token)
|
const [selectedToken, setSelectedToken] = useState(token)
|
||||||
const [showTokenList, setShowTokenList] = useState(false)
|
const [showTokenList, setShowTokenList] = useState(false)
|
||||||
const [sizePercentage, setSizePercentage] = useState('')
|
const [sizePercentage, setSizePercentage] = useState('')
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
const { maxSolDeposit } = useSolBalance()
|
// const { maxSolDeposit } = useSolBalance()
|
||||||
|
|
||||||
const bank = useMemo(() => {
|
const bank = useMemo(() => {
|
||||||
const group = mangoStore.getState().group
|
const group = mangoStore.getState().group
|
||||||
|
@ -55,13 +56,13 @@ function RepayModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
||||||
|
|
||||||
const logoUri = useMemo(() => {
|
const logoUri = useMemo(() => {
|
||||||
let logoURI
|
let logoURI
|
||||||
if (jupiterTokens.length && bank) {
|
if (mangoTokens.length && bank) {
|
||||||
logoURI = jupiterTokens.find(
|
logoURI = mangoTokens.find(
|
||||||
(t) => t.address === bank?.mint.toString()
|
(t) => t.address === bank?.mint.toString()
|
||||||
)!.logoURI
|
)!.logoURI
|
||||||
}
|
}
|
||||||
return logoURI
|
return logoURI
|
||||||
}, [bank, jupiterTokens])
|
}, [bank, mangoTokens])
|
||||||
|
|
||||||
const { wallet } = useWallet()
|
const { wallet } = useWallet()
|
||||||
const walletTokens = mangoStore((s) => s.wallet.tokens)
|
const walletTokens = mangoStore((s) => s.wallet.tokens)
|
||||||
|
|
|
@ -28,6 +28,7 @@ import { getMaxWithdrawForBank } from '../swap/useTokenMax'
|
||||||
import MaxAmountButton from '@components/shared/MaxAmountButton'
|
import MaxAmountButton from '@components/shared/MaxAmountButton'
|
||||||
import HealthImpactTokenChange from '@components/HealthImpactTokenChange'
|
import HealthImpactTokenChange from '@components/HealthImpactTokenChange'
|
||||||
import useMangoAccount from 'hooks/useMangoAccount'
|
import useMangoAccount from 'hooks/useMangoAccount'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
|
|
||||||
interface WithdrawModalProps {
|
interface WithdrawModalProps {
|
||||||
token?: string
|
token?: string
|
||||||
|
@ -45,7 +46,7 @@ function WithdrawModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
||||||
)
|
)
|
||||||
const [showTokenList, setShowTokenList] = useState(false)
|
const [showTokenList, setShowTokenList] = useState(false)
|
||||||
const [sizePercentage, setSizePercentage] = useState('')
|
const [sizePercentage, setSizePercentage] = useState('')
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
const { mangoAccount } = useMangoAccount()
|
const { mangoAccount } = useMangoAccount()
|
||||||
|
|
||||||
const bank = useMemo(() => {
|
const bank = useMemo(() => {
|
||||||
|
@ -55,13 +56,13 @@ function WithdrawModal({ isOpen, onClose, token }: ModalCombinedProps) {
|
||||||
|
|
||||||
const logoUri = useMemo(() => {
|
const logoUri = useMemo(() => {
|
||||||
let logoURI
|
let logoURI
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens.length) {
|
||||||
logoURI = jupiterTokens.find(
|
logoURI = mangoTokens.find(
|
||||||
(t) => t.address === bank?.mint.toString()
|
(t) => t.address === bank?.mint.toString()
|
||||||
)!.logoURI
|
)!.logoURI
|
||||||
}
|
}
|
||||||
return logoURI
|
return logoURI
|
||||||
}, [bank?.mint, jupiterTokens])
|
}, [bank?.mint, mangoTokens])
|
||||||
|
|
||||||
const tokenMax = useMemo(() => {
|
const tokenMax = useMemo(() => {
|
||||||
if (!bank || !mangoAccount || !group) return new Decimal(0)
|
if (!bank || !mangoAccount || !group) return new Decimal(0)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Bank, Serum3Market } from '@blockworks-foundation/mango-v4'
|
import { Bank, Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import Decimal from 'decimal.js'
|
import Decimal from 'decimal.js'
|
||||||
|
@ -18,7 +19,7 @@ const BalancesTable = () => {
|
||||||
const { mangoAccount } = useMangoAccount()
|
const { mangoAccount } = useMangoAccount()
|
||||||
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
|
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
|
||||||
const group = mangoStore((s) => s.group)
|
const group = mangoStore((s) => s.group)
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
const { width } = useViewport()
|
const { width } = useViewport()
|
||||||
const showTableView = width ? width > breakpoints.md : false
|
const showTableView = width ? width > breakpoints.md : false
|
||||||
|
|
||||||
|
@ -69,8 +70,8 @@ const BalancesTable = () => {
|
||||||
const bank = value[0]
|
const bank = value[0]
|
||||||
|
|
||||||
let logoURI
|
let logoURI
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens.length) {
|
||||||
logoURI = jupiterTokens.find(
|
logoURI = mangoTokens.find(
|
||||||
(t) => t.address === bank.mint.toString()
|
(t) => t.address === bank.mint.toString()
|
||||||
)!.logoURI
|
)!.logoURI
|
||||||
}
|
}
|
||||||
|
@ -127,8 +128,8 @@ const BalancesTable = () => {
|
||||||
const bank = value[0]
|
const bank = value[0]
|
||||||
|
|
||||||
let logoURI
|
let logoURI
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens.length) {
|
||||||
logoURI = jupiterTokens.find(
|
logoURI = mangoTokens.find(
|
||||||
(t) => t.address === bank.mint.toString()
|
(t) => t.address === bank.mint.toString()
|
||||||
)!.logoURI
|
)!.logoURI
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import ContentBox from '../shared/ContentBox'
|
||||||
import Change from '../shared/Change'
|
import Change from '../shared/Change'
|
||||||
import MarketLogos from '@components/trade/MarketLogos'
|
import MarketLogos from '@components/trade/MarketLogos'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
|
import { useCoingecko } from 'hooks/useCoingecko'
|
||||||
|
import useMangoGroup from 'hooks/useMangoGroup'
|
||||||
const SimpleAreaChart = dynamic(
|
const SimpleAreaChart = dynamic(
|
||||||
() => import('@components/shared/SimpleAreaChart'),
|
() => import('@components/shared/SimpleAreaChart'),
|
||||||
{ ssr: false }
|
{ ssr: false }
|
||||||
|
@ -18,8 +20,7 @@ const SimpleAreaChart = dynamic(
|
||||||
|
|
||||||
const SpotMarketsTable = () => {
|
const SpotMarketsTable = () => {
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
const coingeckoPrices = mangoStore((s) => s.coingeckoPrices.data)
|
const { isLoading: loadingPrices, data: coingeckoPrices } = useCoingecko()
|
||||||
const loadingCoingeckoPrices = mangoStore((s) => s.coingeckoPrices.loading)
|
|
||||||
const group = mangoStore((s) => s.group)
|
const group = mangoStore((s) => s.group)
|
||||||
const serumMarkets = mangoStore((s) => s.serumMarkets)
|
const serumMarkets = mangoStore((s) => s.serumMarkets)
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
@ -74,7 +75,7 @@ const SpotMarketsTable = () => {
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{!loadingCoingeckoPrices ? (
|
{!loadingPrices ? (
|
||||||
chartData !== undefined ? (
|
chartData !== undefined ? (
|
||||||
<SimpleAreaChart
|
<SimpleAreaChart
|
||||||
color={
|
color={
|
||||||
|
@ -127,22 +128,17 @@ export default SpotMarketsTable
|
||||||
|
|
||||||
const MobileSpotMarketItem = ({ market }: { market: Serum3Market }) => {
|
const MobileSpotMarketItem = ({ market }: { market: Serum3Market }) => {
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
const coingeckoPrices = mangoStore((s) => s.coingeckoPrices.data)
|
const { isLoading: loadingPrices, data: coingeckoPrices } = useCoingecko()
|
||||||
const loadingCoingeckoPrices = mangoStore((s) => s.coingeckoPrices.loading)
|
const { group } = useMangoGroup()
|
||||||
const group = mangoStore((s) => s.group)
|
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const bank = group?.getFirstBankByTokenIndex(market.baseTokenIndex)
|
const bank = group?.getFirstBankByTokenIndex(market.baseTokenIndex)
|
||||||
|
|
||||||
const coingeckoData = useMemo(() => {
|
const coingeckoData = useMemo(() => {
|
||||||
if (!loadingCoingeckoPrices && bank) {
|
if (!loadingPrices && bank) {
|
||||||
return coingeckoPrices.find((asset) =>
|
return coingeckoPrices.find((asset) => asset.symbol === bank?.name)
|
||||||
bank.name === 'soETH'
|
|
||||||
? asset.symbol === 'ETH'
|
|
||||||
: asset.symbol === bank?.name
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}, [loadingCoingeckoPrices, bank])
|
}, [loadingPrices, bank])
|
||||||
|
|
||||||
const change = useMemo(() => {
|
const change = useMemo(() => {
|
||||||
if (coingeckoData) {
|
if (coingeckoData) {
|
||||||
|
@ -178,7 +174,7 @@ const MobileSpotMarketItem = ({ market }: { market: Serum3Market }) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!loadingCoingeckoPrices ? (
|
{!loadingPrices ? (
|
||||||
chartData !== undefined ? (
|
chartData !== undefined ? (
|
||||||
<SimpleAreaChart
|
<SimpleAreaChart
|
||||||
color={change >= 0 ? COLORS.GREEN[theme] : COLORS.RED[theme]}
|
color={change >= 0 ? COLORS.GREEN[theme] : COLORS.RED[theme]}
|
||||||
|
|
|
@ -18,12 +18,13 @@ import FlipNumbers from 'react-flip-numbers'
|
||||||
import Tooltip from '@components/shared/Tooltip'
|
import Tooltip from '@components/shared/Tooltip'
|
||||||
import { Bank } from '@blockworks-foundation/mango-v4'
|
import { Bank } from '@blockworks-foundation/mango-v4'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
|
|
||||||
const TokenStats = () => {
|
const TokenStats = () => {
|
||||||
const { t } = useTranslation(['common', 'token'])
|
const { t } = useTranslation(['common', 'token'])
|
||||||
const [showTokenDetails, setShowTokenDetails] = useState('')
|
const [showTokenDetails, setShowTokenDetails] = useState('')
|
||||||
const group = mangoStore((s) => s.group)
|
const group = mangoStore((s) => s.group)
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
const { width } = useViewport()
|
const { width } = useViewport()
|
||||||
const showTableView = width ? width > breakpoints.md : false
|
const showTableView = width ? width > breakpoints.md : false
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
@ -141,8 +142,8 @@ const TokenStats = () => {
|
||||||
const bank = value[0]
|
const bank = value[0]
|
||||||
|
|
||||||
let logoURI
|
let logoURI
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens.length) {
|
||||||
logoURI = jupiterTokens.find(
|
logoURI = mangoTokens.find(
|
||||||
(t) => t.address === bank.mint.toString()
|
(t) => t.address === bank.mint.toString()
|
||||||
)!.logoURI
|
)!.logoURI
|
||||||
}
|
}
|
||||||
|
@ -232,8 +233,8 @@ const TokenStats = () => {
|
||||||
{banks.map(({ key, value }) => {
|
{banks.map(({ key, value }) => {
|
||||||
const bank = value[0]
|
const bank = value[0]
|
||||||
let logoURI
|
let logoURI
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens.length) {
|
||||||
logoURI = jupiterTokens.find(
|
logoURI = mangoTokens.find(
|
||||||
(t) => t.address === bank.mint.toString()
|
(t) => t.address === bank.mint.toString()
|
||||||
)!.logoURI
|
)!.logoURI
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,6 @@ import React, {
|
||||||
useState,
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { TransactionInstruction, PublicKey } from '@solana/web3.js'
|
import { TransactionInstruction, PublicKey } from '@solana/web3.js'
|
||||||
import { toUiDecimals } from '@blockworks-foundation/mango-v4'
|
|
||||||
import { Jupiter, RouteInfo, TransactionFeeInfo } from '@jup-ag/core'
|
|
||||||
import JSBI from 'jsbi'
|
|
||||||
import Decimal from 'decimal.js'
|
import Decimal from 'decimal.js'
|
||||||
|
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
|
@ -31,10 +28,12 @@ import {
|
||||||
} from '../../utils/numbers'
|
} from '../../utils/numbers'
|
||||||
import { notify } from '../../utils/notifications'
|
import { notify } from '../../utils/notifications'
|
||||||
import { useWallet } from '@solana/wallet-adapter-react'
|
import { useWallet } from '@solana/wallet-adapter-react'
|
||||||
|
import useJupiterMints from '../../hooks/useJupiterMints'
|
||||||
|
import { RouteInfo, TransactionFeeInfo } from 'types/jupiter'
|
||||||
|
import useJupiterSwapData from './useJupiterSwapData'
|
||||||
|
|
||||||
type JupiterRouteInfoProps = {
|
type JupiterRouteInfoProps = {
|
||||||
amountIn: Decimal
|
amountIn: Decimal
|
||||||
jupiter: Jupiter | undefined
|
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
routes: RouteInfo[] | undefined
|
routes: RouteInfo[] | undefined
|
||||||
selectedRoute: RouteInfo | undefined
|
selectedRoute: RouteInfo | undefined
|
||||||
|
@ -43,14 +42,28 @@ type JupiterRouteInfoProps = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseJupiterRoute = async (
|
const parseJupiterRoute = async (
|
||||||
jupiter: Jupiter,
|
|
||||||
selectedRoute: RouteInfo,
|
selectedRoute: RouteInfo,
|
||||||
userPublicKey: PublicKey
|
userPublicKey: PublicKey
|
||||||
): Promise<TransactionInstruction[]> => {
|
): Promise<TransactionInstruction[]> => {
|
||||||
const { transactions } = await jupiter.exchange({
|
const transactions = await (
|
||||||
routeInfo: selectedRoute,
|
await fetch('https://quote-api.jup.ag/v3/swap', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
// route from /quote api
|
||||||
|
route: selectedRoute,
|
||||||
|
// user public key to be used for the swap
|
||||||
userPublicKey,
|
userPublicKey,
|
||||||
|
// auto wrap and unwrap SOL. default is true
|
||||||
|
wrapUnwrapSOL: true,
|
||||||
|
// feeAccount is optional. Use if you want to charge a fee. feeBps must have been passed in /quote API.
|
||||||
|
// This is the ATA account for the output token where the fee will be sent to. If you are swapping from SOL->USDC then this would be the USDC ATA you want to collect the fee.
|
||||||
|
feeAccount: 'fee_account_public_key',
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
).json()
|
||||||
const { swapTransaction } = transactions
|
const { swapTransaction } = transactions
|
||||||
const instructions = []
|
const instructions = []
|
||||||
for (const ix of swapTransaction.instructions) {
|
for (const ix of swapTransaction.instructions) {
|
||||||
|
@ -72,7 +85,6 @@ const EMPTY_COINGECKO_PRICES = {
|
||||||
const JupiterRouteInfo = ({
|
const JupiterRouteInfo = ({
|
||||||
amountIn,
|
amountIn,
|
||||||
onClose,
|
onClose,
|
||||||
jupiter,
|
|
||||||
routes,
|
routes,
|
||||||
selectedRoute,
|
selectedRoute,
|
||||||
setSelectedRoute,
|
setSelectedRoute,
|
||||||
|
@ -80,15 +92,11 @@ const JupiterRouteInfo = ({
|
||||||
const { t } = useTranslation(['common', 'trade'])
|
const { t } = useTranslation(['common', 'trade'])
|
||||||
const [showRoutesModal, setShowRoutesModal] = useState(false)
|
const [showRoutesModal, setShowRoutesModal] = useState(false)
|
||||||
const [swapRate, setSwapRate] = useState<boolean>(false)
|
const [swapRate, setSwapRate] = useState<boolean>(false)
|
||||||
const [depositAndFee, setDepositAndFee] = useState<TransactionFeeInfo>()
|
|
||||||
const [feeValue, setFeeValue] = useState<number | null>(null)
|
const [feeValue, setFeeValue] = useState<number | null>(null)
|
||||||
const [submitting, setSubmitting] = useState(false)
|
const [submitting, setSubmitting] = useState(false)
|
||||||
const [coingeckoPrices, setCoingeckoPrices] = useState(EMPTY_COINGECKO_PRICES)
|
const [coingeckoPrices, setCoingeckoPrices] = useState(EMPTY_COINGECKO_PRICES)
|
||||||
const { connected } = useWallet()
|
const { mangoTokens } = useJupiterMints()
|
||||||
|
const { inputTokenInfo, outputTokenInfo } = useJupiterSwapData()
|
||||||
const inputTokenInfo = mangoStore((s) => s.swap.inputTokenInfo)
|
|
||||||
const outputTokenInfo = mangoStore((s) => s.swap.outputTokenInfo)
|
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
|
||||||
const inputBank = mangoStore((s) => s.swap.inputBank)
|
const inputBank = mangoStore((s) => s.swap.inputBank)
|
||||||
|
|
||||||
const inputTokenIconUri = useMemo(() => {
|
const inputTokenIconUri = useMemo(() => {
|
||||||
|
@ -102,18 +110,6 @@ const JupiterRouteInfo = ({
|
||||||
)
|
)
|
||||||
}, [selectedRoute, outputTokenInfo])
|
}, [selectedRoute, outputTokenInfo])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const getDepositAndFee = async () => {
|
|
||||||
const fees = await selectedRoute?.getDepositAndFee()
|
|
||||||
if (fees) {
|
|
||||||
setDepositAndFee(fees)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (selectedRoute && connected) {
|
|
||||||
getDepositAndFee()
|
|
||||||
}
|
|
||||||
}, [selectedRoute, connected])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCoingeckoPrices(EMPTY_COINGECKO_PRICES)
|
setCoingeckoPrices(EMPTY_COINGECKO_PRICES)
|
||||||
const fetchTokenPrices = async () => {
|
const fetchTokenPrices = async () => {
|
||||||
|
@ -140,7 +136,7 @@ const JupiterRouteInfo = ({
|
||||||
}, [inputTokenInfo, outputTokenInfo])
|
}, [inputTokenInfo, outputTokenInfo])
|
||||||
|
|
||||||
const onSwap = async () => {
|
const onSwap = async () => {
|
||||||
if (!jupiter || !selectedRoute) return
|
if (!selectedRoute) return
|
||||||
try {
|
try {
|
||||||
const client = mangoStore.getState().client
|
const client = mangoStore.getState().client
|
||||||
const group = mangoStore.getState().group
|
const group = mangoStore.getState().group
|
||||||
|
@ -151,11 +147,7 @@ const JupiterRouteInfo = ({
|
||||||
|
|
||||||
if (!mangoAccount || !group || !inputBank || !outputBank) return
|
if (!mangoAccount || !group || !inputBank || !outputBank) return
|
||||||
|
|
||||||
const ixs = await parseJupiterRoute(
|
const ixs = await parseJupiterRoute(selectedRoute, mangoAccount!.owner)
|
||||||
jupiter,
|
|
||||||
selectedRoute,
|
|
||||||
mangoAccount!.owner
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
|
@ -201,7 +193,9 @@ const JupiterRouteInfo = ({
|
||||||
|
|
||||||
const remainingBalance =
|
const remainingBalance =
|
||||||
mangoAccount.getTokenDepositsUi(inputBank) - amountIn.toNumber()
|
mangoAccount.getTokenDepositsUi(inputBank) - amountIn.toNumber()
|
||||||
return remainingBalance < 0 ? Math.abs(remainingBalance) : 0
|
const x = remainingBalance < 0 ? Math.abs(remainingBalance) : 0
|
||||||
|
console.log('borrowAmount', x)
|
||||||
|
return x
|
||||||
}, [amountIn])
|
}, [amountIn])
|
||||||
|
|
||||||
const coinGeckoPriceDifference = useMemo(() => {
|
const coinGeckoPriceDifference = useMemo(() => {
|
||||||
|
@ -220,6 +214,8 @@ const JupiterRouteInfo = ({
|
||||||
: new Decimal(0)
|
: new Decimal(0)
|
||||||
}, [coingeckoPrices, amountIn, amountOut])
|
}, [coingeckoPrices, amountIn, amountOut])
|
||||||
|
|
||||||
|
console.log('selectedRoute', selectedRoute)
|
||||||
|
|
||||||
return routes?.length && selectedRoute && outputTokenInfo && amountOut ? (
|
return routes?.length && selectedRoute && outputTokenInfo && amountOut ? (
|
||||||
<div className="flex h-full flex-col justify-between">
|
<div className="flex h-full flex-col justify-between">
|
||||||
<div>
|
<div>
|
||||||
|
@ -320,7 +316,7 @@ const JupiterRouteInfo = ({
|
||||||
{outputTokenInfo?.decimals ? (
|
{outputTokenInfo?.decimals ? (
|
||||||
<p className="text-right font-mono text-sm text-th-fgd-1">
|
<p className="text-right font-mono text-sm text-th-fgd-1">
|
||||||
{formatDecimal(
|
{formatDecimal(
|
||||||
JSBI.toNumber(selectedRoute?.otherAmountThreshold) /
|
selectedRoute?.otherAmountThreshold /
|
||||||
10 ** outputTokenInfo.decimals || 1,
|
10 ** outputTokenInfo.decimals || 1,
|
||||||
outputTokenInfo.decimals
|
outputTokenInfo.decimals
|
||||||
)}{' '}
|
)}{' '}
|
||||||
|
@ -382,7 +378,7 @@ const JupiterRouteInfo = ({
|
||||||
includeSeparator = true
|
includeSeparator = true
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<span key={index}>{`${info.amm.label} ${
|
<span key={index}>{`${info?.label} ${
|
||||||
includeSeparator ? 'x ' : ''
|
includeSeparator ? 'x ' : ''
|
||||||
}`}</span>
|
}`}</span>
|
||||||
)
|
)
|
||||||
|
@ -402,21 +398,20 @@ const JupiterRouteInfo = ({
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
selectedRoute?.marketInfos.map((info, index) => {
|
selectedRoute?.marketInfos.map((info, index) => {
|
||||||
const feeToken = jupiterTokens.find(
|
const feeToken = mangoTokens.find(
|
||||||
(item) => item?.address === info.lpFee?.mint
|
(item) => item?.address === info.lpFee?.mint
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-between" key={index}>
|
<div className="flex justify-between" key={index}>
|
||||||
<p className="text-sm text-th-fgd-3">
|
<p className="text-sm text-th-fgd-3">
|
||||||
{t('swap:fees-paid-to', {
|
{t('swap:fees-paid-to', {
|
||||||
route: info?.amm?.label,
|
route: info?.label,
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
{feeToken?.decimals && (
|
{feeToken?.decimals && (
|
||||||
<p className="text-right font-mono text-sm text-th-fgd-1">
|
<p className="text-right font-mono text-sm text-th-fgd-1">
|
||||||
{(
|
{(
|
||||||
JSBI.toNumber(info.lpFee?.amount) /
|
info.lpFee?.amount / Math.pow(10, feeToken.decimals)
|
||||||
Math.pow(10, feeToken.decimals)
|
|
||||||
).toFixed(6)}{' '}
|
).toFixed(6)}{' '}
|
||||||
<span className="font-body tracking-wide">
|
<span className="font-body tracking-wide">
|
||||||
{feeToken?.symbol}
|
{feeToken?.symbol}
|
||||||
|
|
|
@ -8,10 +8,12 @@ const LeverageSlider = ({
|
||||||
amount,
|
amount,
|
||||||
leverageMax,
|
leverageMax,
|
||||||
onChange,
|
onChange,
|
||||||
|
step,
|
||||||
}: {
|
}: {
|
||||||
amount: number
|
amount: number
|
||||||
leverageMax: number
|
leverageMax: number
|
||||||
onChange: (x: string) => any
|
onChange: (x: string) => any
|
||||||
|
step: number
|
||||||
}) => {
|
}) => {
|
||||||
const [value, setValue] = useState(0)
|
const [value, setValue] = useState(0)
|
||||||
const inputEl = useRef<HTMLInputElement>(null)
|
const inputEl = useRef<HTMLInputElement>(null)
|
||||||
|
@ -58,7 +60,7 @@ const LeverageSlider = ({
|
||||||
type="range"
|
type="range"
|
||||||
min="0"
|
min="0"
|
||||||
max={leverageMax}
|
max={leverageMax}
|
||||||
step={inputTokenInfo ? 1 / 10 ** inputTokenInfo?.decimals : 6}
|
step={step}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onChange={handleSliderChange}
|
onChange={handleSliderChange}
|
||||||
value={value}
|
value={value}
|
||||||
|
@ -71,10 +73,12 @@ export const SwapLeverageSlider = ({
|
||||||
amount,
|
amount,
|
||||||
onChange,
|
onChange,
|
||||||
useMargin,
|
useMargin,
|
||||||
|
step,
|
||||||
}: {
|
}: {
|
||||||
amount: number
|
amount: number
|
||||||
onChange: (x: string) => void
|
onChange: (x: string) => void
|
||||||
useMargin: boolean
|
useMargin: boolean
|
||||||
|
step: number
|
||||||
}) => {
|
}) => {
|
||||||
const { mangoAccount } = useMangoAccount()
|
const { mangoAccount } = useMangoAccount()
|
||||||
const { amountWithBorrow } = useTokenMax(useMargin)
|
const { amountWithBorrow } = useTokenMax(useMargin)
|
||||||
|
@ -82,12 +86,18 @@ export const SwapLeverageSlider = ({
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!mangoAccount ? (
|
{!mangoAccount ? (
|
||||||
<LeverageSlider amount={amount} leverageMax={100} onChange={onChange} />
|
<LeverageSlider
|
||||||
|
amount={amount}
|
||||||
|
leverageMax={100}
|
||||||
|
onChange={onChange}
|
||||||
|
step={step}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<LeverageSlider
|
<LeverageSlider
|
||||||
amount={amount}
|
amount={amount}
|
||||||
leverageMax={amountWithBorrow.toNumber()}
|
leverageMax={amountWithBorrow.toNumber()}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
step={step}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import { RouteInfo } from '@jup-ag/core'
|
|
||||||
import { Dispatch, SetStateAction } from 'react'
|
import { Dispatch, SetStateAction } from 'react'
|
||||||
import JSBI from 'jsbi'
|
|
||||||
|
|
||||||
import mangoStore from '@store/mangoStore'
|
import { RouteInfo, Token } from '../../types/jupiter'
|
||||||
import { Token } from '../../types/jupiter'
|
|
||||||
import { formatDecimal } from '../../utils/numbers'
|
import { formatDecimal } from '../../utils/numbers'
|
||||||
import Modal from '../shared/Modal'
|
import Modal from '../shared/Modal'
|
||||||
|
import useJupiterMints from '../../hooks/useJupiterMints'
|
||||||
|
|
||||||
type RoutesModalProps = {
|
type RoutesModalProps = {
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
|
@ -26,7 +24,7 @@ const RoutesModal = ({
|
||||||
inputTokenSymbol,
|
inputTokenSymbol,
|
||||||
outputTokenInfo,
|
outputTokenInfo,
|
||||||
}: RoutesModalProps) => {
|
}: RoutesModalProps) => {
|
||||||
const tokens = mangoStore.getState().jupiterTokens
|
const { mangoTokens } = useJupiterMints()
|
||||||
|
|
||||||
const handleSelectRoute = (route: RouteInfo) => {
|
const handleSelectRoute = (route: RouteInfo) => {
|
||||||
setSelectedRoute(route)
|
setSelectedRoute(route)
|
||||||
|
@ -40,7 +38,7 @@ const RoutesModal = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="thin-scroll max-h-96 overflow-y-auto overflow-x-hidden">
|
<div className="thin-scroll max-h-96 overflow-y-auto overflow-x-hidden">
|
||||||
{routes?.map((route, index) => {
|
{routes?.map((route, index) => {
|
||||||
const selected = selectedRoute === route
|
const selected = selectedRoute.outAmount === route.outAmount
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
|
@ -66,7 +64,7 @@ const RoutesModal = ({
|
||||||
includeSeparator = true
|
includeSeparator = true
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<span key={index}>{`${info.amm.label} ${
|
<span key={index}>{`${info.label} ${
|
||||||
includeSeparator ? 'x ' : ''
|
includeSeparator ? 'x ' : ''
|
||||||
}`}</span>
|
}`}</span>
|
||||||
)
|
)
|
||||||
|
@ -81,7 +79,7 @@ const RoutesModal = ({
|
||||||
<span key={index}>
|
<span key={index}>
|
||||||
<span>
|
<span>
|
||||||
{
|
{
|
||||||
tokens.find(
|
mangoTokens.find(
|
||||||
(item) =>
|
(item) =>
|
||||||
item?.address === r?.outputMint?.toString()
|
item?.address === r?.outputMint?.toString()
|
||||||
)?.symbol
|
)?.symbol
|
||||||
|
@ -95,8 +93,7 @@ const RoutesModal = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="text-lg">
|
<div className="text-lg">
|
||||||
{formatDecimal(
|
{formatDecimal(
|
||||||
JSBI.toNumber(route.outAmount) /
|
route.outAmount / 10 ** (outputTokenInfo?.decimals || 1),
|
||||||
10 ** (outputTokenInfo?.decimals || 1),
|
|
||||||
6
|
6
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
ExclamationCircleIcon,
|
ExclamationCircleIcon,
|
||||||
LinkIcon,
|
LinkIcon,
|
||||||
} from '@heroicons/react/20/solid'
|
} from '@heroicons/react/20/solid'
|
||||||
import { RouteInfo } from '@jup-ag/core'
|
|
||||||
import NumberFormat, { NumberFormatValues } from 'react-number-format'
|
import NumberFormat, { NumberFormatValues } from 'react-number-format'
|
||||||
import Decimal from 'decimal.js'
|
import Decimal from 'decimal.js'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
|
@ -24,19 +23,22 @@ import Button, { IconButton } from '../shared/Button'
|
||||||
import ButtonGroup from '../forms/ButtonGroup'
|
import ButtonGroup from '../forms/ButtonGroup'
|
||||||
import Loading from '../shared/Loading'
|
import Loading from '../shared/Loading'
|
||||||
import { EnterBottomExitBottom } from '../shared/Transitions'
|
import { EnterBottomExitBottom } from '../shared/Transitions'
|
||||||
import useJupiter from './useJupiter'
|
import useJupiterRoutes from './useJupiterRoutes'
|
||||||
import SwapSettings from './SwapSettings'
|
import SwapSettings from './SwapSettings'
|
||||||
import SheenLoader from '../shared/SheenLoader'
|
import SheenLoader from '../shared/SheenLoader'
|
||||||
import { HealthType } from '@blockworks-foundation/mango-v4'
|
import { HealthType } from '@blockworks-foundation/mango-v4'
|
||||||
import {
|
import {
|
||||||
INPUT_TOKEN_DEFAULT,
|
INPUT_TOKEN_DEFAULT,
|
||||||
|
MANGO_MINT,
|
||||||
OUTPUT_TOKEN_DEFAULT,
|
OUTPUT_TOKEN_DEFAULT,
|
||||||
|
USDC_MINT,
|
||||||
} from '../../utils/constants'
|
} from '../../utils/constants'
|
||||||
import { useTokenMax } from './useTokenMax'
|
import { useTokenMax } from './useTokenMax'
|
||||||
import MaxAmountButton from '@components/shared/MaxAmountButton'
|
import MaxAmountButton from '@components/shared/MaxAmountButton'
|
||||||
import HealthImpact from '@components/shared/HealthImpact'
|
import HealthImpact from '@components/shared/HealthImpact'
|
||||||
import { useWallet } from '@solana/wallet-adapter-react'
|
import { useWallet } from '@solana/wallet-adapter-react'
|
||||||
import useMangoAccount from 'hooks/useMangoAccount'
|
import useMangoAccount from 'hooks/useMangoAccount'
|
||||||
|
import { RouteInfo } from 'types/jupiter'
|
||||||
|
|
||||||
const MAX_DIGITS = 11
|
const MAX_DIGITS = 11
|
||||||
export const withValueLimit = (values: NumberFormatValues): boolean => {
|
export const withValueLimit = (values: NumberFormatValues): boolean => {
|
||||||
|
@ -53,15 +55,15 @@ const SwapForm = () => {
|
||||||
const [showTokenSelect, setShowTokenSelect] = useState('')
|
const [showTokenSelect, setShowTokenSelect] = useState('')
|
||||||
const [showSettings, setShowSettings] = useState(false)
|
const [showSettings, setShowSettings] = useState(false)
|
||||||
const [showConfirm, setShowConfirm] = useState(false)
|
const [showConfirm, setShowConfirm] = useState(false)
|
||||||
|
console.log('amountInFormValue', amountInFormValue)
|
||||||
const group = mangoStore.getState().group
|
|
||||||
|
|
||||||
const set = mangoStore.getState().set
|
const set = mangoStore.getState().set
|
||||||
const useMargin = mangoStore((s) => s.swap.margin)
|
const {
|
||||||
const slippage = mangoStore((s) => s.swap.slippage)
|
margin: useMargin,
|
||||||
const inputTokenInfo = mangoStore((s) => s.swap.inputTokenInfo)
|
slippage,
|
||||||
const outputTokenInfo = mangoStore((s) => s.swap.outputTokenInfo)
|
inputBank,
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
outputBank,
|
||||||
|
} = mangoStore((s) => s.swap)
|
||||||
const [debouncedAmountIn] = useDebounce(amountInFormValue, 300)
|
const [debouncedAmountIn] = useDebounce(amountInFormValue, 300)
|
||||||
const { mangoAccount } = useMangoAccount()
|
const { mangoAccount } = useMangoAccount()
|
||||||
const { connected } = useWallet()
|
const { connected } = useWallet()
|
||||||
|
@ -72,16 +74,26 @@ const SwapForm = () => {
|
||||||
: new Decimal(0)
|
: new Decimal(0)
|
||||||
}, [debouncedAmountIn])
|
}, [debouncedAmountIn])
|
||||||
|
|
||||||
const { amountOut, jupiter, routes } = useJupiter({
|
const { bestRoute, routes } = useJupiterRoutes({
|
||||||
inputTokenInfo,
|
inputMint: inputBank?.mint.toString() || USDC_MINT,
|
||||||
outputTokenInfo,
|
outputMint: outputBank?.mint.toString() || MANGO_MINT,
|
||||||
inputAmount: debouncedAmountIn,
|
inputAmount: debouncedAmountIn,
|
||||||
slippage,
|
slippage,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const outAmount: number = useMemo(() => {
|
||||||
|
return selectedRoute?.outAmount.toString()
|
||||||
|
? new Decimal(selectedRoute.outAmount.toString())
|
||||||
|
.div(10 ** outputBank!.mintDecimals)
|
||||||
|
.toNumber()
|
||||||
|
: 0
|
||||||
|
}, [selectedRoute, outputBank])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedRoute(routes[0])
|
if (bestRoute) {
|
||||||
}, [routes])
|
setSelectedRoute(bestRoute)
|
||||||
|
}
|
||||||
|
}, [bestRoute])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAmountInFormValue('')
|
setAmountInFormValue('')
|
||||||
|
@ -93,71 +105,50 @@ const SwapForm = () => {
|
||||||
|
|
||||||
const handleTokenInSelect = useCallback(
|
const handleTokenInSelect = useCallback(
|
||||||
(mintAddress: string) => {
|
(mintAddress: string) => {
|
||||||
const inputTokenInfo = jupiterTokens.find(
|
|
||||||
(t: any) => t.address === mintAddress
|
|
||||||
)
|
|
||||||
const group = mangoStore.getState().group
|
const group = mangoStore.getState().group
|
||||||
if (group) {
|
if (group) {
|
||||||
const bank = group.getFirstBankByMint(new PublicKey(mintAddress))
|
const bank = group.getFirstBankByMint(new PublicKey(mintAddress))
|
||||||
set((s) => {
|
set((s) => {
|
||||||
s.swap.inputBank = bank
|
s.swap.inputBank = bank
|
||||||
s.swap.inputTokenInfo = inputTokenInfo
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
setShowTokenSelect('')
|
setShowTokenSelect('')
|
||||||
},
|
},
|
||||||
[jupiterTokens, set]
|
[set]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleTokenOutSelect = useCallback(
|
const handleTokenOutSelect = useCallback(
|
||||||
(mintAddress: string) => {
|
(mintAddress: string) => {
|
||||||
const outputTokenInfo = jupiterTokens.find(
|
|
||||||
(t: any) => t.address === mintAddress
|
|
||||||
)
|
|
||||||
const group = mangoStore.getState().group
|
const group = mangoStore.getState().group
|
||||||
if (group) {
|
if (group) {
|
||||||
const bank = group.getFirstBankByMint(new PublicKey(mintAddress))
|
const bank = group.getFirstBankByMint(new PublicKey(mintAddress))
|
||||||
set((s) => {
|
set((s) => {
|
||||||
s.swap.outputBank = bank
|
s.swap.outputBank = bank
|
||||||
s.swap.outputTokenInfo = outputTokenInfo
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
setShowTokenSelect('')
|
setShowTokenSelect('')
|
||||||
},
|
},
|
||||||
[jupiterTokens, set]
|
[set]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleSwitchTokens = useCallback(() => {
|
const handleSwitchTokens = useCallback(() => {
|
||||||
if (amountIn?.gt(0)) {
|
if (amountIn?.gt(0) && outAmount) {
|
||||||
setAmountInFormValue(amountOut.toString())
|
setAmountInFormValue(outAmount.toString())
|
||||||
}
|
}
|
||||||
const inputBank = mangoStore.getState().swap.inputBank
|
const inputBank = mangoStore.getState().swap.inputBank
|
||||||
const outputBank = mangoStore.getState().swap.outputBank
|
const outputBank = mangoStore.getState().swap.outputBank
|
||||||
set((s) => {
|
set((s) => {
|
||||||
s.swap.inputBank = outputBank
|
s.swap.inputBank = outputBank
|
||||||
s.swap.outputBank = inputBank
|
s.swap.outputBank = inputBank
|
||||||
s.swap.inputTokenInfo = outputTokenInfo
|
|
||||||
s.swap.outputTokenInfo = inputTokenInfo
|
|
||||||
})
|
})
|
||||||
setAnimateSwitchArrow(
|
setAnimateSwitchArrow(
|
||||||
(prevanimateSwitchArrow) => prevanimateSwitchArrow + 1
|
(prevanimateSwitchArrow) => prevanimateSwitchArrow + 1
|
||||||
)
|
)
|
||||||
}, [inputTokenInfo, outputTokenInfo, set, amountOut, amountIn])
|
}, [set, outAmount, amountIn])
|
||||||
|
|
||||||
const currentMaintHealth = useMemo(() => {
|
|
||||||
if (!group || !mangoAccount) return 0
|
|
||||||
return mangoAccount.getHealthRatioUi(group, HealthType.maint)
|
|
||||||
}, [mangoAccount])
|
|
||||||
|
|
||||||
const maintProjectedHealth = useMemo(() => {
|
const maintProjectedHealth = useMemo(() => {
|
||||||
const group = mangoStore.getState().group
|
const group = mangoStore.getState().group
|
||||||
if (
|
if (!inputBank || !mangoAccount || !outputBank || !outAmount || !group)
|
||||||
!inputTokenInfo ||
|
|
||||||
!mangoAccount ||
|
|
||||||
!outputTokenInfo ||
|
|
||||||
!amountOut ||
|
|
||||||
!group
|
|
||||||
)
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
const simulatedHealthRatio =
|
const simulatedHealthRatio =
|
||||||
|
@ -165,12 +156,12 @@ const SwapForm = () => {
|
||||||
group,
|
group,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
mintPk: new PublicKey(inputTokenInfo.address),
|
mintPk: inputBank.mint,
|
||||||
uiTokenAmount: amountIn.toNumber() * -1,
|
uiTokenAmount: amountIn.toNumber() * -1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
mintPk: new PublicKey(outputTokenInfo.address),
|
mintPk: outputBank.mint,
|
||||||
uiTokenAmount: amountOut.toNumber(),
|
uiTokenAmount: outAmount,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
HealthType.maint
|
HealthType.maint
|
||||||
|
@ -180,15 +171,11 @@ const SwapForm = () => {
|
||||||
: simulatedHealthRatio! < 0
|
: simulatedHealthRatio! < 0
|
||||||
? 0
|
? 0
|
||||||
: Math.trunc(simulatedHealthRatio!)
|
: Math.trunc(simulatedHealthRatio!)
|
||||||
}, [mangoAccount, inputTokenInfo, outputTokenInfo, amountIn, amountOut])
|
}, [mangoAccount, inputBank, outputBank, amountIn, outAmount])
|
||||||
|
|
||||||
const loadingSwapDetails: boolean = useMemo(() => {
|
const loadingSwapDetails: boolean = useMemo(() => {
|
||||||
return (
|
return !!amountIn.toNumber() && connected && !selectedRoute
|
||||||
!!amountIn.toNumber() && connected && (!selectedRoute || !outputTokenInfo)
|
}, [amountIn, connected, selectedRoute])
|
||||||
)
|
|
||||||
}, [amountIn, connected, selectedRoute, outputTokenInfo])
|
|
||||||
|
|
||||||
const showHealthImpact = !!inputTokenInfo && !!outputTokenInfo && !!amountOut
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContentBox
|
<ContentBox
|
||||||
|
@ -211,7 +198,6 @@ const SwapForm = () => {
|
||||||
onClose={() => setShowConfirm(false)}
|
onClose={() => setShowConfirm(false)}
|
||||||
amountIn={amountIn}
|
amountIn={amountIn}
|
||||||
slippage={slippage}
|
slippage={slippage}
|
||||||
jupiter={jupiter}
|
|
||||||
routes={routes}
|
routes={routes}
|
||||||
selectedRoute={selectedRoute}
|
selectedRoute={selectedRoute}
|
||||||
setSelectedRoute={setSelectedRoute}
|
setSelectedRoute={setSelectedRoute}
|
||||||
|
@ -260,7 +246,7 @@ const SwapForm = () => {
|
||||||
<div className="mb-3 grid grid-cols-2" id="swap-step-two">
|
<div className="mb-3 grid grid-cols-2" id="swap-step-two">
|
||||||
<div className="col-span-1 rounded-lg rounded-r-none border border-r-0 border-th-bkg-4 bg-th-bkg-1">
|
<div className="col-span-1 rounded-lg rounded-r-none border border-r-0 border-th-bkg-4 bg-th-bkg-1">
|
||||||
<TokenSelect
|
<TokenSelect
|
||||||
tokenSymbol={inputTokenInfo?.symbol || INPUT_TOKEN_DEFAULT}
|
tokenSymbol={inputBank?.name || INPUT_TOKEN_DEFAULT}
|
||||||
showTokenList={setShowTokenSelect}
|
showTokenList={setShowTokenSelect}
|
||||||
type="input"
|
type="input"
|
||||||
/>
|
/>
|
||||||
|
@ -271,7 +257,7 @@ const SwapForm = () => {
|
||||||
thousandSeparator=","
|
thousandSeparator=","
|
||||||
allowNegative={false}
|
allowNegative={false}
|
||||||
isNumericString={true}
|
isNumericString={true}
|
||||||
decimalScale={inputTokenInfo?.decimals || 6}
|
decimalScale={inputBank?.mintDecimals || 6}
|
||||||
name="amountIn"
|
name="amountIn"
|
||||||
id="amountIn"
|
id="amountIn"
|
||||||
className="w-full rounded-r-lg border border-th-bkg-4 bg-th-bkg-1 p-3 text-right font-mono text-base font-bold text-th-fgd-1 focus:outline-none lg:text-lg xl:text-xl"
|
className="w-full rounded-r-lg border border-th-bkg-4 bg-th-bkg-1 p-3 text-right font-mono text-base font-bold text-th-fgd-1 focus:outline-none lg:text-lg xl:text-xl"
|
||||||
|
@ -308,7 +294,7 @@ const SwapForm = () => {
|
||||||
<div id="swap-step-three" className="mb-3 grid grid-cols-2">
|
<div id="swap-step-three" className="mb-3 grid grid-cols-2">
|
||||||
<div className="col-span-1 rounded-lg rounded-r-none border border-r-0 border-th-bkg-4 bg-th-bkg-1">
|
<div className="col-span-1 rounded-lg rounded-r-none border border-r-0 border-th-bkg-4 bg-th-bkg-1">
|
||||||
<TokenSelect
|
<TokenSelect
|
||||||
tokenSymbol={outputTokenInfo?.symbol || OUTPUT_TOKEN_DEFAULT}
|
tokenSymbol={outputBank?.name || OUTPUT_TOKEN_DEFAULT}
|
||||||
showTokenList={setShowTokenSelect}
|
showTokenList={setShowTokenSelect}
|
||||||
type="output"
|
type="output"
|
||||||
/>
|
/>
|
||||||
|
@ -329,15 +315,13 @@ const SwapForm = () => {
|
||||||
thousandSeparator=","
|
thousandSeparator=","
|
||||||
allowNegative={false}
|
allowNegative={false}
|
||||||
isNumericString={true}
|
isNumericString={true}
|
||||||
decimalScale={outputTokenInfo?.decimals || 6}
|
decimalScale={outputBank?.mintDecimals || 6}
|
||||||
name="amountOut"
|
name="amountOut"
|
||||||
id="amountOut"
|
id="amountOut"
|
||||||
className="w-full rounded-r-lg bg-th-bkg-3 p-3 text-right font-mono text-base font-bold text-th-fgd-3 focus:outline-none lg:text-lg xl:text-xl"
|
className="w-full rounded-r-lg bg-th-bkg-3 p-3 text-right font-mono text-base font-bold text-th-fgd-3 focus:outline-none lg:text-lg xl:text-xl"
|
||||||
placeholder="0.00"
|
placeholder="0.00"
|
||||||
disabled
|
disabled
|
||||||
value={
|
value={outAmount ? numberFormat.format(outAmount) : ''}
|
||||||
amountOut ? numberFormat.format(amountOut.toNumber()) : ''
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -352,6 +336,7 @@ const SwapForm = () => {
|
||||||
useMargin={useMargin}
|
useMargin={useMargin}
|
||||||
amount={amountIn.toNumber()}
|
amount={amountIn.toNumber()}
|
||||||
onChange={setAmountInFormValue}
|
onChange={setAmountInFormValue}
|
||||||
|
step={1 / 10 ** (inputBank?.mintDecimals || 6)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -360,8 +345,8 @@ const SwapForm = () => {
|
||||||
useMargin={useMargin}
|
useMargin={useMargin}
|
||||||
setShowConfirm={setShowConfirm}
|
setShowConfirm={setShowConfirm}
|
||||||
amountIn={amountIn}
|
amountIn={amountIn}
|
||||||
inputSymbol={inputTokenInfo?.symbol}
|
inputSymbol={inputBank?.name}
|
||||||
amountOut={amountOut}
|
amountOut={selectedRoute ? outAmount : undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -385,7 +370,7 @@ const SwapFormSubmitButton = ({
|
||||||
useMargin,
|
useMargin,
|
||||||
}: {
|
}: {
|
||||||
amountIn: Decimal
|
amountIn: Decimal
|
||||||
amountOut: Decimal
|
amountOut: number | undefined
|
||||||
inputSymbol: string | undefined
|
inputSymbol: string | undefined
|
||||||
loadingSwapDetails: boolean
|
loadingSwapDetails: boolean
|
||||||
setShowConfirm: (x: any) => any
|
setShowConfirm: (x: any) => any
|
||||||
|
@ -400,10 +385,7 @@ const SwapFormSubmitButton = ({
|
||||||
: tokenMax.lt(amountIn)
|
: tokenMax.lt(amountIn)
|
||||||
|
|
||||||
const disabled =
|
const disabled =
|
||||||
!amountIn.toNumber() ||
|
!amountIn.toNumber() || !connected || showInsufficientBalance || !amountOut
|
||||||
!connected ||
|
|
||||||
showInsufficientBalance ||
|
|
||||||
!amountOut.gt(0)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -1,19 +1,13 @@
|
||||||
import { memo, useMemo, useState, useEffect, ChangeEvent } from 'react'
|
import { memo, useMemo, useEffect } from 'react'
|
||||||
import Image from 'next/legacy/image'
|
|
||||||
import { Token } from '../../types/jupiter'
|
import { Token } from '../../types/jupiter'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import Input from '../forms/Input'
|
|
||||||
import { IconButton } from '../shared/Button'
|
import { IconButton } from '../shared/Button'
|
||||||
import {
|
import { XMarkIcon } from '@heroicons/react/20/solid'
|
||||||
QuestionMarkCircleIcon,
|
|
||||||
MagnifyingGlassIcon,
|
|
||||||
XMarkIcon,
|
|
||||||
} from '@heroicons/react/20/solid'
|
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import { floorToDecimal } from '../../utils/numbers'
|
|
||||||
import Decimal from 'decimal.js'
|
import Decimal from 'decimal.js'
|
||||||
import { getTokenInMax } from './useTokenMax'
|
import { getTokenInMax } from './useTokenMax'
|
||||||
import useMangoAccount from 'hooks/useMangoAccount'
|
import useMangoAccount from 'hooks/useMangoAccount'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
|
|
||||||
const generateSearchTerm = (item: Token, searchValue: string) => {
|
const generateSearchTerm = (item: Token, searchValue: string) => {
|
||||||
const normalizedSearchValue = searchValue.toLowerCase()
|
const normalizedSearchValue = searchValue.toLowerCase()
|
||||||
|
@ -99,10 +93,8 @@ const SwapFormTokenList = ({
|
||||||
useMargin: boolean
|
useMargin: boolean
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation(['common', 'swap'])
|
const { t } = useTranslation(['common', 'swap'])
|
||||||
const [search, setSearch] = useState('')
|
// const [search, setSearch] = useState('')
|
||||||
const tokens = mangoStore.getState().jupiterTokens
|
const { mangoTokens } = useJupiterMints()
|
||||||
const walletTokens = mangoStore((s) => s.wallet.tokens)
|
|
||||||
// const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
|
||||||
const inputBank = mangoStore((s) => s.swap.inputBank)
|
const inputBank = mangoStore((s) => s.swap.inputBank)
|
||||||
const outputBank = mangoStore((s) => s.swap.outputBank)
|
const outputBank = mangoStore((s) => s.swap.outputBank)
|
||||||
const group = mangoStore((s) => s.group)
|
const group = mangoStore((s) => s.group)
|
||||||
|
@ -128,13 +120,13 @@ const SwapFormTokenList = ({
|
||||||
|
|
||||||
const tokenInfos = useMemo(() => {
|
const tokenInfos = useMemo(() => {
|
||||||
if (
|
if (
|
||||||
tokens?.length &&
|
mangoTokens?.length &&
|
||||||
group &&
|
group &&
|
||||||
mangoAccount &&
|
mangoAccount &&
|
||||||
outputBank &&
|
outputBank &&
|
||||||
type === 'input'
|
type === 'input'
|
||||||
) {
|
) {
|
||||||
const filteredSortedTokens = tokens
|
const filteredSortedTokens = mangoTokens
|
||||||
.map((token) => {
|
.map((token) => {
|
||||||
const max = getTokenInMax(
|
const max = getTokenInMax(
|
||||||
mangoAccount,
|
mangoAccount,
|
||||||
|
@ -152,8 +144,8 @@ const SwapFormTokenList = ({
|
||||||
)
|
)
|
||||||
|
|
||||||
return filteredSortedTokens
|
return filteredSortedTokens
|
||||||
} else if (tokens?.length) {
|
} else if (mangoTokens?.length) {
|
||||||
const filteredTokens = tokens
|
const filteredTokens = mangoTokens
|
||||||
.map((token) => ({
|
.map((token) => ({
|
||||||
...token,
|
...token,
|
||||||
amount: new Decimal(0),
|
amount: new Decimal(0),
|
||||||
|
@ -164,7 +156,7 @@ const SwapFormTokenList = ({
|
||||||
} else {
|
} else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}, [tokens, walletTokens, inputBank, outputBank, mangoAccount, group])
|
}, [mangoTokens, inputBank, outputBank, mangoAccount, group, useMargin, type])
|
||||||
|
|
||||||
// const handleUpdateSearch = (e: ChangeEvent<HTMLInputElement>) => {
|
// const handleUpdateSearch = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
// setSearch(e.target.value)
|
// setSearch(e.target.value)
|
||||||
|
@ -203,8 +195,8 @@ const SwapFormTokenList = ({
|
||||||
<div className="mt-4 flex flex-wrap">
|
<div className="mt-4 flex flex-wrap">
|
||||||
{popularTokens.map((token) => {
|
{popularTokens.map((token) => {
|
||||||
let logoURI
|
let logoURI
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens.length) {
|
||||||
logoURI = jupiterTokens.find(
|
logoURI = mangoTokens.find(
|
||||||
(t) => t.address === token.address
|
(t) => t.address === token.address
|
||||||
)!.logoURI
|
)!.logoURI
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,7 @@ import { useViewport } from '../../hooks/useViewport'
|
||||||
import { IconButton } from '../shared/Button'
|
import { IconButton } from '../shared/Button'
|
||||||
import { Transition } from '@headlessui/react'
|
import { Transition } from '@headlessui/react'
|
||||||
import SheenLoader from '../shared/SheenLoader'
|
import SheenLoader from '../shared/SheenLoader'
|
||||||
import { useWallet } from '@solana/wallet-adapter-react'
|
import { SwapHistoryItem } from '@store/mangoStore'
|
||||||
import mangoStore, { SwapHistoryItem } from '@store/mangoStore'
|
|
||||||
import {
|
import {
|
||||||
countLeadingZeros,
|
countLeadingZeros,
|
||||||
formatFixedDecimals,
|
formatFixedDecimals,
|
||||||
|
@ -26,6 +25,7 @@ import { EXPLORERS } from 'pages/settings'
|
||||||
import Tooltip from '@components/shared/Tooltip'
|
import Tooltip from '@components/shared/Tooltip'
|
||||||
import { formatTokenSymbol } from 'utils/tokens'
|
import { formatTokenSymbol } from 'utils/tokens'
|
||||||
import useMangoAccount from 'hooks/useMangoAccount'
|
import useMangoAccount from 'hooks/useMangoAccount'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
|
|
||||||
const SwapHistoryTable = ({
|
const SwapHistoryTable = ({
|
||||||
swapHistory,
|
swapHistory,
|
||||||
|
@ -35,7 +35,7 @@ const SwapHistoryTable = ({
|
||||||
loading: boolean
|
loading: boolean
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation(['common', 'settings'])
|
const { t } = useTranslation(['common', 'settings'])
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
const { mangoAccount } = useMangoAccount()
|
const { mangoAccount } = useMangoAccount()
|
||||||
const [showSwapDetails, setSwapDetails] = useState('')
|
const [showSwapDetails, setSwapDetails] = useState('')
|
||||||
const { width } = useViewport()
|
const { width } = useViewport()
|
||||||
|
@ -95,11 +95,11 @@ const SwapHistoryTable = ({
|
||||||
const inSymbol = formatTokenSymbol(swap_in_symbol)
|
const inSymbol = formatTokenSymbol(swap_in_symbol)
|
||||||
const outSymbol = formatTokenSymbol(swap_out_symbol)
|
const outSymbol = formatTokenSymbol(swap_out_symbol)
|
||||||
|
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens.length) {
|
||||||
baseLogoURI = jupiterTokens.find(
|
baseLogoURI = mangoTokens.find(
|
||||||
(t) => t.symbol === inSymbol
|
(t) => t.symbol === inSymbol
|
||||||
)?.logoURI
|
)?.logoURI
|
||||||
quoteLogoURI = jupiterTokens.find(
|
quoteLogoURI = mangoTokens.find(
|
||||||
(t) => t.symbol === outSymbol
|
(t) => t.symbol === outSymbol
|
||||||
)?.logoURI
|
)?.logoURI
|
||||||
}
|
}
|
||||||
|
@ -255,11 +255,11 @@ const SwapHistoryTable = ({
|
||||||
const inSymbol = formatTokenSymbol(swap_in_symbol)
|
const inSymbol = formatTokenSymbol(swap_in_symbol)
|
||||||
const outSymbol = formatTokenSymbol(swap_out_symbol)
|
const outSymbol = formatTokenSymbol(swap_out_symbol)
|
||||||
|
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens.length) {
|
||||||
baseLogoURI = jupiterTokens.find(
|
baseLogoURI = mangoTokens.find(
|
||||||
(t) => t.symbol === inSymbol
|
(t) => t.symbol === inSymbol
|
||||||
)?.logoURI
|
)?.logoURI
|
||||||
quoteLogoURI = jupiterTokens.find(
|
quoteLogoURI = mangoTokens.find(
|
||||||
(t) => t.symbol === outSymbol
|
(t) => t.symbol === outSymbol
|
||||||
)?.logoURI
|
)?.logoURI
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,6 @@ import { IS_ONBOARDED_KEY } from 'utils/constants'
|
||||||
const SwapTokenChart = dynamic(() => import('./SwapTokenChart'), { ssr: false })
|
const SwapTokenChart = dynamic(() => import('./SwapTokenChart'), { ssr: false })
|
||||||
|
|
||||||
const SwapPage = () => {
|
const SwapPage = () => {
|
||||||
const inputTokenInfo = mangoStore((s) => s.swap.inputTokenInfo)
|
|
||||||
const outputTokenInfo = mangoStore((s) => s.swap.outputTokenInfo)
|
|
||||||
const { connected } = useWallet()
|
const { connected } = useWallet()
|
||||||
const tourSettings = mangoStore((s) => s.settings.tours)
|
const tourSettings = mangoStore((s) => s.settings.tours)
|
||||||
const [isOnboarded] = useLocalStorageState(IS_ONBOARDED_KEY)
|
const [isOnboarded] = useLocalStorageState(IS_ONBOARDED_KEY)
|
||||||
|
@ -19,13 +17,7 @@ const SwapPage = () => {
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-12">
|
<div className="grid grid-cols-12">
|
||||||
<div className="col-span-12 border-th-bkg-3 md:col-span-6 md:border-b lg:col-span-7 xl:col-span-8">
|
<div className="col-span-12 border-th-bkg-3 md:col-span-6 md:border-b lg:col-span-7 xl:col-span-8">
|
||||||
{inputTokenInfo?.extensions?.coingeckoId &&
|
<SwapTokenChart />
|
||||||
outputTokenInfo?.extensions?.coingeckoId ? (
|
|
||||||
<SwapTokenChart
|
|
||||||
inputTokenId={inputTokenInfo?.extensions?.coingeckoId}
|
|
||||||
outputTokenId={outputTokenInfo?.extensions?.coingeckoId}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-12 mt-2 space-y-6 border-th-bkg-3 md:col-span-6 md:mt-0 md:border-b lg:col-span-5 xl:col-span-4">
|
<div className="col-span-12 mt-2 space-y-6 border-th-bkg-3 md:col-span-6 md:mt-0 md:border-b lg:col-span-5 xl:col-span-4">
|
||||||
<SwapForm />
|
<SwapForm />
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
import {
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
FunctionComponent,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from 'react'
|
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
import {
|
import {
|
||||||
|
@ -31,14 +25,10 @@ import { formatTokenSymbol } from 'utils/tokens'
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { fetchChartData } from 'apis/coingecko'
|
import { fetchChartData } from 'apis/coingecko'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
|
import useJupiterSwapData from './useJupiterSwapData'
|
||||||
|
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
interface SwapTokenChartProps {
|
|
||||||
inputTokenId: string
|
|
||||||
outputTokenId: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const CustomizedLabel = ({
|
const CustomizedLabel = ({
|
||||||
chartData,
|
chartData,
|
||||||
x,
|
x,
|
||||||
|
@ -77,21 +67,19 @@ const CustomizedLabel = ({
|
||||||
} else return <div />
|
} else return <div />
|
||||||
}
|
}
|
||||||
|
|
||||||
const SwapTokenChart: FunctionComponent<SwapTokenChartProps> = ({
|
const SwapTokenChart = () => {
|
||||||
inputTokenId,
|
const { inputBank, outputBank } = mangoStore((s) => s.swap)
|
||||||
outputTokenId,
|
const { inputCoingeckoId, outputCoingeckoId } = useJupiterSwapData()
|
||||||
}) => {
|
const [baseTokenId, setBaseTokenId] = useState(inputCoingeckoId)
|
||||||
const inputBank = mangoStore((s) => s.swap.inputBank)
|
const [quoteTokenId, setQuoteTokenId] = useState(outputCoingeckoId)
|
||||||
const outputBank = mangoStore((s) => s.swap.outputBank)
|
|
||||||
const [baseTokenId, setBaseTokenId] = useState(inputTokenId)
|
|
||||||
const [quoteTokenId, setQuoteTokenId] = useState(outputTokenId)
|
|
||||||
const [mouseData, setMouseData] = useState<any>(null)
|
const [mouseData, setMouseData] = useState<any>(null)
|
||||||
const [daysToShow, setDaysToShow] = useState(1)
|
const [daysToShow, setDaysToShow] = useState(1)
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const chartDataQuery = useQuery(
|
const chartDataQuery = useQuery(
|
||||||
['chart-data', baseTokenId, quoteTokenId, daysToShow],
|
['chart-data', baseTokenId, quoteTokenId, daysToShow],
|
||||||
() => fetchChartData(baseTokenId, quoteTokenId, daysToShow),
|
() => fetchChartData(baseTokenId, quoteTokenId, daysToShow),
|
||||||
{ staleTime: 0 }
|
{ staleTime: 0, enabled: !!baseTokenId && !!quoteTokenId }
|
||||||
)
|
)
|
||||||
const chartData = chartDataQuery.data
|
const chartData = chartDataQuery.data
|
||||||
|
|
||||||
|
@ -106,16 +94,16 @@ const SwapTokenChart: FunctionComponent<SwapTokenChartProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!inputTokenId || !outputTokenId) return
|
if (!inputCoingeckoId || !outputCoingeckoId) return
|
||||||
|
|
||||||
if (['usd-coin', 'tether'].includes(outputTokenId)) {
|
if (['usd-coin', 'tether'].includes(outputCoingeckoId)) {
|
||||||
setBaseTokenId(inputTokenId)
|
setBaseTokenId(inputCoingeckoId)
|
||||||
setQuoteTokenId(outputTokenId)
|
setQuoteTokenId(outputCoingeckoId)
|
||||||
} else {
|
} else {
|
||||||
setBaseTokenId(outputTokenId)
|
setBaseTokenId(outputCoingeckoId)
|
||||||
setQuoteTokenId(inputTokenId)
|
setQuoteTokenId(inputCoingeckoId)
|
||||||
}
|
}
|
||||||
}, [inputTokenId, outputTokenId])
|
}, [inputCoingeckoId, outputCoingeckoId])
|
||||||
|
|
||||||
// const handleFlipChart = useCallback(() => {
|
// const handleFlipChart = useCallback(() => {
|
||||||
// if (!baseTokenId || !quoteTokenId) return
|
// if (!baseTokenId || !quoteTokenId) return
|
||||||
|
@ -144,7 +132,7 @@ const SwapTokenChart: FunctionComponent<SwapTokenChartProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContentBox hideBorder hidePadding className="h-full px-6 py-3">
|
<ContentBox hideBorder hidePadding className="h-full px-6 py-3">
|
||||||
{chartDataQuery?.isLoading ? (
|
{chartDataQuery?.isLoading || chartDataQuery.isFetching ? (
|
||||||
<>
|
<>
|
||||||
<SheenLoader className="w-[148px] rounded-md">
|
<SheenLoader className="w-[148px] rounded-md">
|
||||||
<div className="h-[18px] bg-th-bkg-2" />
|
<div className="h-[18px] bg-th-bkg-2" />
|
||||||
|
@ -159,14 +147,14 @@ const SwapTokenChart: FunctionComponent<SwapTokenChartProps> = ({
|
||||||
<div className="h-[308px] bg-th-bkg-2" />
|
<div className="h-[308px] bg-th-bkg-2" />
|
||||||
</SheenLoader>
|
</SheenLoader>
|
||||||
</>
|
</>
|
||||||
) : chartData.length && baseTokenId && quoteTokenId ? (
|
) : chartData?.length && baseTokenId && quoteTokenId ? (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div>
|
<div>
|
||||||
{inputBank && outputBank ? (
|
{inputBank && outputBank ? (
|
||||||
<div className="mb-0.5 flex items-center">
|
<div className="mb-0.5 flex items-center">
|
||||||
<p className="text-base text-th-fgd-3">
|
<p className="text-base text-th-fgd-3">
|
||||||
{['usd-coin', 'tether'].includes(inputTokenId || '')
|
{['usd-coin', 'tether'].includes(inputCoingeckoId || '')
|
||||||
? `${formatTokenSymbol(
|
? `${formatTokenSymbol(
|
||||||
outputBank?.name?.toUpperCase()
|
outputBank?.name?.toUpperCase()
|
||||||
)}/${inputBank?.name?.toUpperCase()}`
|
)}/${inputBank?.name?.toUpperCase()}`
|
||||||
|
|
|
@ -3,7 +3,8 @@ import {
|
||||||
QuestionMarkCircleIcon,
|
QuestionMarkCircleIcon,
|
||||||
} from '@heroicons/react/20/solid'
|
} from '@heroicons/react/20/solid'
|
||||||
import Image from 'next/legacy/image'
|
import Image from 'next/legacy/image'
|
||||||
import mangoStore from '@store/mangoStore'
|
import useMangoGroup from 'hooks/useMangoGroup'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
|
|
||||||
type TokenSelectProps = {
|
type TokenSelectProps = {
|
||||||
tokenSymbol: string | undefined
|
tokenSymbol: string | undefined
|
||||||
|
@ -16,14 +17,14 @@ const TokenSelect = ({
|
||||||
showTokenList,
|
showTokenList,
|
||||||
type,
|
type,
|
||||||
}: TokenSelectProps) => {
|
}: TokenSelectProps) => {
|
||||||
const group = mangoStore((s) => s.group)
|
const { group } = useMangoGroup()
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
|
|
||||||
if (!group) return null
|
if (!group) return null
|
||||||
|
|
||||||
let logoURI
|
let logoURI
|
||||||
if (jupiterTokens.length) {
|
if (mangoTokens.length) {
|
||||||
logoURI = jupiterTokens.find((t) => t.symbol === tokenSymbol)!.logoURI
|
logoURI = mangoTokens.find((t) => t.symbol === tokenSymbol)!.logoURI
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
import { toUiDecimals } from '@blockworks-foundation/mango-v4'
|
|
||||||
import { Jupiter, RouteInfo } from '@jup-ag/core'
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import JSBI from 'jsbi'
|
|
||||||
import Decimal from 'decimal.js'
|
|
||||||
|
|
||||||
import mangoStore, { CLUSTER } from '@store/mangoStore'
|
|
||||||
import { Token } from '../../types/jupiter'
|
|
||||||
import { PublicKey } from '@solana/web3.js'
|
|
||||||
|
|
||||||
type useJupiterPropTypes = {
|
|
||||||
inputTokenInfo: Token | undefined
|
|
||||||
outputTokenInfo: Token | undefined
|
|
||||||
inputAmount: string
|
|
||||||
slippage: number
|
|
||||||
}
|
|
||||||
|
|
||||||
type RouteParams = {
|
|
||||||
routes: RouteInfo[]
|
|
||||||
amountOut: Decimal
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultComputedInfo = {
|
|
||||||
routes: [],
|
|
||||||
amountOut: new Decimal(0),
|
|
||||||
}
|
|
||||||
|
|
||||||
const useJupiter = ({
|
|
||||||
inputTokenInfo,
|
|
||||||
outputTokenInfo,
|
|
||||||
inputAmount,
|
|
||||||
slippage,
|
|
||||||
}: useJupiterPropTypes) => {
|
|
||||||
const [jupiter, setJupiter] = useState<Jupiter>()
|
|
||||||
const [computedInfo, setComputedInfo] =
|
|
||||||
useState<RouteParams>(defaultComputedInfo)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const connection = mangoStore.getState().connection
|
|
||||||
const loadJupiter = async () => {
|
|
||||||
const jupiter = await Jupiter.load({
|
|
||||||
connection,
|
|
||||||
cluster: CLUSTER,
|
|
||||||
wrapUnwrapSOL: false,
|
|
||||||
// platformFeeAndAccounts: NO_PLATFORM_FEE,
|
|
||||||
routeCacheDuration: 30_000, // Will not refetch data on computeRoutes for up to 10 seconds
|
|
||||||
})
|
|
||||||
setJupiter(jupiter)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
loadJupiter()
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(e)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const group = mangoStore.getState().group
|
|
||||||
if (!group) return
|
|
||||||
const tokens = mangoStore.getState().jupiterTokens
|
|
||||||
|
|
||||||
const loadRoutes = async () => {
|
|
||||||
if (!outputTokenInfo || !inputTokenInfo) return
|
|
||||||
if (!inputAmount) {
|
|
||||||
setComputedInfo(defaultComputedInfo)
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
const computedRoutes = await jupiter
|
|
||||||
?.computeRoutes({
|
|
||||||
inputMint: new PublicKey(inputTokenInfo.address), // Mint address of the input token
|
|
||||||
outputMint: new PublicKey(outputTokenInfo.address), // Mint address of the output token
|
|
||||||
amount: JSBI.BigInt(
|
|
||||||
new Decimal(inputAmount).mul(10 ** inputTokenInfo.decimals)
|
|
||||||
),
|
|
||||||
slippage, // The slippage in % terms
|
|
||||||
filterTopNResult: 10,
|
|
||||||
onlyDirectRoutes: true,
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.error('Error computing Jupiter routes:', e)
|
|
||||||
return
|
|
||||||
})
|
|
||||||
|
|
||||||
const routesInfosWithoutRaydium = computedRoutes?.routesInfos.filter(
|
|
||||||
(r) => {
|
|
||||||
for (const mkt of r.marketInfos) {
|
|
||||||
if (mkt.amm.label === 'Raydium' || mkt.amm.label === 'Serum')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if (routesInfosWithoutRaydium?.length) {
|
|
||||||
const bestRoute = routesInfosWithoutRaydium[0]
|
|
||||||
|
|
||||||
setComputedInfo({
|
|
||||||
routes: routesInfosWithoutRaydium,
|
|
||||||
amountOut: new Decimal(bestRoute.outAmount.toString()).div(
|
|
||||||
10 ** outputTokenInfo.decimals!
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadRoutes()
|
|
||||||
}, [inputTokenInfo, outputTokenInfo, jupiter, slippage, inputAmount])
|
|
||||||
|
|
||||||
return { jupiter, ...computedInfo }
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useJupiter
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
import Decimal from 'decimal.js'
|
||||||
|
import { RouteInfo } from 'types/jupiter'
|
||||||
|
import useJupiterSwapData from './useJupiterSwapData'
|
||||||
|
|
||||||
|
type useJupiterPropTypes = {
|
||||||
|
inputMint: string
|
||||||
|
outputMint: string
|
||||||
|
inputAmount: string
|
||||||
|
slippage: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchJupiterRoutes = async (
|
||||||
|
inputMint: string = 'So11111111111111111111111111111111111111112',
|
||||||
|
outputMint: string = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
||||||
|
amount: number = 0,
|
||||||
|
slippageBps: number = 50,
|
||||||
|
feeBps: number = 0
|
||||||
|
) => {
|
||||||
|
{
|
||||||
|
const params: any = {
|
||||||
|
inputMint: inputMint.toString(),
|
||||||
|
outputMint: outputMint.toString(),
|
||||||
|
amount,
|
||||||
|
slippageBps,
|
||||||
|
onlyDirectRoutes: 'true',
|
||||||
|
feeBps,
|
||||||
|
}
|
||||||
|
|
||||||
|
const paramsString = new URLSearchParams(params).toString()
|
||||||
|
const response = await fetch(
|
||||||
|
`https://quote-api.jup.ag/v1/quote?${paramsString}`
|
||||||
|
)
|
||||||
|
|
||||||
|
const res = await response.json()
|
||||||
|
const data = res.data
|
||||||
|
|
||||||
|
return {
|
||||||
|
routes: res.data,
|
||||||
|
bestRoute: data[0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const useJupiterRoutes = ({
|
||||||
|
inputMint,
|
||||||
|
outputMint,
|
||||||
|
inputAmount,
|
||||||
|
slippage,
|
||||||
|
}: useJupiterPropTypes) => {
|
||||||
|
const { inputTokenInfo } = useJupiterSwapData()
|
||||||
|
|
||||||
|
const amount = inputAmount
|
||||||
|
? new Decimal(inputAmount).mul(10 ** (inputTokenInfo?.decimals || 6))
|
||||||
|
: new Decimal(0)
|
||||||
|
|
||||||
|
const res = useQuery<{ routes: RouteInfo[]; bestRoute: RouteInfo }, Error>(
|
||||||
|
['swap-routes', inputMint, outputMint, inputAmount, slippage],
|
||||||
|
async () =>
|
||||||
|
fetchJupiterRoutes(inputMint, outputMint, amount.toNumber(), slippage),
|
||||||
|
{
|
||||||
|
enabled: inputAmount ? true : false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return inputAmount
|
||||||
|
? {
|
||||||
|
...(res.data ?? {
|
||||||
|
routes: [],
|
||||||
|
bestRoute: undefined,
|
||||||
|
}),
|
||||||
|
isLoading: res.isLoading,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
routes: [],
|
||||||
|
bestRoute: undefined,
|
||||||
|
isLoading: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useJupiterRoutes
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
|
import mangoStore from '@store/mangoStore'
|
||||||
|
|
||||||
|
const useJupiterSwapData = () => {
|
||||||
|
const inputBank = mangoStore((s) => s.swap.inputBank)
|
||||||
|
const outputBank = mangoStore((s) => s.swap.outputBank)
|
||||||
|
const { mangoTokens } = useJupiterMints()
|
||||||
|
|
||||||
|
const [inputTokenInfo, outputTokenInfo] = useMemo(() => {
|
||||||
|
if (inputBank && outputBank) {
|
||||||
|
return [
|
||||||
|
mangoTokens?.find(
|
||||||
|
(item) => item?.address === inputBank.mint.toString() || ''
|
||||||
|
),
|
||||||
|
mangoTokens?.find(
|
||||||
|
(item) => item?.address === outputBank.mint.toString() || ''
|
||||||
|
),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}, [inputBank, outputBank, mangoTokens])
|
||||||
|
|
||||||
|
return {
|
||||||
|
inputTokenInfo,
|
||||||
|
inputCoingeckoId: inputTokenInfo?.extensions?.coingeckoId,
|
||||||
|
outputTokenInfo,
|
||||||
|
outputCoingeckoId: outputTokenInfo?.extensions?.coingeckoId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useJupiterSwapData
|
|
@ -4,6 +4,7 @@ import TabUnderline from '@components/shared/TabUnderline'
|
||||||
import { Popover } from '@headlessui/react'
|
import { Popover } from '@headlessui/react'
|
||||||
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
|
import { useCoingecko } from 'hooks/useCoingecko'
|
||||||
import useOraclePrice from 'hooks/useOraclePrice'
|
import useOraclePrice from 'hooks/useOraclePrice'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import { useCallback, useMemo, useState } from 'react'
|
import { useCallback, useMemo, useState } from 'react'
|
||||||
|
@ -113,16 +114,14 @@ const MarketSelectDropdown = () => {
|
||||||
const AdvancedMarketHeader = () => {
|
const AdvancedMarketHeader = () => {
|
||||||
const { t } = useTranslation(['common', 'trade'])
|
const { t } = useTranslation(['common', 'trade'])
|
||||||
const selectedMarket = mangoStore((s) => s.selectedMarket.current)
|
const selectedMarket = mangoStore((s) => s.selectedMarket.current)
|
||||||
const coingeckoPrices = mangoStore((s) => s.coingeckoPrices.data)
|
const { data: tokenPrices } = useCoingecko()
|
||||||
const actions = mangoStore((s) => s.actions)
|
|
||||||
const serumMarkets = mangoStore((s) => s.serumMarkets)
|
|
||||||
const oraclePrice = useOraclePrice()
|
const oraclePrice = useOraclePrice()
|
||||||
|
|
||||||
const baseSymbol = useMemo(() => {
|
const baseSymbol = useMemo(() => {
|
||||||
return selectedMarket?.name.split('/')[0]
|
return selectedMarket?.name.split('/')[0]
|
||||||
}, [selectedMarket])
|
}, [selectedMarket])
|
||||||
|
|
||||||
const coingeckoData = coingeckoPrices.find((asset) =>
|
const coingeckoData = tokenPrices.find((asset) =>
|
||||||
baseSymbol === 'soETH'
|
baseSymbol === 'soETH'
|
||||||
? asset.symbol === 'ETH'
|
? asset.symbol === 'ETH'
|
||||||
: asset.symbol === baseSymbol
|
: asset.symbol === baseSymbol
|
||||||
|
|
|
@ -36,6 +36,7 @@ import { TRADE_FORM_UI_KEY } from 'utils/constants'
|
||||||
import SpotButtonGroup from './SpotButtonGroup'
|
import SpotButtonGroup from './SpotButtonGroup'
|
||||||
import PerpButtonGroup from './PerpButtonGroup'
|
import PerpButtonGroup from './PerpButtonGroup'
|
||||||
import SolBalanceWarnings from '@components/shared/SolBalanceWarnings'
|
import SolBalanceWarnings from '@components/shared/SolBalanceWarnings'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
|
|
||||||
const TABS: [string, number][] = [
|
const TABS: [string, number][] = [
|
||||||
['Limit', 0],
|
['Limit', 0],
|
||||||
|
@ -46,7 +47,7 @@ const AdvancedTradeForm = () => {
|
||||||
const { t } = useTranslation(['common', 'trade'])
|
const { t } = useTranslation(['common', 'trade'])
|
||||||
const set = mangoStore.getState().set
|
const set = mangoStore.getState().set
|
||||||
const tradeForm = mangoStore((s) => s.tradeForm)
|
const tradeForm = mangoStore((s) => s.tradeForm)
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
const selectedMarket = mangoStore((s) => s.selectedMarket.current)
|
const selectedMarket = mangoStore((s) => s.selectedMarket.current)
|
||||||
const [useMargin, setUseMargin] = useState(true)
|
const [useMargin, setUseMargin] = useState(true)
|
||||||
const [placingOrder, setPlacingOrder] = useState(false)
|
const [placingOrder, setPlacingOrder] = useState(false)
|
||||||
|
@ -57,13 +58,13 @@ const AdvancedTradeForm = () => {
|
||||||
}, [selectedMarket])
|
}, [selectedMarket])
|
||||||
|
|
||||||
const baseLogoURI = useMemo(() => {
|
const baseLogoURI = useMemo(() => {
|
||||||
if (!baseSymbol || !jupiterTokens.length) return ''
|
if (!baseSymbol || !mangoTokens.length) return ''
|
||||||
const token = jupiterTokens.find((t) => t.symbol === baseSymbol)
|
const token = mangoTokens.find((t) => t.symbol === baseSymbol)
|
||||||
if (token) {
|
if (token) {
|
||||||
return token.logoURI
|
return token.logoURI
|
||||||
}
|
}
|
||||||
return ''
|
return ''
|
||||||
}, [baseSymbol, jupiterTokens])
|
}, [baseSymbol, mangoTokens])
|
||||||
|
|
||||||
const quoteBank = useMemo(() => {
|
const quoteBank = useMemo(() => {
|
||||||
const group = mangoStore.getState().group
|
const group = mangoStore.getState().group
|
||||||
|
@ -80,13 +81,13 @@ const AdvancedTradeForm = () => {
|
||||||
}, [quoteBank])
|
}, [quoteBank])
|
||||||
|
|
||||||
const quoteLogoURI = useMemo(() => {
|
const quoteLogoURI = useMemo(() => {
|
||||||
if (!quoteSymbol || !jupiterTokens.length) return ''
|
if (!quoteSymbol || !mangoTokens.length) return ''
|
||||||
const token = jupiterTokens.find((t) => t.symbol === quoteSymbol)
|
const token = mangoTokens.find((t) => t.symbol === quoteSymbol)
|
||||||
if (token) {
|
if (token) {
|
||||||
return token.logoURI
|
return token.logoURI
|
||||||
}
|
}
|
||||||
return ''
|
return ''
|
||||||
}, [quoteSymbol, jupiterTokens])
|
}, [quoteSymbol, mangoTokens])
|
||||||
|
|
||||||
const setTradeType = useCallback(
|
const setTradeType = useCallback(
|
||||||
(tradeType: 'Limit' | 'Market') => {
|
(tradeType: 'Limit' | 'Market') => {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Serum3Market, PerpMarket } from '@blockworks-foundation/mango-v4'
|
import { Serum3Market, PerpMarket } from '@blockworks-foundation/mango-v4'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import Image from 'next/legacy/image'
|
import Image from 'next/legacy/image'
|
||||||
|
@ -6,10 +7,10 @@ import { useMemo } from 'react'
|
||||||
|
|
||||||
const MarketLogos = ({ market }: { market: Serum3Market | PerpMarket }) => {
|
const MarketLogos = ({ market }: { market: Serum3Market | PerpMarket }) => {
|
||||||
const group = mangoStore((s) => s.group)
|
const group = mangoStore((s) => s.group)
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
|
|
||||||
const logos = useMemo(() => {
|
const logos = useMemo(() => {
|
||||||
if (!group || !jupiterTokens.length || !market)
|
if (!group || !mangoTokens.length || !market)
|
||||||
return { baseLogoURI: '', quoteLogoURI: '' }
|
return { baseLogoURI: '', quoteLogoURI: '' }
|
||||||
|
|
||||||
let jupiterBaseToken, jupiterQuoteToken
|
let jupiterBaseToken, jupiterQuoteToken
|
||||||
|
@ -17,14 +18,14 @@ const MarketLogos = ({ market }: { market: Serum3Market | PerpMarket }) => {
|
||||||
const baseBank = group.getFirstBankByTokenIndex(market.baseTokenIndex)
|
const baseBank = group.getFirstBankByTokenIndex(market.baseTokenIndex)
|
||||||
const quoteBank = group.getFirstBankByTokenIndex(market.quoteTokenIndex)
|
const quoteBank = group.getFirstBankByTokenIndex(market.quoteTokenIndex)
|
||||||
|
|
||||||
jupiterBaseToken = jupiterTokens.find(
|
jupiterBaseToken = mangoTokens.find(
|
||||||
(t) => t.address === baseBank.mint.toString()
|
(t) => t.address === baseBank.mint.toString()
|
||||||
)
|
)
|
||||||
jupiterQuoteToken = jupiterTokens.find(
|
jupiterQuoteToken = mangoTokens.find(
|
||||||
(t) => t.address === quoteBank.mint.toString()
|
(t) => t.address === quoteBank.mint.toString()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
jupiterBaseToken = jupiterTokens.find(
|
jupiterBaseToken = mangoTokens.find(
|
||||||
(t) => t.symbol === market.name.split('-')[0]
|
(t) => t.symbol === market.name.split('-')[0]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -34,7 +35,7 @@ const MarketLogos = ({ market }: { market: Serum3Market | PerpMarket }) => {
|
||||||
baseLogoURI,
|
baseLogoURI,
|
||||||
quoteLogoURI,
|
quoteLogoURI,
|
||||||
}
|
}
|
||||||
}, [group, jupiterTokens, market])
|
}, [group, mangoTokens, market])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { PerpMarket, Serum3Market } from '@blockworks-foundation/mango-v4'
|
import { PerpMarket } from '@blockworks-foundation/mango-v4'
|
||||||
import LeverageSlider from '@components/swap/LeverageSlider'
|
import LeverageSlider from '@components/swap/LeverageSlider'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import Decimal from 'decimal.js'
|
|
||||||
import useMangoAccount from 'hooks/useMangoAccount'
|
import useMangoAccount from 'hooks/useMangoAccount'
|
||||||
import { useCallback, useMemo } from 'react'
|
import { useCallback, useMemo } from 'react'
|
||||||
import { notify } from 'utils/notifications'
|
import { notify } from 'utils/notifications'
|
||||||
|
@ -75,6 +74,7 @@ const PerpSlider = () => {
|
||||||
}
|
}
|
||||||
leverageMax={leverageMax}
|
leverageMax={leverageMax}
|
||||||
onChange={handleSlide}
|
onChange={handleSlide}
|
||||||
|
step={0.01}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -74,6 +74,7 @@ const SpotSlider = () => {
|
||||||
}
|
}
|
||||||
leverageMax={leverageMax}
|
leverageMax={leverageMax}
|
||||||
onChange={handleSlide}
|
onChange={handleSlide}
|
||||||
|
step={0.01}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,7 +20,6 @@ const UnsettledTrades = ({
|
||||||
const { t } = useTranslation(['common', 'trade'])
|
const { t } = useTranslation(['common', 'trade'])
|
||||||
const { mangoAccount } = useMangoAccount()
|
const { mangoAccount } = useMangoAccount()
|
||||||
const group = mangoStore((s) => s.group)
|
const group = mangoStore((s) => s.group)
|
||||||
// const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
|
||||||
const [settleMktAddress, setSettleMktAddress] = useState<string>('')
|
const [settleMktAddress, setSettleMktAddress] = useState<string>('')
|
||||||
const { width } = useViewport()
|
const { width } = useViewport()
|
||||||
const showTableView = width ? width > breakpoints.md : false
|
const showTableView = width ? width > breakpoints.md : false
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
import { Token } from 'types/jupiter'
|
||||||
|
import useJupiterMints from './useJupiterMints'
|
||||||
|
|
||||||
|
const fetchCoingecko = async (
|
||||||
|
mangoTokens: Token[]
|
||||||
|
): Promise<{ prices: any[]; symbol: string }[]> => {
|
||||||
|
const coingeckoIds = mangoTokens.map((token) => ({
|
||||||
|
id: token.extensions?.coingeckoId,
|
||||||
|
symbol: token.symbol,
|
||||||
|
}))
|
||||||
|
const promises: any = []
|
||||||
|
for (const token of coingeckoIds) {
|
||||||
|
if (token.id) {
|
||||||
|
promises.push(
|
||||||
|
fetch(
|
||||||
|
`https://api.coingecko.com/api/v3/coins/${token.id}/market_chart?vs_currency=usd&days=1`
|
||||||
|
).then((res) => res.json())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await Promise.all(promises)
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
data[i].symbol = coingeckoIds[i].symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
return data || []
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCoingecko = () => {
|
||||||
|
const { mangoTokens } = useJupiterMints()
|
||||||
|
|
||||||
|
const res = useQuery<any[], Error>(
|
||||||
|
['coingecko-tokens'],
|
||||||
|
() => fetchCoingecko(mangoTokens!),
|
||||||
|
{
|
||||||
|
cacheTime: 1000 * 60 * 2,
|
||||||
|
staleTime: 1000 * 60 * 2,
|
||||||
|
retry: true,
|
||||||
|
enabled: !!mangoTokens,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
isLoading: res.isLoading,
|
||||||
|
data: res.data || [],
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { Group } from '@blockworks-foundation/mango-v4'
|
||||||
|
import { CLUSTER } from '@store/mangoStore'
|
||||||
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
import useMangoGroup from 'hooks/useMangoGroup'
|
||||||
|
import { Token } from 'types/jupiter'
|
||||||
|
|
||||||
|
const fetchJupiterTokens = async (group: Group) => {
|
||||||
|
const url =
|
||||||
|
CLUSTER === 'devnet'
|
||||||
|
? 'https://api.jup.ag/api/tokens/devnet'
|
||||||
|
: 'https://cache.jup.ag/tokens'
|
||||||
|
const response = await fetch(url)
|
||||||
|
const data = await response.json()
|
||||||
|
|
||||||
|
const bankMints = Array.from(group!.banksMapByName.values()).map((b) =>
|
||||||
|
b[0].mint.toString()
|
||||||
|
)
|
||||||
|
const mangoTokens = data.filter((t: any) => bankMints.includes(t.address))
|
||||||
|
|
||||||
|
return {
|
||||||
|
mangoTokens,
|
||||||
|
jupiterTokens: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const useJupiterMints = (): {
|
||||||
|
mangoTokens: Token[]
|
||||||
|
jupiterTokens: Token[]
|
||||||
|
isFetching: boolean
|
||||||
|
} => {
|
||||||
|
const { group } = useMangoGroup()
|
||||||
|
|
||||||
|
const res = useQuery<{ mangoTokens: Token[]; jupiterTokens: Token[] }, Error>(
|
||||||
|
['jupiter-mango-tokens'],
|
||||||
|
() => fetchJupiterTokens(group!),
|
||||||
|
{
|
||||||
|
cacheTime: 1000 * 60 * 10,
|
||||||
|
staleTime: 1000 * 60 * 10,
|
||||||
|
retry: true,
|
||||||
|
enabled: !!group,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
mangoTokens: res?.data?.mangoTokens || [],
|
||||||
|
jupiterTokens: res?.data?.jupiterTokens || [],
|
||||||
|
isFetching: res?.isFetching || false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useJupiterMints
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Group } from '@blockworks-foundation/mango-v4'
|
||||||
|
import mangoStore from '@store/mangoStore'
|
||||||
|
|
||||||
|
export default function useMangoGroup(): {
|
||||||
|
group: Group | undefined
|
||||||
|
} {
|
||||||
|
const group = mangoStore((s) => s.group)
|
||||||
|
|
||||||
|
return { group }
|
||||||
|
}
|
|
@ -15,7 +15,6 @@
|
||||||
"@blockworks-foundation/mango-v4": "https://tylersssss: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@github.com/blockworks-foundation/mango-v4.git#main",
|
"@blockworks-foundation/mango-v4": "https://tylersssss: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@github.com/blockworks-foundation/mango-v4.git#main",
|
||||||
"@headlessui/react": "^1.6.6",
|
"@headlessui/react": "^1.6.6",
|
||||||
"@heroicons/react": "^2.0.10",
|
"@heroicons/react": "^2.0.10",
|
||||||
"@jup-ag/core": "^2.0.0-beta.3",
|
|
||||||
"@project-serum/anchor": "0.25.0",
|
"@project-serum/anchor": "0.25.0",
|
||||||
"@solana/wallet-adapter-base": "^0.9.18",
|
"@solana/wallet-adapter-base": "^0.9.18",
|
||||||
"@solana/wallet-adapter-react": "^0.15.24",
|
"@solana/wallet-adapter-react": "^0.15.24",
|
||||||
|
@ -31,7 +30,6 @@
|
||||||
"decimal.js": "^10.4.0",
|
"decimal.js": "^10.4.0",
|
||||||
"html-react-parser": "^3.0.4",
|
"html-react-parser": "^3.0.4",
|
||||||
"immer": "^9.0.12",
|
"immer": "^9.0.12",
|
||||||
"jsbi": "^4.3.0",
|
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"next": "^13.0.0",
|
"next": "^13.0.0",
|
||||||
"next-i18next": "^11.1.1",
|
"next-i18next": "^11.1.1",
|
||||||
|
@ -55,6 +53,7 @@
|
||||||
"@project-serum/serum": ">=0.13.62"
|
"@project-serum/serum": ">=0.13.62"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/big.js": "^6.1.6",
|
||||||
"@types/node": "17.0.23",
|
"@types/node": "17.0.23",
|
||||||
"@types/react": "18.0.3",
|
"@types/react": "18.0.3",
|
||||||
"@types/react-dom": "18.0.0",
|
"@types/react-dom": "18.0.0",
|
||||||
|
|
|
@ -26,6 +26,9 @@ import ChartRangeButtons from '@components/shared/ChartRangeButtons'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import { LISTED_TOKENS } from 'utils/tokens'
|
import { LISTED_TOKENS } from 'utils/tokens'
|
||||||
import useMangoAccount from 'hooks/useMangoAccount'
|
import useMangoAccount from 'hooks/useMangoAccount'
|
||||||
|
import useMangoGroup from 'hooks/useMangoGroup'
|
||||||
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
|
import { useCoingecko } from 'hooks/useCoingecko'
|
||||||
const PriceChart = dynamic(() => import('@components/token/PriceChart'), {
|
const PriceChart = dynamic(() => import('@components/token/PriceChart'), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
})
|
})
|
||||||
|
@ -74,13 +77,12 @@ const Token: NextPage = () => {
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { token } = router.query
|
const { token } = router.query
|
||||||
const group = mangoStore((s) => s.group)
|
const { group } = useMangoGroup()
|
||||||
const { mangoAccount } = useMangoAccount()
|
const { mangoAccount } = useMangoAccount()
|
||||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
const { mangoTokens } = useJupiterMints()
|
||||||
const coingeckoPrices = mangoStore((s) => s.coingeckoPrices.data)
|
const { isLoading: loadingPrices, data: coingeckoPrices } = useCoingecko()
|
||||||
const [chartData, setChartData] = useState<{ prices: any[] } | null>(null)
|
const [chartData, setChartData] = useState<{ prices: any[] } | null>(null)
|
||||||
const [loadChartData, setLoadChartData] = useState(true)
|
const [loadChartData, setLoadChartData] = useState(true)
|
||||||
const loadingCoingeckoPrices = mangoStore((s) => s.coingeckoPrices.loading)
|
|
||||||
const [daysToShow, setDaysToShow] = useState<number>(1)
|
const [daysToShow, setDaysToShow] = useState<number>(1)
|
||||||
|
|
||||||
const bank = useMemo(() => {
|
const bank = useMemo(() => {
|
||||||
|
@ -95,18 +97,18 @@ const Token: NextPage = () => {
|
||||||
}, [group, token])
|
}, [group, token])
|
||||||
|
|
||||||
const logoURI = useMemo(() => {
|
const logoURI = useMemo(() => {
|
||||||
if (bank && jupiterTokens.length) {
|
if (bank && mangoTokens.length) {
|
||||||
return jupiterTokens.find((t) => t.address === bank.mint.toString())
|
return mangoTokens.find((t) => t.address === bank.mint.toString())
|
||||||
?.logoURI
|
?.logoURI
|
||||||
}
|
}
|
||||||
}, [bank, jupiterTokens])
|
}, [bank, mangoTokens])
|
||||||
|
|
||||||
const coingeckoId = useMemo(() => {
|
const coingeckoId = useMemo(() => {
|
||||||
if (bank && jupiterTokens.length) {
|
if (bank && mangoTokens.length) {
|
||||||
return jupiterTokens.find((t) => t.address === bank.mint.toString())
|
return mangoTokens.find((t) => t.address === bank.mint.toString())
|
||||||
?.extensions?.coingeckoId
|
?.extensions?.coingeckoId
|
||||||
}
|
}
|
||||||
}, [bank, jupiterTokens])
|
}, [bank, mangoTokens])
|
||||||
|
|
||||||
const serumMarkets = useMemo(() => {
|
const serumMarkets = useMemo(() => {
|
||||||
if (group) {
|
if (group) {
|
||||||
|
@ -137,13 +139,13 @@ const Token: NextPage = () => {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
const getCoingeckoData = async (id: string) => {
|
const getCoingeckoData = async (id: string) => {
|
||||||
const response = await fetchTokenInfo(id)
|
const response = await fetchTokenInfo(id)
|
||||||
setCoingeckoData(response)
|
setCoingeckoData(response)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (coingeckoId) {
|
if (coingeckoId) {
|
||||||
getCoingeckoData(coingeckoId)
|
getCoingeckoData(coingeckoId)
|
||||||
}
|
}
|
||||||
|
@ -168,16 +170,15 @@ const Token: NextPage = () => {
|
||||||
} = coingeckoData ? coingeckoData.market_data : DEFAULT_COINGECKO_VALUES
|
} = coingeckoData ? coingeckoData.market_data : DEFAULT_COINGECKO_VALUES
|
||||||
|
|
||||||
const loadingChart = useMemo(() => {
|
const loadingChart = useMemo(() => {
|
||||||
return daysToShow == 1 ? loadingCoingeckoPrices : loadChartData
|
return daysToShow == 1 ? loadingPrices : loadChartData
|
||||||
}, [loadChartData, loadingCoingeckoPrices])
|
}, [loadChartData, loadingPrices])
|
||||||
|
|
||||||
const coingeckoTokenPrices = useMemo(() => {
|
const coingeckoTokenPrices = useMemo(() => {
|
||||||
if (daysToShow === 1 && coingeckoPrices.length && bank) {
|
if (daysToShow === 1 && coingeckoPrices.length && bank) {
|
||||||
const tokenPriceData = coingeckoPrices.find((asset) =>
|
const tokenPriceData = coingeckoPrices.find(
|
||||||
bank?.name === 'soETH'
|
(asset) => asset.symbol === bank.name
|
||||||
? asset.symbol === 'ETH'
|
|
||||||
: asset.symbol === bank.name
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (tokenPriceData) {
|
if (tokenPriceData) {
|
||||||
return tokenPriceData.prices
|
return tokenPriceData.prices
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import create from 'zustand'
|
||||||
import { subscribeWithSelector } from 'zustand/middleware'
|
import { subscribeWithSelector } from 'zustand/middleware'
|
||||||
import { AnchorProvider, Wallet, web3 } from '@project-serum/anchor'
|
import { AnchorProvider, Wallet, web3 } from '@project-serum/anchor'
|
||||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js'
|
import { Connection, Keypair, PublicKey } from '@solana/web3.js'
|
||||||
import { TOKEN_LIST_URL } from '@jup-ag/core'
|
|
||||||
import { OpenOrders, Order } from '@project-serum/serum/lib/market'
|
import { OpenOrders, Order } from '@project-serum/serum/lib/market'
|
||||||
import { Wallet as WalletAdapter } from '@solana/wallet-adapter-react'
|
import { Wallet as WalletAdapter } from '@solana/wallet-adapter-react'
|
||||||
import {
|
import {
|
||||||
|
@ -45,7 +44,7 @@ export const connection = new web3.Connection(
|
||||||
'processed'
|
'processed'
|
||||||
)
|
)
|
||||||
const options = AnchorProvider.defaultOptions()
|
const options = AnchorProvider.defaultOptions()
|
||||||
export const CLUSTER = 'mainnet-beta'
|
export const CLUSTER: 'mainnet-beta' | 'devnet' = 'mainnet-beta'
|
||||||
const wallet = new EmptyWallet(Keypair.generate())
|
const wallet = new EmptyWallet(Keypair.generate())
|
||||||
const DEFAULT_PROVIDER = new AnchorProvider(connection, wallet, options)
|
const DEFAULT_PROVIDER = new AnchorProvider(connection, wallet, options)
|
||||||
DEFAULT_PROVIDER.opts.skipPreflight = true
|
DEFAULT_PROVIDER.opts.skipPreflight = true
|
||||||
|
@ -164,16 +163,11 @@ export type MangoStore = {
|
||||||
initialLoad: boolean
|
initialLoad: boolean
|
||||||
loading: boolean
|
loading: boolean
|
||||||
}
|
}
|
||||||
coingeckoPrices: {
|
|
||||||
data: any[]
|
|
||||||
loading: boolean
|
|
||||||
}
|
|
||||||
connected: boolean
|
connected: boolean
|
||||||
connection: Connection
|
connection: Connection
|
||||||
group: Group | undefined
|
group: Group | undefined
|
||||||
groupLoaded: boolean
|
groupLoaded: boolean
|
||||||
client: MangoClient
|
client: MangoClient
|
||||||
jupiterTokens: Token[]
|
|
||||||
mangoAccount: {
|
mangoAccount: {
|
||||||
current: MangoAccount | undefined
|
current: MangoAccount | undefined
|
||||||
initialLoad: boolean
|
initialLoad: boolean
|
||||||
|
@ -246,9 +240,7 @@ export type MangoStore = {
|
||||||
mangoAccountPk: string,
|
mangoAccountPk: string,
|
||||||
range: number
|
range: number
|
||||||
) => Promise<void>
|
) => Promise<void>
|
||||||
fetchCoingeckoPrices: () => Promise<void>
|
|
||||||
fetchGroup: () => Promise<void>
|
fetchGroup: () => Promise<void>
|
||||||
fetchJupiterTokens: () => Promise<void>
|
|
||||||
reloadMangoAccount: () => Promise<void>
|
reloadMangoAccount: () => Promise<void>
|
||||||
fetchMangoAccounts: (wallet: Wallet) => Promise<void>
|
fetchMangoAccounts: (wallet: Wallet) => Promise<void>
|
||||||
fetchNfts: (connection: Connection, walletPk: PublicKey) => void
|
fetchNfts: (connection: Connection, walletPk: PublicKey) => void
|
||||||
|
@ -270,16 +262,11 @@ const mangoStore = create<MangoStore>()(
|
||||||
initialLoad: false,
|
initialLoad: false,
|
||||||
loading: true,
|
loading: true,
|
||||||
},
|
},
|
||||||
coingeckoPrices: {
|
|
||||||
data: [],
|
|
||||||
loading: false,
|
|
||||||
},
|
|
||||||
connected: false,
|
connected: false,
|
||||||
connection,
|
connection,
|
||||||
group: undefined,
|
group: undefined,
|
||||||
groupLoaded: false,
|
groupLoaded: false,
|
||||||
client: DEFAULT_CLIENT,
|
client: DEFAULT_CLIENT,
|
||||||
jupiterTokens: [],
|
|
||||||
mangoAccount: {
|
mangoAccount: {
|
||||||
current: undefined,
|
current: undefined,
|
||||||
initialLoad: true,
|
initialLoad: true,
|
||||||
|
@ -477,45 +464,6 @@ const mangoStore = create<MangoStore>()(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetchCoingeckoPrices: async () => {
|
|
||||||
const set = get().set
|
|
||||||
set((state) => {
|
|
||||||
state.coingeckoPrices.loading = true
|
|
||||||
})
|
|
||||||
try {
|
|
||||||
const jupiterTokens = mangoStore.getState().jupiterTokens
|
|
||||||
if (jupiterTokens.length) {
|
|
||||||
const coingeckoIds = jupiterTokens.map((token) => ({
|
|
||||||
id: token.extensions?.coingeckoId,
|
|
||||||
symbol: token.symbol,
|
|
||||||
}))
|
|
||||||
const promises: any = []
|
|
||||||
for (const token of coingeckoIds) {
|
|
||||||
if (token.id) {
|
|
||||||
promises.push(
|
|
||||||
fetch(
|
|
||||||
`https://api.coingecko.com/api/v3/coins/${token.id}/market_chart?vs_currency=usd&days=1`
|
|
||||||
).then((res) => res.json())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await Promise.all(promises)
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
data[i].symbol = coingeckoIds[i].symbol
|
|
||||||
}
|
|
||||||
set((state) => {
|
|
||||||
state.coingeckoPrices.data = data
|
|
||||||
state.coingeckoPrices.loading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Unable to load Coingecko prices')
|
|
||||||
set((state) => {
|
|
||||||
state.coingeckoPrices.loading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fetchGroup: async () => {
|
fetchGroup: async () => {
|
||||||
try {
|
try {
|
||||||
const set = get().set
|
const set = get().set
|
||||||
|
@ -771,38 +719,6 @@ const mangoStore = create<MangoStore>()(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetchJupiterTokens: async () => {
|
|
||||||
const set = mangoStore.getState().set
|
|
||||||
const group = mangoStore.getState().group
|
|
||||||
if (!group) {
|
|
||||||
console.error(
|
|
||||||
'Mango group unavailable; Loading jupiter tokens failed'
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const bankMints = Array.from(group.banksMapByName.values()).map((b) =>
|
|
||||||
b[0].mint.toString()
|
|
||||||
)
|
|
||||||
|
|
||||||
fetch(TOKEN_LIST_URL[CLUSTER])
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((result) => {
|
|
||||||
const groupTokens = result.filter((t: any) =>
|
|
||||||
bankMints.includes(t.address)
|
|
||||||
)
|
|
||||||
const inputTokenInfo = groupTokens.find(
|
|
||||||
(t: any) => t.symbol === INPUT_TOKEN_DEFAULT
|
|
||||||
)
|
|
||||||
const outputTokenInfo = groupTokens.find(
|
|
||||||
(t: any) => t.symbol === OUTPUT_TOKEN_DEFAULT
|
|
||||||
)
|
|
||||||
set((s) => {
|
|
||||||
s.swap.inputTokenInfo = inputTokenInfo
|
|
||||||
s.swap.outputTokenInfo = outputTokenInfo
|
|
||||||
s.jupiterTokens = groupTokens
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
connectMangoClientWithWallet: async (wallet: WalletAdapter) => {
|
connectMangoClientWithWallet: async (wallet: WalletAdapter) => {
|
||||||
const set = get().set
|
const set = get().set
|
||||||
try {
|
try {
|
||||||
|
|
130
types/jupiter.ts
130
types/jupiter.ts
|
@ -1,6 +1,134 @@
|
||||||
import { RouteInfo } from '@jup-ag/core'
|
import { AccountMeta } from '@solana/web3.js'
|
||||||
|
import { AccountInfo } from '@solana/web3.js'
|
||||||
import Decimal from 'decimal.js'
|
import Decimal from 'decimal.js'
|
||||||
|
|
||||||
|
export declare type SideType = typeof Side.Ask | typeof Side.Bid
|
||||||
|
export declare const Side: {
|
||||||
|
Bid: {
|
||||||
|
bid: {}
|
||||||
|
}
|
||||||
|
Ask: {
|
||||||
|
ask: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QuoteParams {
|
||||||
|
sourceMint: string
|
||||||
|
destinationMint: string
|
||||||
|
amount: number
|
||||||
|
swapMode: SwapMode
|
||||||
|
}
|
||||||
|
export declare type TokenMintAddress = string
|
||||||
|
export interface Quote {
|
||||||
|
notEnoughLiquidity: boolean
|
||||||
|
minInAmount?: number
|
||||||
|
minOutAmount?: number
|
||||||
|
inAmount: number
|
||||||
|
outAmount: number
|
||||||
|
feeAmount: number
|
||||||
|
feeMint: TokenMintAddress
|
||||||
|
feePct: number
|
||||||
|
priceImpactPct: number
|
||||||
|
}
|
||||||
|
export declare type QuoteMintToReferrer = Map<TokenMintAddress, string>
|
||||||
|
export interface SwapParams {
|
||||||
|
sourceMint: string
|
||||||
|
destinationMint: string
|
||||||
|
userSourceTokenAccount: string
|
||||||
|
userDestinationTokenAccount: string
|
||||||
|
userTransferAuthority: string
|
||||||
|
/**
|
||||||
|
* amount is used for instruction and can be null when it is an intermediate swap, only the first swap has an amount
|
||||||
|
*/
|
||||||
|
amount: number
|
||||||
|
swapMode: SwapMode
|
||||||
|
openOrdersAddress?: string
|
||||||
|
quoteMintToReferrer?: QuoteMintToReferrer
|
||||||
|
}
|
||||||
|
export declare type PlatformFee = {
|
||||||
|
feeBps: number
|
||||||
|
feeAccount: string
|
||||||
|
}
|
||||||
|
export interface ExactOutSwapParams extends SwapParams {
|
||||||
|
inAmount: number
|
||||||
|
slippageBps: number
|
||||||
|
platformFee?: PlatformFee
|
||||||
|
overflowFeeAccount?: string
|
||||||
|
}
|
||||||
|
export declare type AccountInfoMap = Map<string, AccountInfo<Buffer> | null>
|
||||||
|
|
||||||
|
declare type AmmLabel =
|
||||||
|
| 'Aldrin'
|
||||||
|
| 'Crema'
|
||||||
|
| 'Cropper'
|
||||||
|
| 'Cykura'
|
||||||
|
| 'DeltaFi'
|
||||||
|
| 'GooseFX'
|
||||||
|
| 'Invariant'
|
||||||
|
| 'Lifinity'
|
||||||
|
| 'Lifinity V2'
|
||||||
|
| 'Marinade'
|
||||||
|
| 'Mercurial'
|
||||||
|
| 'Meteora'
|
||||||
|
| 'Raydium'
|
||||||
|
| 'Raydium CLMM'
|
||||||
|
| 'Saber'
|
||||||
|
| 'Serum'
|
||||||
|
| 'Orca'
|
||||||
|
| 'Step'
|
||||||
|
| 'Penguin'
|
||||||
|
| 'Saros'
|
||||||
|
| 'Stepn'
|
||||||
|
| 'Orca (Whirlpools)'
|
||||||
|
| 'Sencha'
|
||||||
|
| 'Saber (Decimals)'
|
||||||
|
| 'Dradex'
|
||||||
|
| 'Balansol'
|
||||||
|
| 'Openbook'
|
||||||
|
| 'Unknown'
|
||||||
|
|
||||||
|
export interface TransactionFeeInfo {
|
||||||
|
signatureFee: number
|
||||||
|
openOrdersDeposits: number[]
|
||||||
|
ataDeposits: number[]
|
||||||
|
totalFeeAndDeposits: number
|
||||||
|
minimumSOLForTransaction: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare enum SwapMode {
|
||||||
|
ExactIn = 'ExactIn',
|
||||||
|
ExactOut = 'ExactOut',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Fee {
|
||||||
|
amount: number
|
||||||
|
mint: string
|
||||||
|
pct: number
|
||||||
|
}
|
||||||
|
export interface MarketInfo {
|
||||||
|
id: string
|
||||||
|
inAmount: number
|
||||||
|
inputMint: string
|
||||||
|
label: string
|
||||||
|
lpFee: Fee
|
||||||
|
notEnoughLiquidity: boolean
|
||||||
|
outAmount: number
|
||||||
|
outputMint: string
|
||||||
|
platformFee: Fee
|
||||||
|
priceImpactPct: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RouteInfo {
|
||||||
|
amount: number
|
||||||
|
inAmount: number
|
||||||
|
marketInfos: MarketInfo[]
|
||||||
|
otherAmountThreshold: number
|
||||||
|
outAmount: number
|
||||||
|
priceImpactPct: number
|
||||||
|
slippageBps: number
|
||||||
|
swapMode: SwapMode
|
||||||
|
}
|
||||||
|
|
||||||
export type Routes = {
|
export type Routes = {
|
||||||
routesInfos: RouteInfo[]
|
routesInfos: RouteInfo[]
|
||||||
cached: boolean
|
cached: boolean
|
||||||
|
|
|
@ -3,7 +3,8 @@ export const LAST_ACCOUNT_KEY = 'mangoAccount-0.1'
|
||||||
export const CLIENT_TX_TIMEOUT = 90000
|
export const CLIENT_TX_TIMEOUT = 90000
|
||||||
|
|
||||||
export const INPUT_TOKEN_DEFAULT = 'USDC'
|
export const INPUT_TOKEN_DEFAULT = 'USDC'
|
||||||
|
export const MANGO_MINT = 'MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac'
|
||||||
|
export const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
|
||||||
export const OUTPUT_TOKEN_DEFAULT = 'SOL'
|
export const OUTPUT_TOKEN_DEFAULT = 'SOL'
|
||||||
|
|
||||||
export const IS_ONBOARDED_KEY = 'isOnboarded-0.1'
|
export const IS_ONBOARDED_KEY = 'isOnboarded-0.1'
|
||||||
|
|
Loading…
Reference in New Issue