use jupiter api and hooks

This commit is contained in:
tjs 2022-11-18 14:11:06 -05:00
parent 5727d0bc7f
commit 203c571bc5
43 changed files with 645 additions and 1140 deletions

View File

@ -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(

View File

@ -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(() => {

View File

@ -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 (
<> <>

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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
} }

View File

@ -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]}

View File

@ -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
} }

View File

@ -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', {
userPublicKey, 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,
// 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}

View File

@ -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}
/> />
)} )}
</> </>

View File

@ -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>

View File

@ -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

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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 />

View File

@ -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()}`

View File

@ -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 (

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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') => {

View File

@ -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

View File

@ -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>
) )

View File

@ -74,6 +74,7 @@ const SpotSlider = () => {
} }
leverageMax={leverageMax} leverageMax={leverageMax}
onChange={handleSlide} onChange={handleSlide}
step={0.01}
/> />
</div> </div>
) )

View File

@ -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

49
hooks/useCoingecko.ts Normal file
View File

@ -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 || [],
}
}

51
hooks/useJupiterMints.ts Normal file
View File

@ -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

10
hooks/useMangoGroup.ts Normal file
View File

@ -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 }
}

View File

@ -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",

View File

@ -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
} }
const getCoingeckoData = async (id: string) => {
const response = await fetchTokenInfo(id)
setCoingeckoData(response)
setLoading(false)
}
useEffect(() => { useEffect(() => {
const getCoingeckoData = async (id: string) => {
const response = await fetchTokenInfo(id)
setCoingeckoData(response)
setLoading(false)
}
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
} }

View File

@ -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 {

View File

@ -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

View File

@ -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'

638
yarn.lock

File diff suppressed because it is too large Load Diff