various fixes

This commit is contained in:
saml33 2023-08-04 12:06:09 +10:00
parent a2f2c0fe03
commit 78da92f453
10 changed files with 269 additions and 270 deletions

View File

@ -23,7 +23,11 @@ const TokenVaultWarnings = ({
const [maxWithdraw, maxBorrow] = useMemo(() => {
if (!mangoAccount || !group) return [0, 0]
const maxWithdraw = getMaxWithdrawForBank(group, bank, mangoAccount)
const maxWithdraw = getMaxWithdrawForBank(
group,
bank,
mangoAccount,
).toNumber()
const maxBorrow = mangoAccount.getMaxWithdrawWithBorrowForTokenUi(
group,
bank.mint,

View File

@ -5,6 +5,7 @@ import {
useMemo,
Dispatch,
SetStateAction,
useLayoutEffect,
} from 'react'
import { ArrowDownIcon, ArrowsRightLeftIcon } from '@heroicons/react/20/solid'
import NumberFormat, {
@ -34,6 +35,7 @@ import TokenLogo from '@components/shared/TokenLogo'
import InlineNotification from '@components/shared/InlineNotification'
import { handleFlipPrices } from './SwapTokenChart'
import Select from '@components/forms/Select'
import useIpAddress from 'hooks/useIpAddress'
type LimitSwapFormProps = {
showTokenSelect: 'input' | 'output' | undefined
@ -65,10 +67,11 @@ const LimitSwapForm = ({
setShowTokenSelect,
}: LimitSwapFormProps) => {
const { t } = useTranslation(['common', 'swap', 'trade'])
const { ipAllowed, ipCountry } = useIpAddress()
const [animateSwitchArrow, setAnimateSwitchArrow] = useState(0)
const [triggerPrice, setTriggerPrice] = useState('')
const [orderType, setOrderType] = useState(ORDER_TYPES[0])
const [orderTypeMultiplier, setOrderTypeMultiplier] = useState(0.9)
const [orderTypeMultiplier, setOrderTypeMultiplier] = useState(1.1)
const [submitting, setSubmitting] = useState(false)
const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
const [formErrors, setFormErrors] = useState<FormErrors>({})
@ -140,10 +143,18 @@ const LimitSwapForm = ({
useEffect(() => {
if (!quotePrice) return
if (!triggerPrice && !showTokenSelect) {
setTriggerPrice((quotePrice * 0.9).toFixed(inputBank?.mintDecimals))
setTriggerPrice((quotePrice * 1.1).toFixed(inputBank?.mintDecimals))
}
}, [inputBank, quotePrice, showTokenSelect, triggerPrice])
// flip trigger price when chart direction is flipped
useLayoutEffect(() => {
if (!quotePrice) return
setTriggerPrice(
(quotePrice * orderTypeMultiplier).toFixed(inputBank?.mintDecimals),
)
}, [flipPrices, orderTypeMultiplier])
const triggerPriceDifference = useMemo(() => {
if (!quotePrice) return 0
const triggerDifference = triggerPrice
@ -173,9 +184,7 @@ const LimitSwapForm = ({
return invalidFields
}, [])
/*
If the use margin setting is toggled, clear the form values
*/
// If the use margin setting is toggled, clear the form values
useEffect(() => {
setAmountInFormValue('')
setAmountOutFormValue('')
@ -400,22 +409,38 @@ const LimitSwapForm = ({
!triggerPrice
)
return
const quoteString = !flipPrices
? `${inputBank.name} per ${outputBank.name}`
: `${outputBank.name} per ${inputBank.name}`
if (inputBank.name === 'USDC') {
const orderTypeString =
orderType === OrderTypes.STOP_LOSS
? t('trade:falls-to')
: t('trade:rises-to')
if (orderType === OrderTypes.REPAY_BORROW) {
return t('trade:repay-borrow-order-desc', {
amount: floorToDecimal(amountOutFormValue, outputBank.mintDecimals),
priceUnit: quoteString,
symbol: outputBank.name,
triggerPrice: floorToDecimal(triggerPrice, inputBank.mintDecimals),
})
} else if (inputBank.name === 'USDC') {
return t('trade:trigger-order-desc', {
amount: floorToDecimal(amountOutFormValue, outputBank.mintDecimals),
symbol: outputBank.name,
triggerPrice: triggerPrice,
orderType: orderTypeString,
priceUnit: quoteString,
symbol: outputBank.name,
triggerPrice: floorToDecimal(triggerPrice, inputBank.mintDecimals),
})
} else {
return t('trade:trigger-order-desc', {
amount: floorToDecimal(amountInFormValue, inputBank.mintDecimals),
symbol: inputBank.name,
triggerPrice: triggerPrice,
orderType: orderTypeString,
priceUnit: quoteString,
symbol: inputBank.name,
triggerPrice: floorToDecimal(triggerPrice, inputBank.mintDecimals),
})
}
}, [
@ -423,6 +448,7 @@ const LimitSwapForm = ({
amountOutFormValue,
flipPrices,
inputBank,
orderType,
outputBank,
triggerPrice,
])
@ -489,13 +515,20 @@ const LimitSwapForm = ({
setOrderType(newType)
const triggerMultiplier =
newType === OrderTypes.STOP_LOSS
? 0.9
: newType === OrderTypes.TAKE_PROFIT
? 1.1
: newType === OrderTypes.TAKE_PROFIT
? 0.9
: 1
setOrderTypeMultiplier(triggerMultiplier)
const trigger = (quotePrice * triggerMultiplier).toString()
setTriggerPrice(trigger)
if (amountInAsDecimal.gt(0)) {
const amountOut = getAmountOut(
amountInAsDecimal.toString(),
trigger,
).toString()
setAmountOutFormValue(amountOut)
}
},
[quotePrice, setOrderTypeMultiplier],
)
@ -619,7 +652,7 @@ const LimitSwapForm = ({
/>
{swapFormSizeUi === 'slider' ? (
<SwapSlider
useMargin={useMargin}
useMargin={false}
amount={amountInAsDecimal.toNumber()}
onChange={(v) => handleAmountInUi(v)}
step={1 / 10 ** (inputBank?.mintDecimals || 6)}
@ -628,7 +661,7 @@ const LimitSwapForm = ({
<PercentageSelectButtons
amountIn={amountInAsDecimal.toString()}
setAmountIn={(v) => handleAmountInUi(v)}
useMargin={useMargin}
useMargin={false}
/>
)}
{orderDescription ? (
@ -636,11 +669,13 @@ const LimitSwapForm = ({
<InlineNotification
desc={
<>
{inputBank?.name === 'USDC' ? (
<span className="text-th-up">{t('buy')}</span>
) : (
<span className="text-th-down">{t('sell')}</span>
)}{' '}
{orderType !== OrderTypes.REPAY_BORROW ? (
inputBank?.name === 'USDC' ? (
<span className="text-th-up">{t('buy')}</span>
) : (
<span className="text-th-down">{t('sell')}</span>
)
) : null}{' '}
{orderDescription}
</>
}
@ -648,19 +683,31 @@ const LimitSwapForm = ({
/>
</div>
) : null}
{orderType === 'repay-borrow' && !hasBorrowToRepay ? (
{orderType === OrderTypes.REPAY_BORROW && !hasBorrowToRepay ? (
<div className="mt-3">
<InlineNotification desc={t('swap:no-borrow')} type="error" />
</div>
) : null}
<Button
onClick={handlePlaceStopLoss}
disabled={disablePlaceOrder}
className="mt-6 mb-4 flex w-full items-center justify-center text-base"
size="large"
>
{submitting ? <Loading /> : t('swap:place-limit-order')}
</Button>
{ipAllowed ? (
<Button
onClick={handlePlaceStopLoss}
disabled={disablePlaceOrder}
className="mt-6 mb-4 flex w-full items-center justify-center text-base"
size="large"
>
{submitting ? <Loading /> : t('swap:place-limit-order')}
</Button>
) : (
<Button
disabled
className="mt-6 mb-4 w-full leading-tight"
size="large"
>
{t('country-not-allowed', {
country: ipCountry ? `(${ipCountry})` : '',
})}
</Button>
)}
</>
)
}

View File

@ -6,12 +6,16 @@ import {
Dispatch,
SetStateAction,
} from 'react'
import { ArrowDownIcon } from '@heroicons/react/20/solid'
import {
ArrowDownIcon,
ExclamationCircleIcon,
LinkIcon,
} from '@heroicons/react/20/solid'
import { NumberFormatValues, SourceInfo } from 'react-number-format'
import Decimal from 'decimal.js'
import mangoStore from '@store/mangoStore'
import useDebounce from '../shared/useDebounce'
import { SIZE_INPUT_UI_KEY } from '../../utils/constants'
import { MANGO_MINT, SIZE_INPUT_UI_KEY, USDC_MINT } from '../../utils/constants'
import { useWallet } from '@solana/wallet-adapter-react'
import { RouteInfo } from 'types/jupiter'
import useLocalStorageState from 'hooks/useLocalStorageState'
@ -19,11 +23,17 @@ import SwapSlider from './SwapSlider'
import PercentageSelectButtons from './PercentageSelectButtons'
import BuyTokenInput from './BuyTokenInput'
import SellTokenInput from './SellTokenInput'
import Button from '@components/shared/Button'
import { Transition } from '@headlessui/react'
import SwapReviewRouteInfo from './SwapReviewRouteInfo'
import useIpAddress from 'hooks/useIpAddress'
import { useTranslation } from 'react-i18next'
import useQuoteRoutes from './useQuoteRoutes'
import { useTokenMax } from './useTokenMax'
import Loading from '@components/shared/Loading'
import InlineNotification from '@components/shared/InlineNotification'
type MarketSwapFormProps = {
bestRoute: RouteInfo | undefined | null
selectedRoute: RouteInfo | undefined | null
setSelectedRoute: Dispatch<SetStateAction<RouteInfo | undefined | null>>
setShowTokenSelect: Dispatch<SetStateAction<'input' | 'output' | undefined>>
}
@ -39,17 +49,16 @@ export const NUMBER_FORMAT_CLASSNAMES =
const set = mangoStore.getState().set
const MarketSwapForm = ({
bestRoute,
selectedRoute,
setSelectedRoute,
setShowTokenSelect,
}: MarketSwapFormProps) => {
const MarketSwapForm = ({ setShowTokenSelect }: MarketSwapFormProps) => {
const { t } = useTranslation(['common', 'swap', 'trade'])
//initial state is undefined null is returned on error
const [selectedRoute, setSelectedRoute] = useState<RouteInfo | null>()
const [animateSwitchArrow, setAnimateSwitchArrow] = useState(0)
const [showConfirm, setShowConfirm] = useState(false)
const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
const {
margin: useMargin,
slippage,
inputBank,
outputBank,
amountIn: amountInFormValue,
@ -58,7 +67,16 @@ const MarketSwapForm = ({
} = mangoStore((s) => s.swap)
const [debouncedAmountIn] = useDebounce(amountInFormValue, 300)
const [debouncedAmountOut] = useDebounce(amountOutFormValue, 300)
const { connected } = useWallet()
const { connected, publicKey } = useWallet()
const { bestRoute, routes } = useQuoteRoutes({
inputMint: inputBank?.mint.toString() || USDC_MINT,
outputMint: outputBank?.mint.toString() || MANGO_MINT,
amount: swapMode === 'ExactIn' ? debouncedAmountIn : debouncedAmountOut,
slippage,
swapMode,
wallet: publicKey?.toBase58(),
})
const { ipAllowed, ipCountry } = useIpAddress()
const amountInAsDecimal: Decimal | null = useMemo(() => {
return Number(debouncedAmountIn)
@ -197,6 +215,27 @@ const MarketSwapForm = ({
return (
<>
<div>
<Transition
className="absolute top-0 right-0 z-10 h-full w-full bg-th-bkg-1 pb-0"
show={showConfirm}
enter="transition ease-in duration-300"
enterFrom="-translate-x-full"
enterTo="translate-x-0"
leave="transition ease-out duration-300"
leaveFrom="translate-x-0"
leaveTo="-translate-x-full"
>
<SwapReviewRouteInfo
onClose={() => setShowConfirm(false)}
amountIn={amountInAsDecimal}
slippage={slippage}
routes={routes}
selectedRoute={selectedRoute}
setSelectedRoute={setSelectedRoute}
/>
</Transition>
</div>
<SellTokenInput
handleAmountInChange={handleAmountInChange}
setShowTokenSelect={setShowTokenSelect}
@ -237,8 +276,100 @@ const MarketSwapForm = ({
useMargin={useMargin}
/>
)}
{ipAllowed ? (
<SwapFormSubmitButton
loadingSwapDetails={loadingSwapDetails}
useMargin={useMargin}
selectedRoute={selectedRoute}
setShowConfirm={setShowConfirm}
amountIn={amountInAsDecimal}
inputSymbol={inputBank?.name}
amountOut={selectedRoute ? amountOutAsDecimal.toNumber() : undefined}
/>
) : (
<Button
disabled
className="mt-6 mb-4 w-full leading-tight"
size="large"
>
{t('country-not-allowed', {
country: ipCountry ? `(${ipCountry})` : '',
})}
</Button>
)}
</>
)
}
export default MarketSwapForm
const SwapFormSubmitButton = ({
amountIn,
amountOut,
inputSymbol,
loadingSwapDetails,
selectedRoute,
setShowConfirm,
useMargin,
}: {
amountIn: Decimal
amountOut: number | undefined
inputSymbol: string | undefined
loadingSwapDetails: boolean
selectedRoute: RouteInfo | undefined | null
setShowConfirm: (x: boolean) => void
useMargin: boolean
}) => {
const { t } = useTranslation('common')
const { connected, connect } = useWallet()
const { amount: tokenMax, amountWithBorrow } = useTokenMax(useMargin)
const showInsufficientBalance = useMargin
? amountWithBorrow.lt(amountIn)
: tokenMax.lt(amountIn)
const disabled =
connected &&
(!amountIn.toNumber() ||
showInsufficientBalance ||
!amountOut ||
!selectedRoute)
const onClick = connected ? () => setShowConfirm(true) : connect
return (
<>
<Button
onClick={onClick}
className="mt-6 mb-4 flex w-full items-center justify-center text-base"
disabled={disabled}
size="large"
>
{connected ? (
showInsufficientBalance ? (
<div className="flex items-center">
<ExclamationCircleIcon className="mr-2 h-5 w-5 flex-shrink-0" />
{t('swap:insufficient-balance', {
symbol: inputSymbol,
})}
</div>
) : loadingSwapDetails ? (
<Loading />
) : (
<span>{t('swap:review-swap')}</span>
)
) : (
<div className="flex items-center">
<LinkIcon className="mr-2 h-5 w-5" />
{t('connect')}
</div>
)}
</Button>
{selectedRoute === null && amountIn.gt(0) ? (
<div className="mb-4">
<InlineNotification type="error" desc={t('swap:no-swap-found')} />
</div>
) : null}
</>
)
}

View File

@ -1,32 +1,16 @@
import { useState, useCallback, useMemo, useEffect } from 'react'
import { PublicKey } from '@solana/web3.js'
import {
Cog8ToothIcon,
ExclamationCircleIcon,
LinkIcon,
} from '@heroicons/react/20/solid'
import Decimal from 'decimal.js'
import { Cog8ToothIcon } from '@heroicons/react/20/solid'
import mangoStore from '@store/mangoStore'
import ContentBox from '../shared/ContentBox'
import SwapReviewRouteInfo from './SwapReviewRouteInfo'
import useDebounce from '../shared/useDebounce'
import { useTranslation } from 'next-i18next'
import SwapFormTokenList from './SwapFormTokenList'
import { Transition } from '@headlessui/react'
import Button, { IconButton, LinkButton } from '../shared/Button'
import Loading from '../shared/Loading'
import { IconButton, LinkButton } from '../shared/Button'
import { EnterBottomExitBottom } from '../shared/Transitions'
import useQuoteRoutes from './useQuoteRoutes'
import { HealthType } from '@blockworks-foundation/mango-v4'
import { MANGO_MINT, SWAP_MARGIN_KEY, USDC_MINT } from '../../utils/constants'
import { useTokenMax } from './useTokenMax'
import { SWAP_MARGIN_KEY } from '../../utils/constants'
import HealthImpact from '@components/shared/HealthImpact'
import { useWallet } from '@solana/wallet-adapter-react'
import useMangoAccount from 'hooks/useMangoAccount'
import { RouteInfo } from 'types/jupiter'
import useMangoGroup from 'hooks/useMangoGroup'
import TokenVaultWarnings from '@components/shared/TokenVaultWarnings'
import useIpAddress from 'hooks/useIpAddress'
import SwapSettings from './SwapSettings'
import InlineNotification from '@components/shared/InlineNotification'
import Tooltip from '@components/shared/Tooltip'
@ -40,18 +24,13 @@ const set = mangoStore.getState().set
const SwapForm = () => {
const { t } = useTranslation(['common', 'swap', 'trade'])
//initial state is undefined null is returned on error
const [selectedRoute, setSelectedRoute] = useState<RouteInfo | null>()
const [showTokenSelect, setShowTokenSelect] = useState<'input' | 'output'>()
const [showSettings, setShowSettings] = useState(false)
const [showConfirm, setShowConfirm] = useState(false)
const [swapOrLimit, setSwapOrLimit] = useState('swap')
const { group } = useMangoGroup()
const [, setSavedSwapMargin] = useLocalStorageState<boolean>(
SWAP_MARGIN_KEY,
true,
)
const { ipAllowed, ipCountry } = useIpAddress()
const {
margin: useMargin,
@ -60,33 +39,7 @@ const SwapForm = () => {
outputBank,
amountIn: amountInFormValue,
amountOut: amountOutFormValue,
swapMode,
} = mangoStore((s) => s.swap)
const [debouncedAmountIn] = useDebounce(amountInFormValue, 300)
const [debouncedAmountOut] = useDebounce(amountOutFormValue, 300)
const { mangoAccount } = useMangoAccount()
const { connected, publicKey } = useWallet()
const amountInAsDecimal: Decimal | null = useMemo(() => {
return Number(debouncedAmountIn)
? new Decimal(debouncedAmountIn)
: new Decimal(0)
}, [debouncedAmountIn])
const amountOutAsDecimal: Decimal | null = useMemo(() => {
return Number(debouncedAmountOut)
? new Decimal(debouncedAmountOut)
: new Decimal(0)
}, [debouncedAmountOut])
const { bestRoute, routes } = useQuoteRoutes({
inputMint: inputBank?.mint.toString() || USDC_MINT,
outputMint: outputBank?.mint.toString() || MANGO_MINT,
amount: swapMode === 'ExactIn' ? debouncedAmountIn : debouncedAmountOut,
slippage,
swapMode,
wallet: publicKey?.toBase58(),
})
const handleTokenInSelect = useCallback((mintAddress: string) => {
const group = mangoStore.getState().group
@ -114,14 +67,15 @@ const SwapForm = () => {
const maintProjectedHealth = useMemo(() => {
const group = mangoStore.getState().group
const mangoAccount = mangoStore.getState().mangoAccount.current
if (
!inputBank ||
!mangoAccount ||
!outputBank ||
!amountOutAsDecimal ||
!amountOutFormValue ||
!group
)
return 0
return 100
const simulatedHealthRatio =
mangoAccount.simHealthRatioWithTokenPositionUiChanges(
@ -129,11 +83,11 @@ const SwapForm = () => {
[
{
mintPk: inputBank.mint,
uiTokenAmount: amountInAsDecimal.toNumber() * -1,
uiTokenAmount: parseFloat(amountInFormValue) * -1,
},
{
mintPk: outputBank.mint,
uiTokenAmount: amountOutAsDecimal.toNumber(),
uiTokenAmount: parseFloat(amountOutFormValue),
},
],
HealthType.maint,
@ -143,28 +97,7 @@ const SwapForm = () => {
: simulatedHealthRatio < 0
? 0
: Math.trunc(simulatedHealthRatio)
}, [
mangoAccount,
inputBank,
outputBank,
amountInAsDecimal,
amountOutAsDecimal,
])
const loadingSwapDetails: boolean = useMemo(() => {
return (
!!(amountInAsDecimal.toNumber() || amountOutAsDecimal.toNumber()) &&
swapOrLimit === 'swap' &&
connected &&
typeof selectedRoute === 'undefined'
)
}, [
amountInAsDecimal,
amountOutAsDecimal,
connected,
selectedRoute,
swapOrLimit,
])
}, [inputBank, outputBank, amountInFormValue, amountOutFormValue])
const handleSwapOrLimit = useCallback(
(orderType: string) => {
@ -185,15 +118,15 @@ const SwapForm = () => {
const estSlippage = useMemo(() => {
const { group } = mangoStore.getState()
if (!group || !inputBank || !amountInAsDecimal.gt(0)) return 0
const amountIn = amountInAsDecimal.toNumber()
const amountIn = parseFloat(amountInFormValue) || 0
if (!group || !inputBank || amountIn <= 0) return 0
const value = amountIn * inputBank.uiPrice
const slippage = group.getPriceImpactByTokenIndex(
inputBank.tokenIndex,
value,
)
return slippage
}, [amountInAsDecimal, inputBank])
}, [amountInFormValue, inputBank])
return (
<ContentBox
@ -201,25 +134,6 @@ const SwapForm = () => {
className="relative overflow-hidden border-x-0 bg-th-bkg-1 md:border-l md:border-r-0 md:border-t-0 md:border-b-0"
>
<div>
<Transition
className="absolute top-0 right-0 z-10 h-full w-full bg-th-bkg-1 pb-0"
show={showConfirm}
enter="transition ease-in duration-300"
enterFrom="-translate-x-full"
enterTo="translate-x-0"
leave="transition ease-out duration-300"
leaveFrom="translate-x-0"
leaveTo="-translate-x-full"
>
<SwapReviewRouteInfo
onClose={() => setShowConfirm(false)}
amountIn={amountInAsDecimal}
slippage={slippage}
routes={routes}
selectedRoute={selectedRoute}
setSelectedRoute={setSelectedRoute}
/>
</Transition>
<EnterBottomExitBottom
className="thin-scroll absolute bottom-0 left-0 z-10 h-full w-full overflow-auto bg-th-bkg-1 p-6 pb-0"
show={!!showTokenSelect}
@ -259,44 +173,14 @@ const SwapForm = () => {
</IconButton>
</div>
{swapOrLimit === 'swap' ? (
<MarketSwapForm
bestRoute={bestRoute}
selectedRoute={selectedRoute}
setSelectedRoute={setSelectedRoute}
setShowTokenSelect={setShowTokenSelect}
/>
<MarketSwapForm setShowTokenSelect={setShowTokenSelect} />
) : (
<LimitSwapForm
showTokenSelect={showTokenSelect}
setShowTokenSelect={setShowTokenSelect}
/>
)}
{ipAllowed ? (
swapOrLimit === 'swap' ? (
<SwapFormSubmitButton
loadingSwapDetails={loadingSwapDetails}
useMargin={useMargin}
selectedRoute={selectedRoute}
setShowConfirm={setShowConfirm}
amountIn={amountInAsDecimal}
inputSymbol={inputBank?.name}
amountOut={
selectedRoute ? amountOutAsDecimal.toNumber() : undefined
}
/>
) : null
) : (
<Button
disabled
className="mt-6 mb-4 w-full leading-tight"
size="large"
>
{t('country-not-allowed', {
country: ipCountry ? `(${ipCountry})` : '',
})}
</Button>
)}
{group && inputBank ? (
{inputBank ? (
<TokenVaultWarnings bank={inputBank} type="swap" />
) : null}
{inputBank &&
@ -355,7 +239,7 @@ const SwapForm = () => {
</div>
</>
) : null}
{estSlippage > 0 && swapOrLimit === 'trade:trigger-order' ? (
{estSlippage ? (
<>
<div className="flex items-center justify-between">
<p className="text-sm text-th-fgd-3">
@ -392,74 +276,3 @@ const SwapForm = () => {
}
export default SwapForm
const SwapFormSubmitButton = ({
amountIn,
amountOut,
inputSymbol,
loadingSwapDetails,
selectedRoute,
setShowConfirm,
useMargin,
}: {
amountIn: Decimal
amountOut: number | undefined
inputSymbol: string | undefined
loadingSwapDetails: boolean
selectedRoute: RouteInfo | undefined | null
setShowConfirm: (x: boolean) => void
useMargin: boolean
}) => {
const { t } = useTranslation('common')
const { connected, connect } = useWallet()
const { amount: tokenMax, amountWithBorrow } = useTokenMax(useMargin)
const showInsufficientBalance = useMargin
? amountWithBorrow.lt(amountIn)
: tokenMax.lt(amountIn)
const disabled =
connected &&
(!amountIn.toNumber() ||
showInsufficientBalance ||
!amountOut ||
!selectedRoute)
const onClick = connected ? () => setShowConfirm(true) : connect
return (
<>
<Button
onClick={onClick}
className="mt-6 mb-4 flex w-full items-center justify-center text-base"
disabled={disabled}
size="large"
>
{connected ? (
showInsufficientBalance ? (
<div className="flex items-center">
<ExclamationCircleIcon className="mr-2 h-5 w-5 flex-shrink-0" />
{t('swap:insufficient-balance', {
symbol: inputSymbol,
})}
</div>
) : loadingSwapDetails ? (
<Loading />
) : (
<span>{t('swap:review-swap')}</span>
)
) : (
<div className="flex items-center">
<LinkIcon className="mr-2 h-5 w-5" />
{t('connect')}
</div>
)}
</Button>
{selectedRoute === null && amountIn.gt(0) ? (
<div className="mb-4">
<InlineNotification type="error" desc={t('swap:no-swap-found')} />
</div>
) : null}
</>
)
}

View File

@ -249,26 +249,6 @@ const SwapTokenChart = () => {
: `${inputSymbol}/${outputSymbol}`
}, [flipPrices, inputBank, inputCoingeckoId, outputBank])
// const handleFlipPrices = useCallback(
// (flip: boolean) => {
// if (!flipPrices && flip) {
// setSwapChartSettings([
// ...swapChartSettings,
// { pair: swapMarketName, flipPrices: true },
// ])
// } else {
// setSwapChartSettings(
// swapChartSettings.filter(
// (chart: SwapChartSettings) =>
// !chart.pair.includes(inputBank!.name) &&
// !chart.pair.includes(outputBank!.name),
// ),
// )
// }
// },
// [flipPrices, inputBank, outputBank, swapChartSettings, swapMarketName],
// )
const handleSwapMouseEnter = useCallback(
(
swap: SwapHistoryItem | undefined,
@ -460,6 +440,22 @@ const SwapTokenChart = () => {
})
}, [coingeckoData, chartSwapTimes])
const latestChartDataItem = useMemo(() => {
if (!inputBank || !outputBank) return []
const price = !flipPrices
? outputBank.uiPrice / inputBank.uiPrice
: inputBank.uiPrice / outputBank.uiPrice
const item: ChartDataItem[] = [
{
price,
time: Date.now(),
inputTokenPrice: inputBank.uiPrice,
outputTokenPrice: outputBank.uiPrice,
},
]
return item
}, [flipPrices, inputBank, outputBank])
const chartData = useMemo(() => {
if (!coingeckoData || !coingeckoData.length || coingeckoData.length < 2)
return []
@ -469,7 +465,10 @@ const SwapTokenChart = () => {
const swapPoints = swapHistoryPoints.filter(
(point) => point.time >= minTime && point.time <= maxTime,
)
return coingeckoData.concat(swapPoints).sort((a, b) => a.time - b.time)
return coingeckoData
.concat(swapPoints)
.sort((a, b) => a.time - b.time)
.concat(latestChartDataItem)
} else return coingeckoData
}, [coingeckoData, swapHistoryPoints, showSwaps])

View File

@ -73,6 +73,7 @@
"realized-pnl": "Realized PnL",
"reduce": "Reduce",
"reduce-only": "Reduce Only",
"repay-borrow-order-desc": "Repay {{amount}} {{symbol}} if the oracle price reaches {{triggerPrice}} {{priceUnit}}",
"sells": "Sells",
"settle-funds": "Settle Funds",
"settle-funds-error": "Failed to settle funds",
@ -104,7 +105,7 @@
"trades": "Trades",
"trigger-price": "Trigger Price",
"trigger-order": "Trigger Order",
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price is {{triggerPrice}} {{priceUnit}}",
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price {{orderType}} {{triggerPrice}} {{priceUnit}}",
"trigger-orders": "Trigger Orders",
"tweet-position": "Tweet",
"unrealized-pnl": "Unrealized PnL",

View File

@ -73,6 +73,7 @@
"realized-pnl": "Realized PnL",
"reduce": "Reduce",
"reduce-only": "Reduce Only",
"repay-borrow-order-desc": "Repay {{amount}} {{symbol}} if the oracle price reaches {{triggerPrice}} {{priceUnit}}",
"sells": "Sells",
"settle-funds": "Settle Funds",
"settle-funds-error": "Failed to settle funds",
@ -104,7 +105,7 @@
"trades": "Trades",
"trigger-price": "Trigger Price",
"trigger-order": "Trigger Order",
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price is {{triggerPrice}} {{priceUnit}}",
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price {{orderType}} {{triggerPrice}} {{priceUnit}}",
"trigger-orders": "Trigger Orders",
"tweet-position": "Tweet",
"unrealized-pnl": "Unrealized PnL",

View File

@ -73,6 +73,7 @@
"realized-pnl": "Realized PnL",
"reduce": "Reduce",
"reduce-only": "Reduce Only",
"repay-borrow-order-desc": "Repay {{amount}} {{symbol}} if the oracle price reaches {{triggerPrice}} {{priceUnit}}",
"sells": "Sells",
"settle-funds": "Settle Funds",
"settle-funds-error": "Failed to settle funds",
@ -104,7 +105,7 @@
"trades": "Trades",
"trigger-price": "Trigger Price",
"trigger-order": "Trigger Order",
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price is {{triggerPrice}} {{priceUnit}}",
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price {{orderType}} {{triggerPrice}} {{priceUnit}}",
"trigger-orders": "Trigger Orders",
"tweet-position": "Tweet",
"unrealized-pnl": "Unrealized PnL",

View File

@ -72,6 +72,7 @@
"quote": "计价",
"reduce": "Reduce",
"reduce-only": "限减少",
"repay-borrow-order-desc": "Repay {{amount}} {{symbol}} if the oracle price reaches {{triggerPrice}} {{priceUnit}}",
"sells": "卖单",
"settle-funds": "借清资金",
"settle-funds-error": "借清出错",
@ -102,7 +103,7 @@
"tweet-position": "分享至Twitter",
"trigger-price": "Trigger Price",
"trigger-order": "Trigger Order",
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price is {{triggerPrice}} {{priceUnit}}",
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price {{orderType}} {{triggerPrice}} {{priceUnit}}",
"trigger-orders": "Trigger Orders",
"unsettled": "未結清",
"volume-alert": "交易量警報",

View File

@ -73,6 +73,7 @@
"realized-pnl": "已實現的盈虧",
"reduce": "Reduce",
"reduce-only": "限減少",
"repay-borrow-order-desc": "Repay {{amount}} {{symbol}} if the oracle price reaches {{triggerPrice}} {{priceUnit}}",
"sells": "賣單",
"settle-funds": "借清資金",
"settle-funds-error": "借清出錯",
@ -104,7 +105,7 @@
"trades": "交易",
"trigger-price": "Trigger Price",
"trigger-order": "Trigger Order",
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price is {{triggerPrice}} {{priceUnit}}",
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price {{orderType}} {{triggerPrice}} {{priceUnit}}",
"trigger-orders": "Trigger Orders",
"tweet-position": "分享至Twitter",
"unrealized-pnl": "未實現盈虧",