2022-08-17 13:06:25 -07:00
|
|
|
import React, {
|
|
|
|
Dispatch,
|
|
|
|
SetStateAction,
|
|
|
|
useEffect,
|
|
|
|
useMemo,
|
|
|
|
useState,
|
|
|
|
} from 'react'
|
|
|
|
import { TransactionInstruction, PublicKey } from '@solana/web3.js'
|
2022-08-25 06:00:42 -07:00
|
|
|
import { toUiDecimals } from '@blockworks-foundation/mango-v4'
|
2022-08-17 13:06:25 -07:00
|
|
|
import { Jupiter, RouteInfo, TransactionFeeInfo } from '@jup-ag/core'
|
|
|
|
import JSBI from 'jsbi'
|
|
|
|
import Decimal from 'decimal.js'
|
|
|
|
|
2022-08-20 11:17:57 -07:00
|
|
|
import mangoStore from '../../store/mangoStore'
|
2022-08-17 13:06:25 -07:00
|
|
|
import RoutesModal from './RoutesModal'
|
|
|
|
import Button, { IconButton } from '../shared/Button'
|
|
|
|
import Loading from '../shared/Loading'
|
|
|
|
import {
|
|
|
|
ArrowLeftIcon,
|
|
|
|
PencilIcon,
|
|
|
|
SwitchHorizontalIcon,
|
|
|
|
} from '@heroicons/react/solid'
|
|
|
|
import { useTranslation } from 'next-i18next'
|
|
|
|
import Image from 'next/image'
|
2022-08-25 14:24:10 -07:00
|
|
|
import {
|
|
|
|
floorToDecimal,
|
|
|
|
formatDecimal,
|
|
|
|
formatFixedDecimals,
|
|
|
|
} from '../../utils/numbers'
|
2022-08-19 21:03:26 -07:00
|
|
|
import { notify } from '../../utils/notifications'
|
2022-08-17 13:06:25 -07:00
|
|
|
|
|
|
|
type JupiterRouteInfoProps = {
|
2022-08-17 18:26:38 -07:00
|
|
|
amountIn: Decimal
|
2022-08-17 13:06:25 -07:00
|
|
|
jupiter: Jupiter | undefined
|
2022-08-18 13:50:34 -07:00
|
|
|
onClose: () => void
|
|
|
|
routes: RouteInfo[] | undefined
|
2022-08-17 13:06:25 -07:00
|
|
|
selectedRoute: RouteInfo | undefined
|
|
|
|
setSelectedRoute: Dispatch<SetStateAction<RouteInfo | undefined>>
|
2022-08-18 13:50:34 -07:00
|
|
|
slippage: number
|
2022-08-17 13:06:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
const parseJupiterRoute = async (
|
|
|
|
jupiter: Jupiter,
|
|
|
|
selectedRoute: RouteInfo,
|
|
|
|
userPublicKey: PublicKey
|
|
|
|
): Promise<TransactionInstruction[]> => {
|
|
|
|
const { transactions } = await jupiter.exchange({
|
|
|
|
routeInfo: selectedRoute,
|
|
|
|
userPublicKey,
|
|
|
|
})
|
|
|
|
const { swapTransaction } = transactions
|
|
|
|
const instructions = []
|
|
|
|
for (const ix of swapTransaction.instructions) {
|
|
|
|
if (
|
|
|
|
ix.programId.toBase58() === 'JUP3c2Uh3WA4Ng34tw6kPd2G4C5BB21Xo36Je1s32Ph'
|
|
|
|
) {
|
|
|
|
instructions.push(ix)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return instructions
|
|
|
|
}
|
|
|
|
|
2022-08-25 13:24:37 -07:00
|
|
|
const EMPTY_COINGECKO_PRICES = {
|
|
|
|
inputCoingeckoPrice: 0,
|
|
|
|
outputCoingeckoPrice: 0,
|
|
|
|
}
|
|
|
|
|
2022-08-17 13:06:25 -07:00
|
|
|
const JupiterRouteInfo = ({
|
|
|
|
amountIn,
|
|
|
|
onClose,
|
|
|
|
jupiter,
|
|
|
|
routes,
|
|
|
|
selectedRoute,
|
|
|
|
setSelectedRoute,
|
|
|
|
}: JupiterRouteInfoProps) => {
|
2022-08-26 10:17:31 -07:00
|
|
|
const { t } = useTranslation(['common', 'trade'])
|
2022-08-17 13:06:25 -07:00
|
|
|
const [showRoutesModal, setShowRoutesModal] = useState(false)
|
|
|
|
const [swapRate, setSwapRate] = useState<boolean>(false)
|
|
|
|
const [depositAndFee, setDepositAndFee] = useState<TransactionFeeInfo>()
|
|
|
|
const [feeValue, setFeeValue] = useState<number | null>(null)
|
2022-08-19 21:03:26 -07:00
|
|
|
const [submitting, setSubmitting] = useState(false)
|
2022-08-25 13:24:37 -07:00
|
|
|
const [coingeckoPrices, setCoingeckoPrices] = useState(EMPTY_COINGECKO_PRICES)
|
2022-08-17 13:06:25 -07:00
|
|
|
|
2022-08-25 13:24:37 -07:00
|
|
|
const inputTokenInfo = mangoStore((s) => s.swap.inputTokenInfo)
|
|
|
|
const outputTokenInfo = mangoStore((s) => s.swap.outputTokenInfo)
|
2022-08-17 13:06:25 -07:00
|
|
|
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
|
|
|
const connected = mangoStore((s) => s.connected)
|
|
|
|
|
|
|
|
const inputTokenIconUri = useMemo(() => {
|
2022-08-18 13:50:34 -07:00
|
|
|
return inputTokenInfo ? inputTokenInfo.logoURI : ''
|
|
|
|
}, [inputTokenInfo])
|
2022-08-17 13:06:25 -07:00
|
|
|
|
|
|
|
const amountOut = useMemo(() => {
|
|
|
|
if (!selectedRoute || !outputTokenInfo) return
|
|
|
|
return toUiDecimals(
|
|
|
|
JSBI.toNumber(selectedRoute.outAmount),
|
|
|
|
outputTokenInfo.decimals
|
|
|
|
)
|
|
|
|
}, [selectedRoute, outputTokenInfo])
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
const getDepositAndFee = async () => {
|
|
|
|
const fees = await selectedRoute?.getDepositAndFee()
|
|
|
|
if (fees) {
|
|
|
|
setDepositAndFee(fees)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (selectedRoute && connected) {
|
|
|
|
getDepositAndFee()
|
|
|
|
}
|
|
|
|
}, [selectedRoute, connected])
|
|
|
|
|
2022-08-25 13:24:37 -07:00
|
|
|
useEffect(() => {
|
|
|
|
setCoingeckoPrices(EMPTY_COINGECKO_PRICES)
|
|
|
|
const fetchTokenPrices = async () => {
|
|
|
|
const inputId = inputTokenInfo?.extensions?.coingeckoId
|
|
|
|
const outputId = outputTokenInfo?.extensions?.coingeckoId
|
|
|
|
|
|
|
|
if (inputId && outputId) {
|
|
|
|
const results = await fetch(
|
|
|
|
`https://api.coingecko.com/api/v3/simple/price?ids=${inputId},${outputId}&vs_currencies=usd`
|
|
|
|
)
|
|
|
|
const json = await results.json()
|
|
|
|
if (json[inputId]?.usd && json[outputId]?.usd) {
|
|
|
|
setCoingeckoPrices({
|
|
|
|
inputCoingeckoPrice: json[inputId].usd,
|
|
|
|
outputCoingeckoPrice: json[outputId].usd,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inputTokenInfo && outputTokenInfo) {
|
|
|
|
fetchTokenPrices()
|
|
|
|
}
|
|
|
|
}, [inputTokenInfo, outputTokenInfo])
|
|
|
|
|
2022-08-17 13:06:25 -07:00
|
|
|
const onSwap = async () => {
|
|
|
|
if (!jupiter || !selectedRoute) return
|
2022-08-19 21:03:26 -07:00
|
|
|
try {
|
|
|
|
const client = mangoStore.getState().client
|
|
|
|
const group = mangoStore.getState().group
|
|
|
|
const actions = mangoStore.getState().actions
|
|
|
|
const mangoAccount = mangoStore.getState().mangoAccount.current
|
|
|
|
const inputBank = mangoStore.getState().swap.inputBank
|
|
|
|
const outputBank = mangoStore.getState().swap.outputBank
|
|
|
|
|
|
|
|
if (!mangoAccount || !group || !inputBank || !outputBank) return
|
|
|
|
|
|
|
|
const ixs = await parseJupiterRoute(
|
|
|
|
jupiter,
|
|
|
|
selectedRoute,
|
|
|
|
mangoAccount!.owner
|
|
|
|
)
|
|
|
|
|
|
|
|
try {
|
|
|
|
setSubmitting(true)
|
|
|
|
const tx = await client.marginTrade({
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
|
|
|
inputMintPk: inputBank.mint,
|
|
|
|
amountIn: amountIn.toNumber(),
|
|
|
|
outputMintPk: outputBank.mint,
|
|
|
|
userDefinedInstructions: ixs,
|
|
|
|
flashLoanType: { swap: {} },
|
|
|
|
})
|
|
|
|
|
|
|
|
notify({
|
|
|
|
title: 'Transaction confirmed',
|
|
|
|
type: 'success',
|
|
|
|
txid: tx,
|
|
|
|
})
|
2022-08-25 20:30:39 -07:00
|
|
|
await actions.reloadMangoAccount()
|
2022-08-19 21:03:26 -07:00
|
|
|
} catch (e: any) {
|
2022-08-20 17:09:36 -07:00
|
|
|
console.error('onSwap error: ', e)
|
2022-08-19 21:03:26 -07:00
|
|
|
notify({
|
|
|
|
title: 'Transaction failed',
|
|
|
|
description: e.message,
|
|
|
|
txid: e?.signature,
|
|
|
|
type: 'error',
|
|
|
|
})
|
|
|
|
} finally {
|
|
|
|
setSubmitting(false)
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
console.error('Swap error:', e)
|
|
|
|
} finally {
|
|
|
|
onClose()
|
|
|
|
}
|
2022-08-17 13:06:25 -07:00
|
|
|
}
|
|
|
|
|
2022-08-25 13:24:37 -07:00
|
|
|
const borrowAmount = useMemo(() => {
|
|
|
|
const mangoAccount = mangoStore.getState().mangoAccount.current
|
|
|
|
const inputBank = mangoStore.getState().swap.inputBank
|
|
|
|
if (!mangoAccount || !inputBank) return 0
|
|
|
|
|
|
|
|
const remainingBalance =
|
|
|
|
mangoAccount.getTokenDepositsUi(inputBank) - amountIn.toNumber()
|
|
|
|
return remainingBalance < 0 ? Math.abs(remainingBalance) : 0
|
|
|
|
}, [amountIn])
|
|
|
|
|
|
|
|
const coinGeckoPriceDifference = useMemo(() => {
|
|
|
|
return amountOut
|
2022-08-25 14:24:10 -07:00
|
|
|
? floorToDecimal(
|
|
|
|
(amountIn.toNumber() / amountOut -
|
|
|
|
coingeckoPrices?.outputCoingeckoPrice /
|
|
|
|
coingeckoPrices?.inputCoingeckoPrice) /
|
|
|
|
(amountIn.toNumber() / amountOut),
|
|
|
|
1
|
|
|
|
)
|
2022-08-25 13:24:37 -07:00
|
|
|
: 0
|
|
|
|
}, [coingeckoPrices, amountIn, amountOut])
|
|
|
|
|
2022-08-17 13:06:25 -07:00
|
|
|
return routes?.length && selectedRoute && outputTokenInfo && amountOut ? (
|
|
|
|
<div className="flex h-full flex-col justify-between">
|
|
|
|
<div>
|
|
|
|
<IconButton
|
2022-08-25 04:15:21 -07:00
|
|
|
className="absolute mr-3 text-th-fgd-3"
|
2022-08-17 13:06:25 -07:00
|
|
|
onClick={onClose}
|
|
|
|
size="small"
|
|
|
|
>
|
|
|
|
<ArrowLeftIcon className="h-5 w-5" />
|
|
|
|
</IconButton>
|
2022-08-25 06:00:42 -07:00
|
|
|
<div className="mb-6 mt-4 flex justify-center">
|
2022-08-17 13:06:25 -07:00
|
|
|
<div className="flex flex-col items-center">
|
|
|
|
<div className="relative mb-2 w-[72px]">
|
|
|
|
<Image alt="" width="40" height="40" src={inputTokenIconUri} />
|
|
|
|
<div className="absolute right-0 top-0">
|
|
|
|
<Image
|
|
|
|
className="drop-shadow-md"
|
|
|
|
alt=""
|
|
|
|
width="40"
|
|
|
|
height="40"
|
|
|
|
src={outputTokenInfo.logoURI}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-08-25 03:52:44 -07:00
|
|
|
<p className="mb-0.5 text-center text-lg text-th-fgd-1">{`${amountIn} ${
|
|
|
|
inputTokenInfo!.symbol
|
2022-08-18 13:50:34 -07:00
|
|
|
} for ${amountOut} ${outputTokenInfo.symbol}`}</p>
|
2022-08-17 13:06:25 -07:00
|
|
|
</div>
|
|
|
|
</div>
|
2022-08-25 13:24:37 -07:00
|
|
|
|
2022-08-17 13:06:25 -07:00
|
|
|
<div className="space-y-2 px-1">
|
2022-08-25 13:24:37 -07:00
|
|
|
<div className="flex justify-between">
|
2022-08-26 10:17:31 -07:00
|
|
|
<span>Rate</span>
|
2022-08-25 13:24:37 -07:00
|
|
|
<div>
|
|
|
|
<div className="flex items-center justify-end">
|
|
|
|
<p className="text-right text-sm">
|
|
|
|
{swapRate ? (
|
|
|
|
<>
|
|
|
|
1 {inputTokenInfo!.name} ≈{' '}
|
|
|
|
{formatDecimal(amountOut / amountIn.toNumber(), 6)}{' '}
|
|
|
|
{outputTokenInfo?.symbol}
|
|
|
|
</>
|
|
|
|
) : (
|
|
|
|
<>
|
|
|
|
1 {outputTokenInfo?.symbol} ≈{' '}
|
|
|
|
{formatDecimal(amountIn.toNumber() / amountOut, 6)}{' '}
|
|
|
|
{inputTokenInfo!.name}
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</p>
|
|
|
|
<SwitchHorizontalIcon
|
|
|
|
className="default-transition ml-1 h-4 w-4 cursor-pointer text-th-fgd-3 hover:text-th-fgd-2"
|
|
|
|
onClick={() => setSwapRate(!swapRate)}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<div className="space-y-2 px-1">
|
|
|
|
{coingeckoPrices?.outputCoingeckoPrice &&
|
|
|
|
coingeckoPrices?.inputCoingeckoPrice ? (
|
|
|
|
<div
|
|
|
|
className={`text-right ${
|
|
|
|
coinGeckoPriceDifference * 100 > 0
|
|
|
|
? 'text-th-red'
|
|
|
|
: 'text-th-green'
|
|
|
|
}`}
|
|
|
|
>
|
|
|
|
{Math.abs(coinGeckoPriceDifference * 100).toFixed(1)}%{' '}
|
|
|
|
<span className="text-th-fgd-4">{`${
|
|
|
|
coinGeckoPriceDifference * 100 <= 0
|
|
|
|
? 'cheaper'
|
|
|
|
: 'more expensive'
|
|
|
|
} than CoinGecko`}</span>
|
|
|
|
</div>
|
|
|
|
) : null}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-08-17 13:06:25 -07:00
|
|
|
<div className="flex justify-between">
|
|
|
|
<p className="text-sm text-th-fgd-3">
|
|
|
|
{t('trade:minimum-received')}
|
|
|
|
</p>
|
|
|
|
{outputTokenInfo?.decimals ? (
|
|
|
|
<p className="text-right text-sm text-th-fgd-1">
|
|
|
|
{formatDecimal(
|
|
|
|
JSBI.toNumber(selectedRoute?.otherAmountThreshold) /
|
|
|
|
10 ** outputTokenInfo.decimals || 1,
|
|
|
|
6
|
|
|
|
)}{' '}
|
|
|
|
{outputTokenInfo?.symbol}
|
|
|
|
</p>
|
|
|
|
) : null}
|
|
|
|
</div>
|
2022-08-25 13:24:37 -07:00
|
|
|
{borrowAmount ? (
|
|
|
|
<div className="flex justify-between">
|
|
|
|
<p className="text-sm text-th-fgd-3">{t('borrow-amount')}</p>
|
|
|
|
<p className="text-right text-sm text-th-fgd-1">
|
2022-08-26 10:17:31 -07:00
|
|
|
~ {formatFixedDecimals(borrowAmount)} {inputTokenInfo?.symbol}
|
2022-08-25 13:24:37 -07:00
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
) : null}
|
2022-08-17 13:06:25 -07:00
|
|
|
<div className="flex justify-between">
|
|
|
|
<p className="text-sm text-th-fgd-3">{t('trade:slippage')}</p>
|
|
|
|
<p className="text-right text-sm text-th-fgd-1">
|
|
|
|
{selectedRoute?.priceImpactPct * 100 < 0.1
|
|
|
|
? '< 0.1%'
|
|
|
|
: `~ ${(selectedRoute?.priceImpactPct * 100).toFixed(4)}%`}
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
<p className="text-sm text-th-fgd-3">Swap Route</p>
|
|
|
|
<div
|
|
|
|
className="flex items-center text-th-fgd-1 md:hover:cursor-pointer md:hover:text-th-fgd-3"
|
|
|
|
role="button"
|
|
|
|
onClick={() => setShowRoutesModal(true)}
|
|
|
|
>
|
|
|
|
<span className="overflow-ellipsis whitespace-nowrap">
|
|
|
|
{selectedRoute?.marketInfos.map((info, index) => {
|
|
|
|
let includeSeparator = false
|
|
|
|
if (
|
|
|
|
selectedRoute?.marketInfos.length > 1 &&
|
|
|
|
index !== selectedRoute?.marketInfos.length - 1
|
|
|
|
) {
|
|
|
|
includeSeparator = true
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<span key={index}>{`${info.amm.label} ${
|
|
|
|
includeSeparator ? 'x ' : ''
|
|
|
|
}`}</span>
|
|
|
|
)
|
|
|
|
})}
|
|
|
|
</span>
|
|
|
|
<PencilIcon className="ml-2 h-4 w-4" />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{typeof feeValue === 'number' ? (
|
|
|
|
<div className="flex justify-between">
|
|
|
|
<p className="text-sm text-th-fgd-3">{t('fee')}</p>
|
|
|
|
<div className="flex items-center">
|
|
|
|
<p className="text-right text-sm text-th-fgd-1">
|
|
|
|
≈ ${feeValue?.toFixed(2)}
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
selectedRoute?.marketInfos.map((info, index) => {
|
|
|
|
const feeToken = jupiterTokens.find(
|
|
|
|
(item) => item?.address === info.lpFee?.mint
|
|
|
|
)
|
|
|
|
return (
|
|
|
|
<div className="flex justify-between" key={index}>
|
|
|
|
<p className="text-sm text-th-fgd-3">
|
|
|
|
{t('trade:fees-paid-to', {
|
|
|
|
route: info?.amm?.label,
|
|
|
|
})}
|
|
|
|
</p>
|
|
|
|
{feeToken?.decimals && (
|
|
|
|
<p className="text-right text-sm text-th-fgd-1">
|
|
|
|
{(
|
|
|
|
JSBI.toNumber(info.lpFee?.amount) /
|
|
|
|
Math.pow(10, feeToken.decimals)
|
|
|
|
).toFixed(6)}{' '}
|
|
|
|
{feeToken?.symbol} ({info.lpFee?.pct * 100}%)
|
|
|
|
</p>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
})
|
|
|
|
)}
|
|
|
|
{/* {connected ? (
|
|
|
|
<>
|
|
|
|
<div className="flex justify-between">
|
|
|
|
<span>{t('swap:transaction-fee')}</span>
|
|
|
|
<div className="text-right text-th-fgd-1">
|
|
|
|
{depositAndFee
|
|
|
|
? depositAndFee?.signatureFee / Math.pow(10, 9)
|
|
|
|
: '-'}{' '}
|
|
|
|
SOL
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{depositAndFee?.ataDepositLength ||
|
|
|
|
depositAndFee?.openOrdersDeposits?.length ? (
|
|
|
|
<div className="flex justify-between">
|
|
|
|
<div className="flex items-center">
|
|
|
|
<span>{t('deposit')}</span>
|
|
|
|
<Tooltip
|
|
|
|
content={
|
|
|
|
<>
|
|
|
|
{depositAndFee?.ataDepositLength ? (
|
|
|
|
<div>{t('need-ata-account')}</div>
|
|
|
|
) : null}
|
|
|
|
{depositAndFee?.openOrdersDeposits?.length ? (
|
|
|
|
<div className="mt-2">
|
|
|
|
{t('swap:serum-requires-openorders')}{' '}
|
|
|
|
<a
|
|
|
|
href="https://docs.google.com/document/d/1qEWc_Bmc1aAxyCUcilKB4ZYpOu3B0BxIbe__dRYmVns/"
|
|
|
|
target="_blank"
|
|
|
|
rel="noopener noreferrer"
|
|
|
|
>
|
|
|
|
{t('swap:heres-how')}
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
) : null}
|
|
|
|
</>
|
|
|
|
}
|
|
|
|
placement={'left'}
|
|
|
|
>
|
|
|
|
<InformationCircleIcon className="ml-1.5 h-3.5 w-3.5 cursor-help text-th-primary" />
|
|
|
|
</Tooltip>
|
|
|
|
</div>
|
|
|
|
<div>
|
|
|
|
{depositAndFee?.ataDepositLength ? (
|
|
|
|
<div className="text-right text-th-fgd-1">
|
|
|
|
{depositAndFee?.ataDepositLength === 1
|
|
|
|
? t('swap:ata-deposit-details', {
|
|
|
|
cost: (
|
|
|
|
depositAndFee?.ataDeposit / Math.pow(10, 9)
|
|
|
|
).toFixed(5),
|
|
|
|
count: depositAndFee?.ataDepositLength,
|
|
|
|
})
|
|
|
|
: t('swap:ata-deposit-details_plural', {
|
|
|
|
cost: (
|
|
|
|
depositAndFee?.ataDeposit / Math.pow(10, 9)
|
|
|
|
).toFixed(5),
|
|
|
|
count: depositAndFee?.ataDepositLength,
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
) : null}
|
|
|
|
{depositAndFee?.openOrdersDeposits?.length ? (
|
|
|
|
<div className="text-right text-th-fgd-1">
|
|
|
|
{depositAndFee?.openOrdersDeposits.length > 1
|
|
|
|
? t('swap:serum-details_plural', {
|
|
|
|
cost: (
|
|
|
|
sum(depositAndFee?.openOrdersDeposits) /
|
|
|
|
Math.pow(10, 9)
|
|
|
|
).toFixed(5),
|
|
|
|
count: depositAndFee?.openOrdersDeposits.length,
|
|
|
|
})
|
|
|
|
: t('swap:serum-details', {
|
|
|
|
cost: (
|
|
|
|
sum(depositAndFee?.openOrdersDeposits) /
|
|
|
|
Math.pow(10, 9)
|
|
|
|
).toFixed(5),
|
|
|
|
count: depositAndFee?.openOrdersDeposits.length,
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
) : null}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
) : null}
|
|
|
|
</>
|
|
|
|
) : null} */}
|
|
|
|
</div>
|
|
|
|
{showRoutesModal ? (
|
|
|
|
<RoutesModal
|
|
|
|
show={showRoutesModal}
|
|
|
|
onClose={() => setShowRoutesModal(false)}
|
|
|
|
setSelectedRoute={setSelectedRoute}
|
|
|
|
selectedRoute={selectedRoute}
|
|
|
|
routes={routes}
|
2022-08-18 13:50:34 -07:00
|
|
|
inputTokenSymbol={inputTokenInfo!.name}
|
2022-08-17 13:06:25 -07:00
|
|
|
outputTokenInfo={outputTokenInfo}
|
|
|
|
/>
|
|
|
|
) : null}
|
|
|
|
</div>
|
|
|
|
<div className="flex items-center justify-center pb-6">
|
|
|
|
<Button
|
|
|
|
onClick={onSwap}
|
|
|
|
className="flex w-full items-center justify-center text-base"
|
|
|
|
size="large"
|
|
|
|
>
|
|
|
|
{submitting ? (
|
|
|
|
<Loading className="mr-2 h-5 w-5" />
|
|
|
|
) : (
|
|
|
|
t('trade:confirm-trade')
|
|
|
|
)}
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
) : null
|
|
|
|
}
|
|
|
|
|
|
|
|
export default React.memo(JupiterRouteInfo)
|