2022-08-17 13:06:25 -07:00
import React , {
Dispatch ,
SetStateAction ,
2022-12-19 10:48:59 -08:00
useCallback ,
2022-08-17 13:06:25 -07:00
useEffect ,
useMemo ,
2023-03-24 05:22:29 -07:00
useRef ,
2022-08-17 13:06:25 -07:00
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'
2022-08-17 13:06:25 -07:00
import Decimal from 'decimal.js'
2022-09-12 08:53:57 -07:00
import mangoStore from '@store/mangoStore'
2022-08-17 13:06:25 -07:00
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'
2022-08-17 13:06:25 -07:00
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'
2022-12-16 09:30:39 -08:00
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'
2022-08-17 13:06:25 -07:00
type JupiterRouteInfoProps = {
2022-08-17 18:26:38 -07:00
amountIn : Decimal
2022-08-18 13:50:34 -07:00
onClose : ( ) = > void
routes : RouteInfo [ ] | undefined
2023-02-04 13:55:42 -08:00
selectedRoute : RouteInfo | undefined | null
setSelectedRoute : Dispatch < SetStateAction < RouteInfo | undefined | null > >
2022-08-18 13:50:34 -07:00
slippage : number
2022-08-17 13:06:25 -07:00
}
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 ,
2022-08-17 13:06:25 -07:00
selectedRoute : RouteInfo ,
2022-11-18 20:59:06 -08:00
userPublicKey : PublicKey ,
2022-12-16 14:49:03 -08:00
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
2022-08-17 13:06:25 -07: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
2022-12-16 09:30:39 -08:00
const isSetupIx = ( pk : PublicKey ) : boolean = >
pk . toString ( ) === 'ComputeBudget111111111111111111111111111111' ||
pk . toString ( ) === 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
2022-12-14 18:51:21 -08:00
2022-12-16 14:49:03 -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-16 14:49:03 -08:00
)
}
2022-12-26 09:14:34 -08:00
const filtered_jup_ixs = ixs
. filter ( ( ix ) = > ! isSetupIx ( ix . programId ) )
. filter ( ( ix ) = > ! isDuplicateAta ( ix ) )
2022-12-16 14:49:03 -08:00
2022-12-15 14:51:19 -08:00
return [ filtered_jup_ixs , alts ]
2022-08-17 13:06:25 -07:00
}
2022-08-25 13:24:37 -07:00
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 ,
} )
2022-12-07 13:33:35 -08:00
const SwapReviewRouteInfo = ( {
2022-08-17 13:06:25 -07:00
amountIn ,
onClose ,
routes ,
selectedRoute ,
2022-12-29 02:59:56 -08:00
setSelectedRoute ,
2022-08-17 13:06:25 -07:00
} : 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 )
2022-08-17 13:06:25 -07:00
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 )
2022-08-25 13:24:37 -07:00
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
)
2023-03-24 05:22:29 -07:00
const focusRef = useRef < HTMLButtonElement > ( null )
2022-08-17 13:06:25 -07:00
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
2022-09-02 16:52:07 -07:00
return new Decimal ( selectedRoute . outAmount . toString ( ) ) . div (
10 * * outputTokenInfo . decimals
2022-08-17 13:06:25 -07:00
)
} , [ selectedRoute , outputTokenInfo ] )
2023-03-24 05:22:29 -07:00
useEffect ( ( ) = > {
if ( focusRef ? . current ) {
focusRef . current . focus ( )
}
} , [ focusRef ] )
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 ) {
2023-01-19 20:15:04 -08:00
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 )
2022-08-25 13:24:37 -07:00
}
}
}
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
2023-01-02 13:04:58 -08:00
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
} )
2022-09-02 16:51:35 -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 ) {
2022-08-20 17:09:36 -07:00
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-08-17 13:06:25 -07:00
2022-12-08 15:42:55 -08:00
const [ balance , borrowAmount ] = useMemo ( ( ) = > {
2022-08-25 13:24:37 -07:00
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-09 10:16:48 -08:00
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-09 10:16:48 -08:00
2022-12-08 15:42:55 -08:00
return [ balance , borrowAmount ]
2022-08-25 13:24:37 -07:00
} , [ amountIn ] )
const coinGeckoPriceDifference = useMemo ( ( ) = > {
2022-12-09 10:16:48 -08:00
return amountOut ? . toNumber ( )
2022-12-16 14:12:07 -08:00
? amountIn
. div ( amountOut )
. minus (
new Decimal ( coingeckoPrices ? . outputCoingeckoPrice ) . div (
coingeckoPrices ? . inputCoingeckoPrice
2022-09-02 11:36:57 -07:00
)
2022-12-16 14:12:07 -08:00
)
. div ( amountIn . div ( amountOut ) )
. mul ( 100 )
2022-09-02 11:36:57 -07:00
: new Decimal ( 0 )
2022-08-25 13:24:37 -07:00
} , [ 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" >
2022-08-17 13:06:25 -07:00
< div >
< IconButton
2022-12-19 03:20:01 -08:00
className = "absolute top-4 left-4 mr-3 text-th-fgd-2"
2022-08-17 13:06:25 -07:00
onClick = { onClose }
size = "small"
2023-03-24 05:22:29 -07:00
ref = { focusRef }
2022-08-17 13:06:25 -07:00
>
< ArrowLeftIcon className = "h-5 w-5" / >
< / IconButton >
2022-12-19 03:20:01 -08:00
< 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" >
2022-08-17 13:06:25 -07:00
< 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 }
2022-08-17 13:06:25 -07:00
/ >
< / 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 >
2022-08-17 13:06:25 -07:00
< / div >
< / div >
2022-12-27 14:24:58 -08:00
< div className = "space-y-2 overflow-auto px-6" >
2022-08-25 13:24:37 -07:00
< div className = "flex justify-between" >
2022-12-29 02:59:56 -08:00
< p className = "text-sm text-th-fgd-3" > { t ( 'price' ) } < / p >
2022-08-25 13:24:37 -07:00
< div >
< div className = "flex items-center justify-end" >
2022-12-19 03:20:01 -08:00
< p className = "text-right font-mono text-sm text-th-fgd-2" >
2022-08-25 13:24:37 -07:00
{ 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-08-25 13:24:37 -07:00
< / >
) : (
< >
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 >
2022-08-25 13:24:37 -07:00
< / >
) }
< / p >
2022-09-06 21:36:35 -07:00
< ArrowsRightLeftIcon
2022-12-19 03:20:01 -08:00
className = "default-transition ml-1 h-4 w-4 cursor-pointer text-th-fgd-2 hover:text-th-active"
2022-08-25 13:24:37 -07:00
onClick = { ( ) = > setSwapRate ( ! swapRate ) }
/ >
< / div >
2022-09-07 19:49:12 -07:00
< div className = "space-y-2 px-1 text-xs" >
2022-08-25 13:24:37 -07:00
{ coingeckoPrices ? . outputCoingeckoPrice &&
coingeckoPrices ? . inputCoingeckoPrice ? (
< div
2022-09-22 03:46:51 -07:00
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'
2022-08-25 13:24:37 -07:00
} ` }
>
2022-09-02 11:36:57 -07:00
{ Decimal . abs ( coinGeckoPriceDifference ) . toFixed ( 1 ) } % { ' ' }
2023-01-05 18:03:58 -08:00
< span className = "font-body text-th-fgd-3" > { ` ${
2022-09-02 11:36:57 -07:00
coinGeckoPriceDifference . lte ( 0 )
2022-08-25 13:24:37 -07:00
? 'cheaper'
: 'more expensive'
} than CoinGecko ` }</span>
< / div >
) : null }
< / div >
< / div >
< / div >
2022-08-17 13:06:25 -07:00
< 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 >
2022-08-17 13:06:25 -07:00
< / div >
2022-08-25 13:24:37 -07:00
{ 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 >
2022-08-25 13:24:37 -07:00
) : 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
className = { ` default-transition flex w-full items-center justify-between rounded-md p-3 focus:bg-th-bkg-3 ${
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-08-17 13:06:25 -07:00
) }
2022-12-28 20:17:38 -08:00
< / Disclosure.Panel >
< / >
) }
< / Disclosure >
< / div >
2022-08-17 13:06:25 -07:00
< / 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 }
2022-08-17 13:06:25 -07:00
< / div >
) : null
}
2022-12-07 13:33:35 -08:00
export default React . memo ( SwapReviewRouteInfo )