add trade page swap summary details

This commit is contained in:
tjs 2023-07-18 14:55:13 -04:00
parent e5945b60e2
commit 848286d947
10 changed files with 147 additions and 53 deletions

View File

@ -233,6 +233,7 @@ const useQuoteRoutes = ({
cacheTime: 1000 * 60,
staleTime: 1000 * 3,
enabled: enabled ? enabled() : amount ? true : false,
refetchInterval: 20000,
retry: 3,
}
)

View File

@ -19,6 +19,7 @@ import Button from '@components/shared/Button'
import Image from 'next/image'
import useQuoteRoutes from '@components/swap/useQuoteRoutes'
import {
HealthType,
Serum3Market,
fetchJupiterTransaction,
} from '@blockworks-foundation/mango-v4'
@ -31,6 +32,10 @@ import SwapSlider from '@components/swap/SwapSlider'
import PercentageSelectButtons from '@components/swap/PercentageSelectButtons'
import { SIZE_INPUT_UI_KEY } from 'utils/constants'
import useLocalStorageState from 'hooks/useLocalStorageState'
import MaxSwapAmount from '@components/swap/MaxSwapAmount'
import useUnownedAccount from 'hooks/useUnownedAccount'
import HealthImpact from '@components/shared/HealthImpact'
import Tooltip from '@components/shared/Tooltip'
const set = mangoStore.getState().set
const slippage = 100
@ -44,8 +49,9 @@ function stringToNumberOrZero(s: string): number {
}
export default function SpotMarketOrderSwapForm() {
const { t } = useTranslation(['common', 'trade'])
const { t } = useTranslation()
const { baseSize, price, quoteSize, side } = mangoStore((s) => s.tradeForm)
const { isUnownedAccount } = useUnownedAccount()
const [placingOrder, setPlacingOrder] = useState(false)
const { ipAllowed, ipCountry } = useIpAddress()
const { connected, publicKey } = useWallet()
@ -64,8 +70,6 @@ export default function SpotMarketOrderSwapForm() {
const handleBaseSizeChange = useCallback(
(e: NumberFormatValues, info: SourceInfo) => {
if (info.source !== 'event') return
console.log('base size change')
set((s) => {
const price =
s.tradeForm.tradeType === 'Market'
@ -103,12 +107,8 @@ export default function SpotMarketOrderSwapForm() {
[oraclePrice]
)
console.log('side outer', side)
const setAmountFromSlider = useCallback(
(amount: string) => {
console.log('amount', amount)
if (side === 'buy') {
handleQuoteSizeChange(
{ value: amount } as NumberFormatValues,
@ -121,7 +121,7 @@ export default function SpotMarketOrderSwapForm() {
)
}
},
[side]
[side, handleBaseSizeChange, handleQuoteSizeChange]
)
const [inputBank, outputBank] = useMemo(() => {
@ -168,7 +168,6 @@ export default function SpotMarketOrderSwapForm() {
const connection = mangoStore.getState().connection
if (!group || !mangoAccount) return
console.log('placing order')
if (
!mangoAccount ||
@ -177,22 +176,10 @@ export default function SpotMarketOrderSwapForm() {
!outputBank ||
!publicKey ||
!selectedRoute
) {
console.log(
mangoAccount,
group,
inputBank,
outputBank,
publicKey,
selectedRoute
)
return
}
setPlacingOrder(true)
console.log('placing order 2')
console.log('selected route', selectedRoute)
const [ixs, alts] = await fetchJupiterTransaction(
connection,
selectedRoute,
@ -256,16 +243,61 @@ export default function SpotMarketOrderSwapForm() {
connected ? handlePlaceOrder() : handleConnect()
}
const maintProjectedHealth = useMemo(() => {
const group = mangoStore.getState().group
const mangoAccount = mangoStore.getState().mangoAccount.current
if (!inputBank || !mangoAccount || !outputBank || !group) return 0
const simulatedHealthRatio =
mangoAccount.simHealthRatioWithTokenPositionUiChanges(
group,
[
{
mintPk: inputBank.mint,
uiTokenAmount:
(side === 'buy'
? stringToNumberOrZero(quoteSize)
: stringToNumberOrZero(baseSize)) * -1,
},
{
mintPk: outputBank.mint,
uiTokenAmount:
side === 'buy'
? stringToNumberOrZero(baseSize)
: stringToNumberOrZero(quoteSize),
},
],
HealthType.maint
)
return simulatedHealthRatio > 100
? 100
: simulatedHealthRatio < 0
? 0
: Math.trunc(simulatedHealthRatio)
}, [inputBank, outputBank, baseSize, quoteSize, side])
const disabled =
(connected && (!baseSize || !price)) ||
!serumOrPerpMarket ||
parseFloat(baseSize) < serumOrPerpMarket.minOrderSize ||
isLoading
const useMargin = true
return (
<>
<form onSubmit={(e) => handleSubmit(e)}>
<div className="mt-3 px-3 md:px-4">
<div className="mb-2 flex items-end justify-end">
{!isUnownedAccount ? (
<>
<MaxSwapAmount
useMargin={useMargin}
setAmountIn={setAmountFromSlider}
/>
</>
) : null}
</div>
<div className="flex flex-col">
<div className="relative">
<NumberFormat
@ -337,20 +369,20 @@ export default function SpotMarketOrderSwapForm() {
<div className="mt-6 mb-4 flex px-3 md:px-4">
{swapFormSizeUi === 'slider' ? (
<SwapSlider
useMargin={true}
useMargin={useMargin}
amount={
side === 'buy'
? stringToNumberOrZero(quoteSize)
: stringToNumberOrZero(baseSize)
}
onChange={(v) => setAmountFromSlider(v)}
onChange={setAmountFromSlider}
step={1 / 10 ** (inputBank?.mintDecimals || 6)}
/>
) : (
<PercentageSelectButtons
amountIn={side === 'buy' ? quoteSize : baseSize}
setAmountIn={(v) => setAmountFromSlider(v)}
useMargin={true}
setAmountIn={setAmountFromSlider}
useMargin={useMargin}
/>
)}
</div>
@ -403,6 +435,57 @@ export default function SpotMarketOrderSwapForm() {
</Button>
)}
</div>
<div className="space-y-2 px-3 md:px-4">
<div className="">
<HealthImpact maintProjectedHealth={maintProjectedHealth} small />
</div>
<div className="flex justify-between text-xs">
<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>
</>
}
>
<p className="tooltip-underline">{t('swap:price-impact')}</p>
</Tooltip>
<p className="text-right font-mono text-th-fgd-2">
{selectedRoute
? selectedRoute?.priceImpactPct * 100 < 0.1
? '<0.1%'
: `${(selectedRoute?.priceImpactPct * 100).toFixed(2)}%`
: '-'}
</p>
</div>
<div className="flex items-center justify-between text-xs">
<p className="pr-2 text-th-fgd-3">{t('common:route')}</p>
<div className="flex items-center overflow-hidden text-th-fgd-3">
<div className="truncate 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>
)
})}
</div>
</div>
</div>
</div>
</form>
</>
)

View File

@ -53,12 +53,13 @@
"html-react-parser": "3.0.4",
"html2canvas": "1.4.1",
"http-proxy-middleware": "2.0.6",
"i18next": "23.2.11",
"immer": "9.0.12",
"js-cookie": "3.0.1",
"klinecharts": "8.6.3",
"lodash": "4.17.21",
"next": "13.4.4",
"next-i18next": "11.1.1",
"next-i18next": "14.0.0",
"next-themes": "0.1.1",
"process": "0.11.10",
"react": "18.2.0",
@ -66,6 +67,7 @@
"react-flip-numbers": "3.0.5",
"react-grid-layout": "1.3.4",
"react-hot-keys": "2.7.2",
"react-i18next": "13.0.2",
"react-nice-dates": "3.1.0",
"react-number-format": "4.9.2",
"react-tsparticles": "2.2.4",

View File

@ -19,12 +19,10 @@ export async function getStaticProps({ locale }: { locale: string }) {
'notifications',
'onboarding',
'onboarding-tours',
'profile',
'search',
'settings',
'trade',
'close-account',
'tv-chart',
'swap',
])),
},
}

View File

@ -130,6 +130,7 @@
"repay-borrow": "Repay Borrow",
"repayment-amount": "Repayment Amount",
"rolling-change": "24h Change",
"route": "Route",
"save": "Save",
"select": "Select",
"select-borrow-token": "Select Borrow Token",

View File

@ -129,6 +129,7 @@
"repay-borrow": "Repay Borrow",
"repayment-amount": "Repayment Amount",
"rolling-change": "24h Change",
"route": "Route",
"save": "Save",
"select": "Select",
"select-borrow-token": "Select Borrow Token",

View File

@ -129,6 +129,7 @@
"repay-borrow": "Repay Borrow",
"repayment-amount": "Repayment Amount",
"rolling-change": "24h Change",
"route": "Route",
"save": "Save",
"select": "Select",
"select-borrow-token": "Select Borrow Token",

View File

@ -129,6 +129,7 @@
"repay-deposit": "归还及存入",
"repayment-amount": "还贷额",
"rolling-change": "24小时变化",
"route": "Route",
"save": "存",
"select": "选择",
"select-borrow-token": "选借入币种",

View File

@ -129,6 +129,7 @@
"repay-deposit": "歸還及存入",
"repayment-amount": "還貸額",
"rolling-change": "24小時變化",
"route": "Route",
"save": "存",
"select": "選擇",
"select-borrow-token": "選借入幣種",

View File

@ -7,13 +7,20 @@
resolved "https://registry.yarnpkg.com/@apocentre/alias-sampling/-/alias-sampling-0.5.3.tgz#897ff181b48ad7b2bcb4ecf29400214888244f08"
integrity sha512-7UDWIIF9hIeJqfKXkNIzkVandlwLf1FWTSdrb9iXvOP8oF544JRXQjCbiTmCv2c9n44n/FIWtehhBfNuAx2CZA==
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.5", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.5", "@babel/runtime@^7.17.2", "@babel/runtime@^7.18.6", "@babel/runtime@^7.19.4", "@babel/runtime@^7.20.7":
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.5", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2", "@babel/runtime@^7.19.4", "@babel/runtime@^7.20.7":
version "7.21.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
dependencies:
regenerator-runtime "^0.13.11"
"@babel/runtime@^7.20.13", "@babel/runtime@^7.22.5":
version "7.22.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438"
integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==
dependencies:
regenerator-runtime "^0.13.11"
"@blockworks-foundation/mango-feeds@0.1.7":
version "0.1.7"
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-feeds/-/mango-feeds-0.1.7.tgz#07283e4f6b52ce2e631b866f7675b395dcc15f9e"
@ -5673,17 +5680,17 @@ husky@8.0.1:
resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.1.tgz#511cb3e57de3e3190514ae49ed50f6bc3f50b3e9"
integrity sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==
i18next-fs-backend@^1.1.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/i18next-fs-backend/-/i18next-fs-backend-1.2.0.tgz#c498c68c8e6a8ae5ed59bea5e5392a11991de696"
integrity sha512-pUx3AcgXCbur0jpFA7U67Z2RJflAcIi698Y8VL+phdOqUchahxriV3Cs+M6UkPNQSS/zPEzWLfdJ8EgjB7HVxg==
i18next-fs-backend@^2.1.5:
version "2.1.5"
resolved "https://registry.yarnpkg.com/i18next-fs-backend/-/i18next-fs-backend-2.1.5.tgz#03347eacde6a35b599da1889a048ae63bcf0c687"
integrity sha512-7fgSH8nVhXSBYPHR/W3tEXXhcnwHwNiND4Dfx9knzPzdsWTUTL/TdDVV+DY0dL0asHKLbdoJaXS4LdVW6R8MVQ==
i18next@^21.8.13:
version "21.10.0"
resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.10.0.tgz#85429af55fdca4858345d0e16b584ec29520197d"
integrity sha512-YeuIBmFsGjUfO3qBmMOc0rQaun4mIpGKET5WDwvu8lU7gvwpcariZLNtL0Fzj+zazcHUrlXHiptcFhBMFaxzfg==
i18next@23.2.11:
version "23.2.11"
resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.2.11.tgz#0c6f3a637fa87d3243e64b78ad285b7f77d41353"
integrity sha512-MA4FsxOjyCaOZtRDB4yuwjCvqYEioD4G4LlXOn7SO3rnQUlxTufyLsOqfL9MKakeLRBkefe8bqcs0D6Z/xFk1w==
dependencies:
"@babel/runtime" "^7.17.2"
"@babel/runtime" "^7.22.5"
iconv-lite@^0.4.24:
version "0.4.24"
@ -6634,18 +6641,16 @@ near-seed-phrase@^0.2.0:
near-hd-key "^1.2.1"
tweetnacl "^1.0.2"
next-i18next@11.1.1:
version "11.1.1"
resolved "https://registry.yarnpkg.com/next-i18next/-/next-i18next-11.1.1.tgz#27650f1995c306174ed96436fa77ab4db2273e6a"
integrity sha512-msffPxyaU7oM5s55taIlvhSbVa8E62tXbuFZr+4myrhALQzRY+N8ExurJG33LvM9GbcwQazzXt1yeNYUynm4qQ==
next-i18next@14.0.0:
version "14.0.0"
resolved "https://registry.yarnpkg.com/next-i18next/-/next-i18next-14.0.0.tgz#fd637c81df96c9d6f2cc4716037130ac4e5d6968"
integrity sha512-umv8hOZoSoAA+td3ErfemyO/5Ib2pnYCdQ8/Oy+fncS2skFIL3hHKRer3Oa3Nfm4Xbv5p6DHWzm3NhT1j4tWwg==
dependencies:
"@babel/runtime" "^7.18.6"
"@babel/runtime" "^7.20.13"
"@types/hoist-non-react-statics" "^3.3.1"
core-js "^3"
hoist-non-react-statics "^3.3.2"
i18next "^21.8.13"
i18next-fs-backend "^1.1.4"
react-i18next "^11.18.0"
i18next-fs-backend "^2.1.5"
next-themes@0.1.1:
version "0.1.1"
@ -7390,12 +7395,12 @@ react-hot-keys@2.7.2:
hotkeys-js "^3.8.1"
prop-types "^15.7.2"
react-i18next@^11.18.0:
version "11.18.6"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.18.6.tgz#e159c2960c718c1314f1e8fcaa282d1c8b167887"
integrity sha512-yHb2F9BiT0lqoQDt8loZ5gWP331GwctHz9tYQ8A2EIEUu+CcEdjBLQWli1USG3RdWQt3W+jqQLg/d4rrQR96LA==
react-i18next@13.0.2:
version "13.0.2"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-13.0.2.tgz#1708a9bdabc1fe1dd4a8534f4c3a80ab784b01e9"
integrity sha512-NEVxC32v0oR4egwYM0QM0WE93AiJG5r0NTXTL8mhQfAhsMfDS2fSO6jpluyfsfypP988KzUQrAXncspcJ7+GHA==
dependencies:
"@babel/runtime" "^7.14.5"
"@babel/runtime" "^7.22.5"
html-parse-stringify "^3.0.1"
react-is@^16.10.2, react-is@^16.13.1, react-is@^16.7.0: