add checkbox to swap review to accept high price impact (#433)

* add checkbox to swap review to accept high price impact

* use coingecko price
This commit is contained in:
saml33 2024-07-09 23:36:08 +10:00 committed by GitHub
parent 92199db112
commit 676f9e09a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 67 additions and 31 deletions

View File

@ -6,6 +6,8 @@ const UninsuredNotification = ({ name }: { name: string | undefined }) => {
const { t } = useTranslation(['common', 'account'])
return (
<InlineNotification
hideBorder
hidePadding
type="info"
desc={
<>
@ -13,7 +15,7 @@ const UninsuredNotification = ({ name }: { name: string | undefined }) => {
wrapperClassName="inline"
content={t('account:tooltip-warning-uninsured', { token: name })}
>
<span className="tooltip-underline">
<span className="tooltip-underline text-th-fgd-3">
{t('account:warning-uninsured', {
token: name,
})}

View File

@ -67,6 +67,7 @@ import { isTokenInsured } from '@components/DepositForm'
import UninsuredNotification from '@components/shared/UninsuredNotification'
import { sendTxAndConfirm } from 'utils/governance/tools'
import useAnalytics from 'hooks/useAnalytics'
import Checkbox from '@components/forms/Checkbox'
type JupiterRouteInfoProps = {
amountIn: Decimal
@ -320,6 +321,7 @@ const SwapReviewRouteInfo = ({
const [feeValue] = useState<number | null>(null)
const [submitting, setSubmitting] = useState(false)
const [coingeckoPrices, setCoingeckoPrices] = useState(EMPTY_COINGECKO_PRICES)
const [acceptPriceImpact, setAcceptPriceImpact] = useState(false)
const { jupiterTokens } = useJupiterMints()
const { inputTokenInfo, outputTokenInfo } = useJupiterSwapData()
const inputBank = mangoStore((s) => s.swap.inputBank)
@ -656,17 +658,21 @@ const SwapReviewRouteInfo = ({
}, [amountIn, inputBank, outputBank])
const coinGeckoPriceDifference = useMemo(() => {
return amountOut?.toNumber()
? amountIn
.div(amountOut)
.minus(
new Decimal(coingeckoPrices?.outputCoingeckoPrice).div(
coingeckoPrices?.inputCoingeckoPrice,
),
)
.div(amountIn.div(amountOut))
.mul(100)
: new Decimal(0)
if (
!coingeckoPrices?.outputCoingeckoPrice ||
!coingeckoPrices?.inputCoingeckoPrice ||
!amountOut
)
return
return amountIn
.div(amountOut)
.minus(
new Decimal(coingeckoPrices.outputCoingeckoPrice).div(
coingeckoPrices.inputCoingeckoPrice,
),
)
.div(amountIn.div(amountOut))
.mul(100)
}, [coingeckoPrices, amountIn, amountOut])
const isInsured = useMemo(() => {
@ -674,6 +680,11 @@ const SwapReviewRouteInfo = ({
return isTokenInsured(outputBank, group)
}, [outputBank])
const showPriceImpactCheck = useMemo(() => {
if (!coinGeckoPriceDifference) return true
return coinGeckoPriceDifference.gte(5)
}, [coinGeckoPriceDifference])
return routes?.length &&
selectedRoute &&
inputTokenInfo &&
@ -725,29 +736,37 @@ const SwapReviewRouteInfo = ({
</div>
</div>
<div className="flex justify-center bg-th-bkg-1 px-6 pt-4">
<div className="mb-3 flex w-full flex-col items-center border-b border-th-bkg-3 pb-4">
<div className="flex w-full flex-col items-center pb-4">
<div className="relative mb-2 w-[72px]">
<TokenLogo bank={inputBank} size={40} />
<div className="absolute right-0 top-0">
<TokenLogo bank={outputBank} size={40} />
</div>
</div>
<p className="mb-0.5 flex items-center text-center text-lg">
<span className="mr-1 font-mono text-th-fgd-1">
<FormatNumericValue value={amountIn} />
</span>{' '}
{inputTokenInfo?.symbol}
<div className="mb-0.5 flex items-center text-center text-lg">
<span>
<span className="font-mono text-th-fgd-1">
<FormatNumericValue value={amountIn} />
</span>{' '}
<span className="text-xs text-th-fgd-4">
{inputBank?.name}
</span>
</span>
<ArrowRightIcon className="mx-2 h-5 w-5 text-th-fgd-2" />
<span className="mr-1 font-mono text-th-fgd-1">
<FormatNumericValue value={amountOut} />
</span>{' '}
{`${outputTokenInfo?.symbol}`}
</p>
<span>
<span className="font-mono text-th-fgd-1">
<FormatNumericValue value={amountOut} />
</span>{' '}
<span className="text-xs text-th-fgd-4">
{outputBank?.name}
</span>
</span>
</div>
</div>
</div>
<div className="space-y-2 overflow-auto px-6">
<div className="flex justify-between">
<p className="text-sm text-th-fgd-3">{t('price')}</p>
<p className="text-sm text-th-fgd-3">{t('swap:rate')}</p>
<div>
<div className="flex items-center justify-end">
<p className="text-right font-mono text-sm text-th-fgd-2">
@ -781,8 +800,7 @@ const SwapReviewRouteInfo = ({
/>
</div>
<div className="space-y-2 px-1 text-xs">
{coingeckoPrices?.outputCoingeckoPrice &&
coingeckoPrices?.inputCoingeckoPrice ? (
{coinGeckoPriceDifference ? (
<div
className={`text-right font-mono ${
coinGeckoPriceDifference.gt(1)
@ -995,18 +1013,29 @@ const SwapReviewRouteInfo = ({
</>
) : null}
</div>
{!isInsured ? (
<div className="mt-4 px-6">
<UninsuredNotification name={outputBank?.name} />
</div>
) : null}
</div>
<div className="p-6">
{showPriceImpactCheck ? (
<div className="mb-4 rounded-md border border-th-error p-2">
<Checkbox
checked={acceptPriceImpact}
onChange={(e) => setAcceptPriceImpact(e.target.checked)}
>
<p className="whitespace-normal text-th-fgd-2">
{coinGeckoPriceDifference
? `I accept the rate for this swap is
${coinGeckoPriceDifference.toFixed(1)}% worse than Coingecko`
: 'I accept the price of this swap has not been benchmarked to Coingecko and could differ to current market prices'}
</p>
</Checkbox>
</div>
) : null}
<div className="mb-4 flex items-center justify-center">
<Button
onClick={onClick}
className="flex w-full items-center justify-center text-base"
size="large"
disabled={showPriceImpactCheck && !acceptPriceImpact}
>
{submitting ? (
<Loading className="mr-2 h-5 w-5" />
@ -1116,6 +1145,11 @@ const SwapReviewRouteInfo = ({
)}
</Disclosure>
</div>
{!isInsured ? (
<div className="mt-4">
<UninsuredNotification name={outputBank?.name} />
</div>
) : null}
</div>
{showRoutesModal ? (
<RoutesModal