mango-v4-ui/components/swap/SwapReviewRouteInfo.tsx

718 lines
26 KiB
TypeScript
Raw Normal View History

import React, {
Dispatch,
SetStateAction,
2022-12-19 10:48:59 -08:00
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react'
2022-12-14 18:51:21 -08:00
import {
TransactionInstruction,
PublicKey,
VersionedTransaction,
Connection,
2022-12-15 14:51:19 -08:00
TransactionMessage,
AddressLookupTableAccount,
2022-12-14 18:51:21 -08:00
} from '@solana/web3.js'
import Decimal from 'decimal.js'
2022-09-12 08:53:57 -07:00
import mangoStore from '@store/mangoStore'
import Button, { IconButton } from '../shared/Button'
import Loading from '../shared/Loading'
import {
ArrowLeftIcon,
PencilIcon,
2022-09-06 21:36:35 -07:00
ArrowsRightLeftIcon,
2022-09-16 04:37:24 -07:00
ArrowRightIcon,
2022-12-28 20:17:38 -08:00
ChevronDownIcon,
2022-09-06 21:36:35 -07:00
} from '@heroicons/react/20/solid'
import { useTranslation } from 'next-i18next'
2022-10-28 14:46:38 -07:00
import Image from 'next/legacy/image'
2023-01-24 17:12:13 -08:00
import { formatNumericValue } from '../../utils/numbers'
2022-08-19 21:03:26 -07:00
import { notify } from '../../utils/notifications'
2022-11-18 11:11:06 -08:00
import useJupiterMints from '../../hooks/useJupiterMints'
2022-11-18 20:59:06 -08:00
import { RouteInfo } from 'types/jupiter'
2022-11-18 11:11:06 -08:00
import useJupiterSwapData from './useJupiterSwapData'
2022-12-14 18:51:21 -08:00
// import { Transaction } from '@solana/web3.js'
import { SOUND_SETTINGS_KEY } from 'utils/constants'
2022-11-22 21:38:31 -08:00
import useLocalStorageState from 'hooks/useLocalStorageState'
2022-11-23 04:40:38 -08:00
import { Howl } from 'howler'
2022-11-24 18:39:14 -08:00
import { INITIAL_SOUND_SETTINGS } from '@components/settings/SoundSettings'
2022-12-08 15:42:55 -08:00
import Tooltip from '@components/shared/Tooltip'
2022-12-28 20:17:38 -08:00
import { Disclosure } from '@headlessui/react'
2022-12-29 02:59:56 -08:00
import RoutesModal from './RoutesModal'
2023-01-15 05:15:35 -08:00
import { createAssociatedTokenAccountIdempotentInstruction } from '@blockworks-foundation/mango-v4'
2023-01-24 16:54:24 -08:00
import FormatNumericValue from '@components/shared/FormatNumericValue'
2023-02-27 23:20:11 -08:00
import { isMangoError } from 'types'
type JupiterRouteInfoProps = {
2022-08-17 18:26:38 -07:00
amountIn: Decimal
onClose: () => void
routes: RouteInfo[] | undefined
2023-02-04 13:55:42 -08:00
selectedRoute: RouteInfo | undefined | null
setSelectedRoute: Dispatch<SetStateAction<RouteInfo | undefined | null>>
slippage: number
}
2022-12-15 14:51:19 -08:00
const deserializeJupiterIxAndAlt = async (
connection: Connection,
swapTransaction: string
): Promise<[TransactionInstruction[], AddressLookupTableAccount[]]> => {
const parsedSwapTransaction = VersionedTransaction.deserialize(
Buffer.from(swapTransaction, 'base64')
)
const message = parsedSwapTransaction.message
2022-12-15 15:00:47 -08:00
// const lookups = message.addressTableLookups
2022-12-15 14:51:19 -08:00
const addressLookupTablesResponses = await Promise.all(
message.addressTableLookups.map((alt) =>
connection.getAddressLookupTable(alt.accountKey)
)
)
const addressLookupTables: AddressLookupTableAccount[] =
addressLookupTablesResponses
.map((alt) => alt.value)
.filter((x): x is AddressLookupTableAccount => x !== null)
const decompiledMessage = TransactionMessage.decompile(message, {
addressLookupTableAccounts: addressLookupTables,
})
2022-12-15 15:00:47 -08:00
return [decompiledMessage.instructions, addressLookupTables]
2022-12-15 14:51:19 -08:00
}
2023-01-15 05:15:35 -08:00
const prepareMangoRouterInstructions = async (
selectedRoute: RouteInfo,
inputMint: PublicKey,
outputMint: PublicKey,
userPublicKey: PublicKey
): Promise<[TransactionInstruction[], AddressLookupTableAccount[]]> => {
if (!selectedRoute || !selectedRoute.mints || !selectedRoute.instructions) {
return [[], []]
}
const mintsToFilterOut = [inputMint, outputMint]
const filteredOutMints = [
...selectedRoute.mints.filter(
(routeMint) =>
!mintsToFilterOut.find((filterOutMint) =>
filterOutMint.equals(routeMint)
)
),
]
const additionalInstructions = []
for (const mint of filteredOutMints) {
const ix = await createAssociatedTokenAccountIdempotentInstruction(
userPublicKey,
userPublicKey,
mint
)
additionalInstructions.push(ix)
}
const instructions = [
...additionalInstructions,
...selectedRoute.instructions,
]
return [instructions, []]
}
2022-12-15 14:51:19 -08:00
const fetchJupiterTransaction = async (
2022-12-14 18:51:21 -08:00
connection: Connection,
selectedRoute: RouteInfo,
2022-11-18 20:59:06 -08:00
userPublicKey: PublicKey,
slippage: number,
2022-12-26 09:14:34 -08:00
inputMint: PublicKey,
outputMint: PublicKey
2022-12-15 14:51:19 -08:00
): Promise<[TransactionInstruction[], AddressLookupTableAccount[]]> => {
2022-11-18 11:11:06 -08:00
const transactions = await (
2022-12-14 18:51:21 -08:00
await fetch('https://quote-api.jup.ag/v4/swap', {
2022-11-18 11:11:06 -08:00
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,
// 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.
2022-11-18 20:59:06 -08:00
// feeAccount: 'fee_account_public_key',
slippageBps: Math.ceil(slippage * 100),
2022-11-18 11:11:06 -08:00
}),
})
).json()
2022-11-18 20:59:06 -08:00
const { swapTransaction } = transactions
2022-11-18 20:59:06 -08:00
2022-12-15 14:51:19 -08:00
const [ixs, alts] = await deserializeJupiterIxAndAlt(
connection,
swapTransaction
)
2022-12-14 18:51:21 -08:00
const isSetupIx = (pk: PublicKey): boolean =>
pk.toString() === 'ComputeBudget111111111111111111111111111111' ||
pk.toString() === 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
2022-12-14 18:51:21 -08:00
const isDuplicateAta = (ix: TransactionInstruction): boolean => {
return (
ix.programId.toString() ===
'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL' &&
2022-12-26 09:14:34 -08:00
(ix.keys[3].pubkey.toString() === inputMint.toString() ||
ix.keys[3].pubkey.toString() === outputMint.toString())
)
}
2022-12-26 09:14:34 -08:00
const filtered_jup_ixs = ixs
.filter((ix) => !isSetupIx(ix.programId))
.filter((ix) => !isDuplicateAta(ix))
2022-12-15 14:51:19 -08:00
return [filtered_jup_ixs, alts]
}
const EMPTY_COINGECKO_PRICES = {
inputCoingeckoPrice: 0,
outputCoingeckoPrice: 0,
}
2022-12-07 21:05:36 -08:00
const successSound = new Howl({
src: ['/sounds/swap-success.mp3'],
volume: 0.5,
})
const SwapReviewRouteInfo = ({
amountIn,
onClose,
routes,
selectedRoute,
2022-12-29 02:59:56 -08:00
setSelectedRoute,
}: JupiterRouteInfoProps) => {
2022-08-26 10:17:31 -07:00
const { t } = useTranslation(['common', 'trade'])
2022-12-28 20:17:38 -08:00
const slippage = mangoStore((s) => s.swap.slippage)
2022-12-29 02:59:56 -08:00
const [showRoutesModal, setShowRoutesModal] = useState<boolean>(false)
const [swapRate, setSwapRate] = useState<boolean>(false)
2022-11-19 11:20:36 -08:00
const [feeValue] = useState<number | null>(null)
2022-08-19 21:03:26 -07:00
const [submitting, setSubmitting] = useState(false)
const [coingeckoPrices, setCoingeckoPrices] = useState(EMPTY_COINGECKO_PRICES)
2022-12-15 14:51:19 -08:00
const { jupiterTokens } = useJupiterMints()
2022-11-18 11:11:06 -08:00
const { inputTokenInfo, outputTokenInfo } = useJupiterSwapData()
2022-09-05 15:38:47 -07:00
const inputBank = mangoStore((s) => s.swap.inputBank)
2022-11-22 21:38:31 -08:00
const [soundSettings] = useLocalStorageState(
SOUND_SETTINGS_KEY,
INITIAL_SOUND_SETTINGS
)
const focusRef = useRef<HTMLButtonElement>(null)
const inputTokenIconUri = useMemo(() => {
return inputTokenInfo ? inputTokenInfo.logoURI : ''
}, [inputTokenInfo])
const amountOut = useMemo(() => {
if (!selectedRoute || !outputTokenInfo) return
return new Decimal(selectedRoute.outAmount.toString()).div(
10 ** outputTokenInfo.decimals
)
}, [selectedRoute, outputTokenInfo])
useEffect(() => {
if (focusRef?.current) {
focusRef.current.focus()
}
}, [focusRef])
useEffect(() => {
setCoingeckoPrices(EMPTY_COINGECKO_PRICES)
const fetchTokenPrices = async () => {
const inputId = inputTokenInfo?.extensions?.coingeckoId
const outputId = outputTokenInfo?.extensions?.coingeckoId
if (inputId && outputId) {
try {
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,
})
}
} catch (e) {
console.error('Loading coingecko prices: ', e)
}
}
}
if (inputTokenInfo && outputTokenInfo) {
fetchTokenPrices()
}
}, [inputTokenInfo, outputTokenInfo])
2022-12-19 10:48:59 -08:00
const onSwap = useCallback(async () => {
2022-11-18 11:11:06 -08:00
if (!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
2022-11-21 19:23:54 -08:00
const set = mangoStore.getState().set
2022-12-14 18:51:21 -08:00
const connection = mangoStore.getState().connection
2022-08-19 21:03:26 -07:00
if (!mangoAccount || !group || !inputBank || !outputBank) return
setSubmitting(true)
2023-01-15 05:15:35 -08:00
const [ixs, alts] =
selectedRoute.routerName === 'Mango'
? await prepareMangoRouterInstructions(
selectedRoute,
inputBank.mint,
outputBank.mint,
mangoAccount.owner
)
: await fetchJupiterTransaction(
connection,
selectedRoute,
mangoAccount.owner,
slippage,
inputBank.mint,
outputBank.mint
)
2022-08-19 21:03:26 -07:00
try {
const tx = await client.marginTrade({
group,
mangoAccount,
inputMintPk: inputBank.mint,
amountIn: amountIn.toNumber(),
outputMintPk: outputBank.mint,
userDefinedInstructions: ixs,
2022-12-15 14:51:19 -08:00
userDefinedAlts: alts,
2022-08-19 21:03:26 -07:00
flashLoanType: { swap: {} },
})
2022-11-21 19:23:54 -08:00
set((s) => {
2023-01-02 04:19:30 -08:00
s.successAnimation.swap = true
2022-11-21 19:23:54 -08:00
})
2022-11-23 04:40:38 -08:00
if (soundSettings['swap-success']) {
successSound.play()
2022-11-22 21:38:31 -08:00
}
2022-08-19 21:03:26 -07:00
notify({
title: 'Transaction confirmed',
type: 'success',
txid: tx,
2022-11-23 04:40:38 -08:00
noSound: true,
2022-08-19 21:03:26 -07:00
})
actions.fetchGroup()
2022-12-19 11:42:28 -08:00
actions.fetchSwapHistory(mangoAccount.publicKey.toString(), 30000)
2022-08-25 20:30:39 -07:00
await actions.reloadMangoAccount()
2023-02-27 23:20:11 -08:00
} catch (e) {
console.error('onSwap error: ', e)
2023-02-27 23:20:11 -08:00
if (isMangoError(e)) {
notify({
title: 'Transaction failed',
description: e.message,
txid: e?.txid,
type: 'error',
})
}
2022-08-19 21:03:26 -07:00
} finally {
setSubmitting(false)
}
} catch (e) {
console.error('Swap error:', e)
} finally {
onClose()
}
2022-12-19 10:48:59 -08:00
}, [amountIn, onClose, selectedRoute, soundSettings])
2022-12-08 15:42:55 -08:00
const [balance, borrowAmount] = useMemo(() => {
const mangoAccount = mangoStore.getState().mangoAccount.current
const inputBank = mangoStore.getState().swap.inputBank
2022-12-08 15:42:55 -08:00
if (!mangoAccount || !inputBank) return [0, 0]
2022-12-08 15:42:55 -08:00
const balance = mangoAccount.getTokenDepositsUi(inputBank)
const remainingBalance = balance - amountIn.toNumber()
const borrowAmount = remainingBalance < 0 ? Math.abs(remainingBalance) : 0
2022-12-08 15:42:55 -08:00
return [balance, borrowAmount]
}, [amountIn])
const coinGeckoPriceDifference = useMemo(() => {
return amountOut?.toNumber()
2022-12-16 14:12:07 -08:00
? amountIn
.div(amountOut)
.minus(
new Decimal(coingeckoPrices?.outputCoingeckoPrice).div(
coingeckoPrices?.inputCoingeckoPrice
)
2022-12-16 14:12:07 -08:00
)
.div(amountIn.div(amountOut))
.mul(100)
: new Decimal(0)
}, [coingeckoPrices, amountIn, amountOut])
2023-01-06 02:32:54 -08:00
return routes?.length &&
selectedRoute &&
inputTokenInfo &&
outputTokenInfo &&
amountOut ? (
2022-12-28 20:17:38 -08:00
<div className="thin-scroll flex h-full flex-col justify-between overflow-y-auto">
<div>
<IconButton
className="absolute top-4 left-4 mr-3 text-th-fgd-2"
onClick={onClose}
size="small"
ref={focusRef}
>
<ArrowLeftIcon className="h-5 w-5" />
</IconButton>
<div className="flex justify-center bg-gradient-to-t from-th-bkg-1 to-th-bkg-2 p-6 pb-0">
<div className="mb-4 flex w-full flex-col items-center border-b border-th-bkg-3 pb-4">
<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"
2023-01-06 02:32:54 -08:00
src={outputTokenInfo?.logoURI}
/>
</div>
</div>
2022-09-16 04:37:24 -07:00
<p className="mb-0.5 flex items-center text-center text-lg">
2023-01-24 16:54:24 -08:00
<span className="mr-1 font-mono text-th-fgd-1">
<FormatNumericValue value={amountIn} />
</span>{' '}
2023-01-06 02:32:54 -08:00
{inputTokenInfo?.symbol}
2022-09-16 04:37:24 -07:00
<ArrowRightIcon className="mx-2 h-5 w-5 text-th-fgd-4" />
2023-01-24 16:54:24 -08:00
<span className="mr-1 font-mono text-th-fgd-1">
<FormatNumericValue value={amountOut} />
</span>{' '}
2023-01-06 02:32:54 -08:00
{`${outputTokenInfo?.symbol}`}
2022-09-05 15:38:47 -07:00
</p>
</div>
</div>
2022-12-27 14:24:58 -08:00
<div className="space-y-2 overflow-auto px-6">
<div className="flex justify-between">
2022-12-29 02:59:56 -08:00
<p className="text-sm text-th-fgd-3">{t('price')}</p>
<div>
<div className="flex items-center justify-end">
<p className="text-right font-mono text-sm text-th-fgd-2">
{swapRate ? (
<>
2022-09-16 04:37:24 -07:00
1{' '}
2023-01-14 04:41:30 -08:00
<span className="font-body text-th-fgd-3">
{inputTokenInfo?.symbol} {' '}
2022-09-16 04:37:24 -07:00
</span>
2023-01-24 16:54:24 -08:00
<FormatNumericValue value={amountOut.div(amountIn)} />{' '}
2023-01-14 04:41:30 -08:00
<span className="font-body text-th-fgd-3">
2022-09-16 04:37:24 -07:00
{outputTokenInfo?.symbol}
</span>
</>
) : (
<>
2022-09-16 04:37:24 -07:00
1{' '}
2023-01-14 04:41:30 -08:00
<span className="font-body text-th-fgd-3">
2022-09-16 04:37:24 -07:00
{outputTokenInfo?.symbol} {' '}
</span>
2023-01-24 16:54:24 -08:00
<FormatNumericValue value={amountIn.div(amountOut)} />{' '}
2023-01-14 04:41:30 -08:00
<span className="font-body text-th-fgd-3">
2023-01-06 02:32:54 -08:00
{inputTokenInfo?.symbol}
2022-09-16 04:37:24 -07:00
</span>
</>
)}
</p>
2022-09-06 21:36:35 -07:00
<ArrowsRightLeftIcon
2023-04-19 18:12:45 -07:00
className="ml-1 h-4 w-4 cursor-pointer text-th-fgd-2 hover:text-th-active"
onClick={() => setSwapRate(!swapRate)}
/>
</div>
2022-09-07 19:49:12 -07:00
<div className="space-y-2 px-1 text-xs">
{coingeckoPrices?.outputCoingeckoPrice &&
coingeckoPrices?.inputCoingeckoPrice ? (
<div
className={`text-right font-mono ${
2022-12-16 14:12:07 -08:00
coinGeckoPriceDifference.gt(1)
2022-11-30 19:32:32 -08:00
? 'text-th-down'
: 'text-th-up'
}`}
>
{Decimal.abs(coinGeckoPriceDifference).toFixed(1)}%{' '}
<span className="font-body text-th-fgd-3">{`${
coinGeckoPriceDifference.lte(0)
? 'cheaper'
: 'more expensive'
} than CoinGecko`}</span>
</div>
) : null}
</div>
</div>
</div>
<div className="flex justify-between">
2022-12-29 02:59:56 -08:00
<p className="text-sm text-th-fgd-3">
{t('swap:minimum-received')}
</p>
2023-01-14 04:41:30 -08:00
{outputTokenInfo?.decimals && selectedRoute ? (
2022-12-29 02:59:56 -08:00
<p className="text-right font-mono text-sm text-th-fgd-2">
2023-01-24 17:12:13 -08:00
{selectedRoute.swapMode === 'ExactIn' ? (
<FormatNumericValue
value={
2023-01-14 04:41:30 -08:00
selectedRoute.otherAmountThreshold /
2023-01-24 17:12:13 -08:00
10 ** outputTokenInfo.decimals
}
decimals={outputTokenInfo.decimals}
/>
) : (
<FormatNumericValue
value={
selectedRoute.outAmount / 10 ** outputTokenInfo.decimals
}
decimals={outputTokenInfo.decimals}
/>
)}{' '}
2023-01-14 04:41:30 -08:00
<span className="font-body text-th-fgd-3">
2022-12-29 02:59:56 -08:00
{outputTokenInfo?.symbol}
</span>
</p>
) : null}
</div>
2023-01-14 04:41:30 -08:00
{selectedRoute?.swapMode === 'ExactOut' ? (
<div className="flex justify-between">
<p className="text-sm text-th-fgd-3">{t('swap:maximum-cost')}</p>
{inputTokenInfo?.decimals && selectedRoute ? (
<p className="text-right font-mono text-sm text-th-fgd-2">
2023-01-24 17:12:13 -08:00
<FormatNumericValue
value={
selectedRoute.otherAmountThreshold /
10 ** inputTokenInfo.decimals
}
decimals={inputTokenInfo.decimals}
/>{' '}
2023-01-14 04:41:30 -08:00
<span className="font-body text-th-fgd-3">
{inputTokenInfo?.symbol}
</span>
</p>
) : null}
</div>
) : null}
2022-12-29 02:59:56 -08:00
<div className="flex justify-between">
2023-03-17 04:46:57 -07:00
<Tooltip
content={
<>
<p>
The price impact is the difference observed between the
total value of the entry tokens swapped and the destination
tokens obtained.
</p>
<p className="mt-1">
The bigger the trade is, the bigger the price impact can be.
</p>
</>
}
>
<span className="tooltip-underline">
{t('swap:price-impact')}
</span>
</Tooltip>
2022-12-29 02:59:56 -08:00
<p className="text-right font-mono text-sm text-th-fgd-2">
{selectedRoute?.priceImpactPct * 100 < 0.1
? '<0.1%'
: `${(selectedRoute?.priceImpactPct * 100).toFixed(2)}%`}
</p>
</div>
{borrowAmount ? (
2022-12-29 02:59:56 -08:00
<div className="flex justify-between">
<Tooltip
content={
balance
? t('swap:tooltip-borrow-balance', {
2023-01-24 16:54:24 -08:00
balance: formatNumericValue(balance),
borrowAmount: formatNumericValue(borrowAmount),
2022-12-29 02:59:56 -08:00
token: inputTokenInfo?.symbol,
2023-01-24 17:12:13 -08:00
rate: formatNumericValue(
inputBank!.getBorrowRateUi(),
2
),
2022-12-29 02:59:56 -08:00
})
: t('swap:tooltip-borrow-no-balance', {
2023-01-24 16:54:24 -08:00
borrowAmount: formatNumericValue(borrowAmount),
2022-12-29 02:59:56 -08:00
token: inputTokenInfo?.symbol,
2023-01-24 17:12:13 -08:00
rate: formatNumericValue(
inputBank!.getBorrowRateUi(),
2
),
2022-12-29 02:59:56 -08:00
})
}
2023-03-10 10:01:47 -08:00
delay={100}
2022-12-29 02:59:56 -08:00
>
<p className="tooltip-underline text-sm text-th-fgd-3">
{t('borrow-amount')}
2022-12-08 15:42:55 -08:00
</p>
2022-12-29 02:59:56 -08:00
</Tooltip>
<p className="text-right font-mono text-sm text-th-fgd-2">
2023-01-24 16:54:24 -08:00
~<FormatNumericValue value={borrowAmount} />{' '}
2023-01-14 04:41:30 -08:00
<span className="font-body">{inputTokenInfo?.symbol}</span>
2022-12-29 02:59:56 -08:00
</p>
</div>
) : null}
2022-12-28 20:17:38 -08:00
</div>
</div>
<div className="p-6">
2022-12-29 02:59:56 -08:00
<div className="mb-4 flex items-center justify-center">
<Button
onClick={onSwap}
className="flex w-full items-center justify-center text-base"
size="large"
>
{submitting ? (
<Loading className="mr-2 h-5 w-5" />
) : (
<div className="flex items-center">
<ArrowsRightLeftIcon className="mr-2 h-5 w-5" />
{t('swap')}
</div>
)}
</Button>
</div>
2022-12-28 20:17:38 -08:00
<div className="rounded-md bg-th-bkg-2">
<Disclosure>
{({ open }) => (
<>
2023-03-26 20:11:10 -07:00
<Disclosure.Button
2023-04-19 18:12:45 -07:00
className={`flex w-full items-center justify-between rounded-md p-3 focus-visible:bg-th-bkg-3 ${
2023-03-26 20:11:10 -07:00
open ? 'mb-2 rounded-b-none' : ''
}`}
>
2022-12-28 20:17:38 -08:00
<p>{open ? t('swap:hide-fees') : t('swap:show-fees')}</p>
<ChevronDownIcon
className={`${
open ? 'rotate-180' : 'rotate-360'
} h-5 w-5 text-th-fgd-3`}
/>
</Disclosure.Button>
<Disclosure.Panel className="space-y-2 p-3 pt-0">
{borrowAmount ? (
<div className="flex justify-between">
2022-12-29 02:59:56 -08:00
<Tooltip
2023-03-17 05:48:38 -07:00
content={t('loan-origination-fee-tooltip', {
fee: `${(
inputBank!.loanOriginationFeeRate.toNumber() * 100
).toFixed(3)}%`,
})}
2023-03-10 10:01:47 -08:00
delay={100}
2022-12-29 02:59:56 -08:00
>
2023-03-17 05:48:38 -07:00
<p className="tooltip-underline">
2022-12-28 20:17:38 -08:00
{t('loan-origination-fee')}
</p>
</Tooltip>
2023-03-17 05:48:38 -07:00
<p className="text-right font-mono text-th-fgd-2">
2022-12-28 20:17:38 -08:00
~
2023-01-24 16:54:24 -08:00
<FormatNumericValue
2023-03-17 04:46:57 -07:00
value={
borrowAmount *
2023-01-24 16:54:24 -08:00
inputBank!.loanOriginationFeeRate.toNumber()
2023-03-17 04:46:57 -07:00
}
2023-03-17 05:48:38 -07:00
decimals={inputBank!.mintDecimals}
2023-01-24 16:54:24 -08:00
/>{' '}
2023-03-17 05:48:38 -07:00
<span className="font-body text-th-fgd-4">
{inputBank!.name}
</span>
2022-12-28 20:17:38 -08:00
</p>
</div>
) : null}
2023-03-08 12:40:33 -08:00
<div className="flex items-center justify-between">
<p className="text-sm text-th-fgd-3">
{t('swap:swap-route')}
</p>
<div
className="flex items-center text-th-fgd-2 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?.label} ${
includeSeparator ? 'x ' : ''
}`}</span>
)
})}
</span>
<PencilIcon className="ml-2 h-4 w-4 hover:text-th-active" />
</div>
</div>
2022-12-28 20:17:38 -08:00
{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 font-mono text-sm text-th-fgd-2">
${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('swap:fees-paid-to', {
route: info?.label,
})}
</p>
{feeToken?.decimals && (
<p className="pl-4 text-right font-mono text-sm text-th-fgd-2">
{(
info.lpFee?.amount /
Math.pow(10, feeToken.decimals)
).toFixed(6)}{' '}
2023-01-14 04:41:30 -08:00
<span className="font-body">
2022-12-28 20:17:38 -08:00
{feeToken?.symbol}
</span>{' '}
(
{(info.lpFee?.pct * 100).toLocaleString(
undefined,
{
2023-03-08 12:40:33 -08:00
maximumSignificantDigits: 2,
2022-12-28 20:17:38 -08:00
}
)}
%)
</p>
)}
</div>
)
})
)}
2022-12-28 20:17:38 -08:00
</Disclosure.Panel>
</>
)}
</Disclosure>
</div>
</div>
2022-12-29 02:59:56 -08:00
{showRoutesModal ? (
<RoutesModal
show={showRoutesModal}
onClose={() => setShowRoutesModal(false)}
setSelectedRoute={setSelectedRoute}
selectedRoute={selectedRoute}
routes={routes}
2023-01-06 02:32:54 -08:00
inputTokenSymbol={inputTokenInfo?.name}
2022-12-29 02:59:56 -08:00
outputTokenInfo={outputTokenInfo}
/>
) : null}
</div>
) : null
}
export default React.memo(SwapReviewRouteInfo)