Merge branch 'main' into edit-orders
This commit is contained in:
commit
0ebad8d9a3
|
@ -2,7 +2,6 @@ import { Bank, Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||||
import useJupiterMints from 'hooks/useJupiterMints'
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import Decimal from 'decimal.js'
|
|
||||||
import useMangoAccount from 'hooks/useMangoAccount'
|
import useMangoAccount from 'hooks/useMangoAccount'
|
||||||
import { useViewport } from 'hooks/useViewport'
|
import { useViewport } from 'hooks/useViewport'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
@ -17,7 +16,7 @@ import {
|
||||||
trimDecimals,
|
trimDecimals,
|
||||||
} from 'utils/numbers'
|
} from 'utils/numbers'
|
||||||
import { breakpoints } from 'utils/theme'
|
import { breakpoints } from 'utils/theme'
|
||||||
import { calculateMarketPrice } from 'utils/tradeForm'
|
import { calculateLimitPriceForMarketOrder } from 'utils/tradeForm'
|
||||||
import { LinkButton } from './Button'
|
import { LinkButton } from './Button'
|
||||||
import { Table, Td, Th, TrBody, TrHead } from './TableElements'
|
import { Table, Td, Th, TrBody, TrHead } from './TableElements'
|
||||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||||
|
@ -218,9 +217,9 @@ const Balance = ({ bank }: { bank: Bank }) => {
|
||||||
(balance > 0 && type === 'quote') || (balance < 0 && type === 'base')
|
(balance > 0 && type === 'quote') || (balance < 0 && type === 'base')
|
||||||
? 'buy'
|
? 'buy'
|
||||||
: 'sell'
|
: 'sell'
|
||||||
price = calculateMarketPrice(orderbook, balance, side)
|
price = calculateLimitPriceForMarketOrder(orderbook, balance, side)
|
||||||
} else {
|
} else {
|
||||||
price = new Decimal(tradeForm.price).toNumber()
|
price = Number(tradeForm.price)
|
||||||
}
|
}
|
||||||
|
|
||||||
let minOrderDecimals: number
|
let minOrderDecimals: number
|
||||||
|
|
|
@ -61,6 +61,16 @@ export const getTokenInMax = (
|
||||||
inputBank.mintDecimals
|
inputBank.mintDecimals
|
||||||
)
|
)
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'getMaxSourceUiForTokenSwap',
|
||||||
|
mangoAccount.getMaxSourceUiForTokenSwap(
|
||||||
|
group,
|
||||||
|
inputBank.mint,
|
||||||
|
outputBank.mint,
|
||||||
|
inputBank.uiPrice / outputBank.uiPrice
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
const inputBankVaultBalance = floorToDecimal(
|
const inputBankVaultBalance = floorToDecimal(
|
||||||
group.getTokenVaultBalanceByMintUi(inputBank.mint),
|
group.getTokenVaultBalanceByMintUi(inputBank.mint),
|
||||||
inputBank.mintDecimals
|
inputBank.mintDecimals
|
||||||
|
|
|
@ -22,7 +22,7 @@ import NumberFormat, {
|
||||||
} from 'react-number-format'
|
} from 'react-number-format'
|
||||||
import { notify } from 'utils/notifications'
|
import { notify } from 'utils/notifications'
|
||||||
import SpotSlider from './SpotSlider'
|
import SpotSlider from './SpotSlider'
|
||||||
import { calculateMarketPrice } from 'utils/tradeForm'
|
import { calculateLimitPriceForMarketOrder } from 'utils/tradeForm'
|
||||||
import Image from 'next/legacy/image'
|
import Image from 'next/legacy/image'
|
||||||
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
||||||
import Loading from '@components/shared/Loading'
|
import Loading from '@components/shared/Loading'
|
||||||
|
@ -45,12 +45,13 @@ const TABS: [string, number][] = [
|
||||||
['Market', 0],
|
['Market', 0],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const set = mangoStore.getState().set
|
||||||
|
|
||||||
const AdvancedTradeForm = () => {
|
const AdvancedTradeForm = () => {
|
||||||
const { t } = useTranslation(['common', 'trade'])
|
const { t } = useTranslation(['common', 'trade'])
|
||||||
const set = mangoStore.getState().set
|
|
||||||
const tradeForm = mangoStore((s) => s.tradeForm)
|
const tradeForm = mangoStore((s) => s.tradeForm)
|
||||||
const { mangoTokens } = useJupiterMints()
|
const { mangoTokens } = useJupiterMints()
|
||||||
const { selectedMarket, price: marketPrice } = useSelectedMarket()
|
const { selectedMarket, price: oraclePrice } = useSelectedMarket()
|
||||||
const [useMargin, setUseMargin] = useState(true)
|
const [useMargin, setUseMargin] = useState(true)
|
||||||
const [placingOrder, setPlacingOrder] = useState(false)
|
const [placingOrder, setPlacingOrder] = useState(false)
|
||||||
const [tradeFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'Slider')
|
const [tradeFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'Slider')
|
||||||
|
@ -93,14 +94,11 @@ const AdvancedTradeForm = () => {
|
||||||
return ''
|
return ''
|
||||||
}, [quoteSymbol, mangoTokens])
|
}, [quoteSymbol, mangoTokens])
|
||||||
|
|
||||||
const setTradeType = useCallback(
|
const setTradeType = useCallback((tradeType: 'Limit' | 'Market') => {
|
||||||
(tradeType: 'Limit' | 'Market') => {
|
set((s) => {
|
||||||
set((s) => {
|
s.tradeForm.tradeType = tradeType
|
||||||
s.tradeForm.tradeType = tradeType
|
})
|
||||||
})
|
}, [])
|
||||||
},
|
|
||||||
[set]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handlePriceChange = useCallback(
|
const handlePriceChange = useCallback(
|
||||||
(e: NumberFormatValues, info: SourceInfo) => {
|
(e: NumberFormatValues, info: SourceInfo) => {
|
||||||
|
@ -114,7 +112,7 @@ const AdvancedTradeForm = () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[set]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleBaseSizeChange = useCallback(
|
const handleBaseSizeChange = useCallback(
|
||||||
|
@ -123,8 +121,8 @@ const AdvancedTradeForm = () => {
|
||||||
set((s) => {
|
set((s) => {
|
||||||
const price =
|
const price =
|
||||||
s.tradeForm.tradeType === 'Market'
|
s.tradeForm.tradeType === 'Market'
|
||||||
? marketPrice
|
? oraclePrice
|
||||||
: parseFloat(s.tradeForm.price)
|
: Number(s.tradeForm.price)
|
||||||
|
|
||||||
s.tradeForm.baseSize = e.value
|
s.tradeForm.baseSize = e.value
|
||||||
if (price && e.value !== '' && !Number.isNaN(Number(e.value))) {
|
if (price && e.value !== '' && !Number.isNaN(Number(e.value))) {
|
||||||
|
@ -134,7 +132,7 @@ const AdvancedTradeForm = () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[set, marketPrice]
|
[oraclePrice]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleQuoteSizeChange = useCallback(
|
const handleQuoteSizeChange = useCallback(
|
||||||
|
@ -143,8 +141,8 @@ const AdvancedTradeForm = () => {
|
||||||
set((s) => {
|
set((s) => {
|
||||||
const price =
|
const price =
|
||||||
s.tradeForm.tradeType === 'Market'
|
s.tradeForm.tradeType === 'Market'
|
||||||
? marketPrice
|
? oraclePrice
|
||||||
: parseFloat(s.tradeForm.price)
|
: Number(s.tradeForm.price)
|
||||||
|
|
||||||
s.tradeForm.quoteSize = e.value
|
s.tradeForm.quoteSize = e.value
|
||||||
if (price && e.value !== '' && !Number.isNaN(Number(e.value))) {
|
if (price && e.value !== '' && !Number.isNaN(Number(e.value))) {
|
||||||
|
@ -154,47 +152,55 @@ const AdvancedTradeForm = () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[set, marketPrice]
|
[oraclePrice]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handlePostOnlyChange = useCallback(
|
const handlePostOnlyChange = useCallback((postOnly: boolean) => {
|
||||||
(postOnly: boolean) => {
|
set((s) => {
|
||||||
|
s.tradeForm.postOnly = postOnly
|
||||||
|
if (s.tradeForm.ioc === true) {
|
||||||
|
s.tradeForm.ioc = !postOnly
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleIocChange = useCallback((ioc: boolean) => {
|
||||||
|
set((s) => {
|
||||||
|
s.tradeForm.ioc = ioc
|
||||||
|
if (s.tradeForm.postOnly === true) {
|
||||||
|
s.tradeForm.postOnly = !ioc
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleSetSide = useCallback((side: 'buy' | 'sell') => {
|
||||||
|
set((s) => {
|
||||||
|
s.tradeForm.side = side
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Updates the limit price on page load
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (tradeForm.price === undefined) {
|
||||||
|
const group = mangoStore.getState().group
|
||||||
|
if (!group || !oraclePrice) return
|
||||||
|
|
||||||
set((s) => {
|
set((s) => {
|
||||||
s.tradeForm.postOnly = postOnly
|
s.tradeForm.price = oraclePrice.toString()
|
||||||
if (s.tradeForm.ioc === true) {
|
|
||||||
s.tradeForm.ioc = !postOnly
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
[set]
|
}, [oraclePrice, tradeForm.price])
|
||||||
)
|
|
||||||
|
|
||||||
const handleIocChange = useCallback(
|
|
||||||
(ioc: boolean) => {
|
|
||||||
set((s) => {
|
|
||||||
s.tradeForm.ioc = ioc
|
|
||||||
if (s.tradeForm.postOnly === true) {
|
|
||||||
s.tradeForm.postOnly = !ioc
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[set]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleSetSide = useCallback(
|
|
||||||
(side: 'buy' | 'sell') => {
|
|
||||||
set((s) => {
|
|
||||||
s.tradeForm.side = side
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[set]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Updates the price and the quote size when a Market order is selected
|
||||||
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const group = mangoStore.getState().group
|
const group = mangoStore.getState().group
|
||||||
if (
|
if (
|
||||||
tradeForm.tradeType === 'Market' &&
|
tradeForm.tradeType === 'Market' &&
|
||||||
marketPrice &&
|
oraclePrice &&
|
||||||
selectedMarket &&
|
selectedMarket &&
|
||||||
group
|
group
|
||||||
) {
|
) {
|
||||||
|
@ -209,20 +215,18 @@ const AdvancedTradeForm = () => {
|
||||||
}
|
}
|
||||||
if (!isNaN(parseFloat(tradeForm.baseSize))) {
|
if (!isNaN(parseFloat(tradeForm.baseSize))) {
|
||||||
const baseSize = new Decimal(tradeForm.baseSize)?.toNumber()
|
const baseSize = new Decimal(tradeForm.baseSize)?.toNumber()
|
||||||
const orderbook = mangoStore.getState().selectedMarket.orderbook
|
const quoteSize = baseSize * oraclePrice
|
||||||
const price = calculateMarketPrice(orderbook, baseSize, tradeForm.side)
|
|
||||||
const quoteSize = baseSize * price
|
|
||||||
set((s) => {
|
set((s) => {
|
||||||
s.tradeForm.price = price.toFixed(getDecimalCount(tickSize))
|
s.tradeForm.price = oraclePrice.toFixed(getDecimalCount(tickSize))
|
||||||
s.tradeForm.quoteSize = quoteSize.toFixed(getDecimalCount(tickSize))
|
s.tradeForm.quoteSize = quoteSize.toFixed(getDecimalCount(tickSize))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
set((s) => {
|
set((s) => {
|
||||||
s.tradeForm.price = marketPrice.toFixed(getDecimalCount(tickSize))
|
s.tradeForm.price = oraclePrice.toFixed(getDecimalCount(tickSize))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [marketPrice, selectedMarket, tradeForm])
|
}, [oraclePrice, selectedMarket, tradeForm])
|
||||||
|
|
||||||
const handlePlaceOrder = useCallback(async () => {
|
const handlePlaceOrder = useCallback(async () => {
|
||||||
const client = mangoStore.getState().client
|
const client = mangoStore.getState().client
|
||||||
|
@ -235,11 +239,15 @@ const AdvancedTradeForm = () => {
|
||||||
if (!group || !mangoAccount) return
|
if (!group || !mangoAccount) return
|
||||||
setPlacingOrder(true)
|
setPlacingOrder(true)
|
||||||
try {
|
try {
|
||||||
const baseSize = new Decimal(tradeForm.baseSize).toNumber()
|
const baseSize = Number(tradeForm.baseSize)
|
||||||
let price = new Decimal(tradeForm.price).toNumber()
|
let price = Number(tradeForm.price)
|
||||||
if (tradeForm.tradeType === 'Market') {
|
if (tradeForm.tradeType === 'Market') {
|
||||||
const orderbook = mangoStore.getState().selectedMarket.orderbook
|
const orderbook = mangoStore.getState().selectedMarket.orderbook
|
||||||
price = calculateMarketPrice(orderbook, baseSize, tradeForm.side)
|
price = calculateLimitPriceForMarketOrder(
|
||||||
|
orderbook,
|
||||||
|
baseSize,
|
||||||
|
tradeForm.side
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedMarket instanceof Serum3Market) {
|
if (selectedMarket instanceof Serum3Market) {
|
||||||
|
@ -260,7 +268,6 @@ const AdvancedTradeForm = () => {
|
||||||
Date.now(),
|
Date.now(),
|
||||||
10
|
10
|
||||||
)
|
)
|
||||||
actions.reloadMangoAccount()
|
|
||||||
actions.fetchOpenOrders()
|
actions.fetchOpenOrders()
|
||||||
notify({
|
notify({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
@ -289,7 +296,6 @@ const AdvancedTradeForm = () => {
|
||||||
undefined,
|
undefined,
|
||||||
undefined
|
undefined
|
||||||
)
|
)
|
||||||
actions.reloadMangoAccount()
|
|
||||||
actions.fetchOpenOrders()
|
actions.fetchOpenOrders()
|
||||||
notify({
|
notify({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
@ -308,7 +314,7 @@ const AdvancedTradeForm = () => {
|
||||||
} finally {
|
} finally {
|
||||||
setPlacingOrder(false)
|
setPlacingOrder(false)
|
||||||
}
|
}
|
||||||
}, [t])
|
}, [])
|
||||||
|
|
||||||
const maintProjectedHealth = useMemo(() => {
|
const maintProjectedHealth = useMemo(() => {
|
||||||
const group = mangoStore.getState().group
|
const group = mangoStore.getState().group
|
||||||
|
@ -344,13 +350,13 @@ const AdvancedTradeForm = () => {
|
||||||
group,
|
group,
|
||||||
selectedMarket.perpMarketIndex,
|
selectedMarket.perpMarketIndex,
|
||||||
parseFloat(tradeForm.baseSize),
|
parseFloat(tradeForm.baseSize),
|
||||||
parseFloat(tradeForm.price)
|
Number(tradeForm.price)
|
||||||
)
|
)
|
||||||
: mangoAccount.simHealthRatioWithPerpBidUiChanges(
|
: mangoAccount.simHealthRatioWithPerpBidUiChanges(
|
||||||
group,
|
group,
|
||||||
selectedMarket.perpMarketIndex,
|
selectedMarket.perpMarketIndex,
|
||||||
parseFloat(tradeForm.baseSize),
|
parseFloat(tradeForm.baseSize),
|
||||||
parseFloat(tradeForm.price)
|
Number(tradeForm.price)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -23,13 +23,13 @@ const PerpButtonGroup = () => {
|
||||||
return mangoAccount.getMaxQuoteForPerpBidUi(
|
return mangoAccount.getMaxQuoteForPerpBidUi(
|
||||||
group,
|
group,
|
||||||
selectedMarket.perpMarketIndex,
|
selectedMarket.perpMarketIndex,
|
||||||
parseFloat(tradeFormPrice)
|
Number(tradeFormPrice)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return mangoAccount.getMaxBaseForPerpAskUi(
|
return mangoAccount.getMaxBaseForPerpAskUi(
|
||||||
group,
|
group,
|
||||||
selectedMarket.perpMarketIndex,
|
selectedMarket.perpMarketIndex,
|
||||||
parseFloat(tradeFormPrice)
|
Number(tradeFormPrice)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -53,9 +53,7 @@ const PerpButtonGroup = () => {
|
||||||
s.tradeForm.quoteSize = size.toString()
|
s.tradeForm.quoteSize = size.toString()
|
||||||
|
|
||||||
if (Number(s.tradeForm.price)) {
|
if (Number(s.tradeForm.price)) {
|
||||||
s.tradeForm.baseSize = (
|
s.tradeForm.baseSize = (size / Number(s.tradeForm.price)).toString()
|
||||||
size / parseFloat(s.tradeForm.price)
|
|
||||||
).toString()
|
|
||||||
} else {
|
} else {
|
||||||
s.tradeForm.baseSize = ''
|
s.tradeForm.baseSize = ''
|
||||||
}
|
}
|
||||||
|
@ -64,7 +62,7 @@ const PerpButtonGroup = () => {
|
||||||
|
|
||||||
if (Number(s.tradeForm.price)) {
|
if (Number(s.tradeForm.price)) {
|
||||||
s.tradeForm.quoteSize = (
|
s.tradeForm.quoteSize = (
|
||||||
size * parseFloat(s.tradeForm.price)
|
size * Number(s.tradeForm.price)
|
||||||
).toString()
|
).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
|
||||||
import { LinkIcon, NoSymbolIcon } from '@heroicons/react/20/solid'
|
import { LinkIcon, NoSymbolIcon } from '@heroicons/react/20/solid'
|
||||||
import { useWallet } from '@solana/wallet-adapter-react'
|
import { useWallet } from '@solana/wallet-adapter-react'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import Decimal from 'decimal.js'
|
|
||||||
import useMangoGroup from 'hooks/useMangoGroup'
|
import useMangoGroup from 'hooks/useMangoGroup'
|
||||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
@ -14,7 +13,7 @@ import {
|
||||||
numberFormat,
|
numberFormat,
|
||||||
trimDecimals,
|
trimDecimals,
|
||||||
} from 'utils/numbers'
|
} from 'utils/numbers'
|
||||||
import { calculateMarketPrice } from 'utils/tradeForm'
|
import { calculateLimitPriceForMarketOrder } from 'utils/tradeForm'
|
||||||
import PerpSideBadge from './PerpSideBadge'
|
import PerpSideBadge from './PerpSideBadge'
|
||||||
import TableMarketName from './TableMarketName'
|
import TableMarketName from './TableMarketName'
|
||||||
|
|
||||||
|
@ -29,10 +28,14 @@ const PerpPositions = () => {
|
||||||
const tradeForm = mangoStore.getState().tradeForm
|
const tradeForm = mangoStore.getState().tradeForm
|
||||||
const set = mangoStore.getState().set
|
const set = mangoStore.getState().set
|
||||||
|
|
||||||
let price = new Decimal(tradeForm.price).toNumber()
|
let price = Number(tradeForm.price)
|
||||||
if (tradeForm.tradeType === 'Market') {
|
if (tradeForm.tradeType === 'Market') {
|
||||||
const orderbook = mangoStore.getState().selectedMarket.orderbook
|
const orderbook = mangoStore.getState().selectedMarket.orderbook
|
||||||
price = calculateMarketPrice(orderbook, positionSize, tradeForm.side)
|
price = calculateLimitPriceForMarketOrder(
|
||||||
|
orderbook,
|
||||||
|
positionSize,
|
||||||
|
tradeForm.side
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const newSide = positionSize > 0 ? 'sell' : 'buy'
|
const newSide = positionSize > 0 ? 'sell' : 'buy'
|
||||||
|
|
||||||
|
|
|
@ -29,13 +29,13 @@ const PerpSlider = () => {
|
||||||
return mangoAccount.getMaxQuoteForPerpBidUi(
|
return mangoAccount.getMaxQuoteForPerpBidUi(
|
||||||
group,
|
group,
|
||||||
selectedMarket.perpMarketIndex,
|
selectedMarket.perpMarketIndex,
|
||||||
parseFloat(tradeForm.price)
|
Number(tradeForm.price)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return mangoAccount.getMaxBaseForPerpAskUi(
|
return mangoAccount.getMaxBaseForPerpAskUi(
|
||||||
group,
|
group,
|
||||||
selectedMarket.perpMarketIndex,
|
selectedMarket.perpMarketIndex,
|
||||||
parseFloat(tradeForm.price)
|
Number(tradeForm.price)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -56,7 +56,7 @@ const PerpSlider = () => {
|
||||||
const price =
|
const price =
|
||||||
s.tradeForm.tradeType === 'Market'
|
s.tradeForm.tradeType === 'Market'
|
||||||
? marketPrice
|
? marketPrice
|
||||||
: parseFloat(s.tradeForm.price)
|
: Number(s.tradeForm.price)
|
||||||
|
|
||||||
if (s.tradeForm.side === 'buy') {
|
if (s.tradeForm.side === 'buy') {
|
||||||
s.tradeForm.quoteSize = val
|
s.tradeForm.quoteSize = val
|
||||||
|
|
|
@ -50,9 +50,7 @@ const SpotButtonGroup = () => {
|
||||||
s.tradeForm.quoteSize = size.toString()
|
s.tradeForm.quoteSize = size.toString()
|
||||||
|
|
||||||
if (Number(s.tradeForm.price)) {
|
if (Number(s.tradeForm.price)) {
|
||||||
s.tradeForm.baseSize = (
|
s.tradeForm.baseSize = (size / Number(s.tradeForm.price)).toString()
|
||||||
size / parseFloat(s.tradeForm.price)
|
|
||||||
).toString()
|
|
||||||
} else {
|
} else {
|
||||||
s.tradeForm.baseSize = ''
|
s.tradeForm.baseSize = ''
|
||||||
}
|
}
|
||||||
|
@ -61,7 +59,7 @@ const SpotButtonGroup = () => {
|
||||||
|
|
||||||
if (Number(s.tradeForm.price)) {
|
if (Number(s.tradeForm.price)) {
|
||||||
s.tradeForm.quoteSize = (
|
s.tradeForm.quoteSize = (
|
||||||
size * parseFloat(s.tradeForm.price)
|
size * Number(s.tradeForm.price)
|
||||||
).toString()
|
).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ const SpotSlider = () => {
|
||||||
const price =
|
const price =
|
||||||
s.tradeForm.tradeType === 'Market'
|
s.tradeForm.tradeType === 'Market'
|
||||||
? marketPrice
|
? marketPrice
|
||||||
: parseFloat(s.tradeForm.price)
|
: Number(s.tradeForm.price)
|
||||||
|
|
||||||
if (s.tradeForm.side === 'buy') {
|
if (s.tradeForm.side === 'buy') {
|
||||||
s.tradeForm.quoteSize = val
|
s.tradeForm.quoteSize = val
|
||||||
|
|
|
@ -45,7 +45,6 @@ const UnsettledTrades = ({
|
||||||
new PublicKey(mktAddress)
|
new PublicKey(mktAddress)
|
||||||
)
|
)
|
||||||
actions.fetchOpenOrders()
|
actions.fetchOpenOrders()
|
||||||
actions.reloadMangoAccount()
|
|
||||||
notify({
|
notify({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
title: 'Successfully settled funds',
|
title: 'Successfully settled funds',
|
||||||
|
|
|
@ -4,7 +4,10 @@ import type { NextPage } from 'next'
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||||
import useMangoAccount from 'hooks/useMangoAccount'
|
import useMangoAccount from 'hooks/useMangoAccount'
|
||||||
import { toUiDecimalsForQuote, HealthType } from '@blockworks-foundation/mango-v4';
|
import {
|
||||||
|
toUiDecimalsForQuote,
|
||||||
|
HealthType,
|
||||||
|
} from '@blockworks-foundation/mango-v4'
|
||||||
|
|
||||||
export async function getStaticProps({ locale }: { locale: string }) {
|
export async function getStaticProps({ locale }: { locale: string }) {
|
||||||
return {
|
return {
|
||||||
|
@ -56,19 +59,27 @@ const Dashboard: NextPage = () => {
|
||||||
/>
|
/>
|
||||||
<KeyValuePair
|
<KeyValuePair
|
||||||
label="Init Health"
|
label="Init Health"
|
||||||
value={`$${toUiDecimalsForQuote(mangoAccount.getHealth(group, HealthType.init)).toFixed(4)}`}
|
value={`$${toUiDecimalsForQuote(
|
||||||
|
mangoAccount.getHealth(group, HealthType.init)
|
||||||
|
).toFixed(4)}`}
|
||||||
/>
|
/>
|
||||||
<KeyValuePair
|
<KeyValuePair
|
||||||
label="Maint Health"
|
label="Maint Health"
|
||||||
value={`$${toUiDecimalsForQuote(mangoAccount.getHealth(group, HealthType.maint)).toFixed(4)}`}
|
value={`$${toUiDecimalsForQuote(
|
||||||
|
mangoAccount.getHealth(group, HealthType.maint)
|
||||||
|
).toFixed(4)}`}
|
||||||
/>
|
/>
|
||||||
<KeyValuePair
|
<KeyValuePair
|
||||||
label="Perp Settle Health"
|
label="Perp Settle Health"
|
||||||
value={`$${toUiDecimalsForQuote(mangoAccount.getPerpSettleHealth(group)).toFixed(4)}`}
|
value={`$${toUiDecimalsForQuote(
|
||||||
|
mangoAccount.getPerpSettleHealth(group)
|
||||||
|
).toFixed(4)}`}
|
||||||
/>
|
/>
|
||||||
<KeyValuePair
|
<KeyValuePair
|
||||||
label="Net Deposits"
|
label="Net Deposits"
|
||||||
value={`$${toUiDecimalsForQuote(mangoAccount.netDeposits).toFixed(4)}`}
|
value={`$${toUiDecimalsForQuote(
|
||||||
|
mangoAccount.netDeposits
|
||||||
|
).toFixed(4)}`}
|
||||||
/>
|
/>
|
||||||
<KeyValuePair
|
<KeyValuePair
|
||||||
label="Perp Spot Transfers"
|
label="Perp Spot Transfers"
|
||||||
|
@ -154,7 +165,9 @@ const Dashboard: NextPage = () => {
|
||||||
/>
|
/>
|
||||||
<KeyValuePair
|
<KeyValuePair
|
||||||
label="Quote Position UI"
|
label="Quote Position UI"
|
||||||
value={`$${toUiDecimalsForQuote(perp.quotePositionNative).toFixed(4)}`}
|
value={`$${toUiDecimalsForQuote(
|
||||||
|
perp.quotePositionNative
|
||||||
|
).toFixed(4)}`}
|
||||||
/>
|
/>
|
||||||
<KeyValuePair
|
<KeyValuePair
|
||||||
label="Quote Running Native"
|
label="Quote Running Native"
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
|
import {
|
||||||
|
Group,
|
||||||
|
PerpMarket,
|
||||||
|
Serum3Market,
|
||||||
|
} from '@blockworks-foundation/mango-v4'
|
||||||
import TradeAdvancedPage from '@components/trade/TradeAdvancedPage'
|
import TradeAdvancedPage from '@components/trade/TradeAdvancedPage'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore, { DEFAULT_TRADE_FORM } from '@store/mangoStore'
|
||||||
// import mangoStore from '@store/mangoStore'
|
// import mangoStore from '@store/mangoStore'
|
||||||
import type { NextPage } from 'next'
|
import type { NextPage } from 'next'
|
||||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||||
|
@ -20,6 +25,23 @@ export async function getStaticProps({ locale }: { locale: string }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getOraclePriceForMarket = (
|
||||||
|
group: Group,
|
||||||
|
mkt: Serum3Market | PerpMarket
|
||||||
|
): number => {
|
||||||
|
let price: number
|
||||||
|
if (mkt instanceof Serum3Market) {
|
||||||
|
const baseBank = group.getFirstBankByTokenIndex(mkt.baseTokenIndex)
|
||||||
|
|
||||||
|
price = baseBank.uiPrice
|
||||||
|
} else if (mkt) {
|
||||||
|
price = mkt._uiPrice
|
||||||
|
} else {
|
||||||
|
price = 0
|
||||||
|
}
|
||||||
|
return price
|
||||||
|
}
|
||||||
|
|
||||||
const Trade: NextPage = () => {
|
const Trade: NextPage = () => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { name: marketName } = router.query
|
const { name: marketName } = router.query
|
||||||
|
@ -34,10 +56,15 @@ const Trade: NextPage = () => {
|
||||||
const mkt =
|
const mkt =
|
||||||
serumMarkets.find((m) => m.name === marketName) ||
|
serumMarkets.find((m) => m.name === marketName) ||
|
||||||
perpMarkets.find((m) => m.name === marketName)
|
perpMarkets.find((m) => m.name === marketName)
|
||||||
|
|
||||||
if (mkt) {
|
if (mkt) {
|
||||||
set((s) => {
|
set((s) => {
|
||||||
s.selectedMarket.name = marketName
|
s.selectedMarket.name = marketName
|
||||||
s.selectedMarket.current = mkt
|
s.selectedMarket.current = mkt
|
||||||
|
s.tradeForm = {
|
||||||
|
...DEFAULT_TRADE_FORM,
|
||||||
|
price: getOraclePriceForMarket(group, mkt).toString(),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { subscribeWithSelector } from 'zustand/middleware'
|
||||||
import { AnchorProvider, Wallet, web3 } from '@project-serum/anchor'
|
import { AnchorProvider, Wallet, web3 } from '@project-serum/anchor'
|
||||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js'
|
import { Connection, Keypair, PublicKey } from '@solana/web3.js'
|
||||||
import { OpenOrders, Order } from '@project-serum/serum/lib/market'
|
import { OpenOrders, Order } from '@project-serum/serum/lib/market'
|
||||||
import { Orderbook as SpotOrderBook } from '@project-serum/serum'
|
import { Orderbook } from '@project-serum/serum'
|
||||||
import { Wallet as WalletAdapter } from '@solana/wallet-adapter-react'
|
import { Wallet as WalletAdapter } from '@solana/wallet-adapter-react'
|
||||||
import {
|
import {
|
||||||
MangoClient,
|
MangoClient,
|
||||||
|
@ -33,7 +33,7 @@ import {
|
||||||
LAST_ACCOUNT_KEY,
|
LAST_ACCOUNT_KEY,
|
||||||
OUTPUT_TOKEN_DEFAULT,
|
OUTPUT_TOKEN_DEFAULT,
|
||||||
} from '../utils/constants'
|
} from '../utils/constants'
|
||||||
import { Orderbook, SpotBalances } from 'types'
|
import { OrderbookL2, SpotBalances } from 'types'
|
||||||
import spotBalancesUpdater from './spotBalancesUpdater'
|
import spotBalancesUpdater from './spotBalancesUpdater'
|
||||||
import { PerpMarket } from '@blockworks-foundation/mango-v4/'
|
import { PerpMarket } from '@blockworks-foundation/mango-v4/'
|
||||||
import perpPositionsUpdater from './perpPositionsUpdater'
|
import perpPositionsUpdater from './perpPositionsUpdater'
|
||||||
|
@ -175,6 +175,26 @@ export interface TokenStatsItem {
|
||||||
// wallet_pk: '',
|
// wallet_pk: '',
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
interface TradeForm {
|
||||||
|
side: 'buy' | 'sell'
|
||||||
|
price: string | undefined
|
||||||
|
baseSize: string
|
||||||
|
quoteSize: string
|
||||||
|
tradeType: 'Market' | 'Limit'
|
||||||
|
postOnly: boolean
|
||||||
|
ioc: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_TRADE_FORM: TradeForm = {
|
||||||
|
side: 'buy',
|
||||||
|
price: undefined,
|
||||||
|
baseSize: '',
|
||||||
|
quoteSize: '',
|
||||||
|
tradeType: 'Limit',
|
||||||
|
postOnly: false,
|
||||||
|
ioc: false,
|
||||||
|
}
|
||||||
|
|
||||||
export type MangoStore = {
|
export type MangoStore = {
|
||||||
activityFeed: {
|
activityFeed: {
|
||||||
feed: Array<DepositWithdrawFeedItem | LiquidationFeedItem>
|
feed: Array<DepositWithdrawFeedItem | LiquidationFeedItem>
|
||||||
|
@ -213,9 +233,9 @@ export type MangoStore = {
|
||||||
name: string
|
name: string
|
||||||
current: Serum3Market | PerpMarket | undefined
|
current: Serum3Market | PerpMarket | undefined
|
||||||
fills: any
|
fills: any
|
||||||
bidsAccount: BookSide | SpotOrderBook | undefined
|
bidsAccount: BookSide | Orderbook | undefined
|
||||||
asksAccount: BookSide | SpotOrderBook | undefined
|
asksAccount: BookSide | Orderbook | undefined
|
||||||
orderbook: Orderbook
|
orderbook: OrderbookL2
|
||||||
markPrice: number
|
markPrice: number
|
||||||
}
|
}
|
||||||
serumMarkets: Serum3Market[]
|
serumMarkets: Serum3Market[]
|
||||||
|
@ -243,15 +263,7 @@ export type MangoStore = {
|
||||||
loading: boolean
|
loading: boolean
|
||||||
data: TokenStatsItem[]
|
data: TokenStatsItem[]
|
||||||
}
|
}
|
||||||
tradeForm: {
|
tradeForm: TradeForm
|
||||||
side: 'buy' | 'sell'
|
|
||||||
price: string
|
|
||||||
baseSize: string
|
|
||||||
quoteSize: string
|
|
||||||
tradeType: 'Market' | 'Limit'
|
|
||||||
postOnly: boolean
|
|
||||||
ioc: boolean
|
|
||||||
}
|
|
||||||
wallet: {
|
wallet: {
|
||||||
tokens: TokenAccount[]
|
tokens: TokenAccount[]
|
||||||
nfts: {
|
nfts: {
|
||||||
|
@ -364,15 +376,7 @@ const mangoStore = create<MangoStore>()(
|
||||||
loading: false,
|
loading: false,
|
||||||
data: [],
|
data: [],
|
||||||
},
|
},
|
||||||
tradeForm: {
|
tradeForm: DEFAULT_TRADE_FORM,
|
||||||
side: 'buy',
|
|
||||||
price: '',
|
|
||||||
baseSize: '',
|
|
||||||
quoteSize: '',
|
|
||||||
tradeType: 'Limit',
|
|
||||||
postOnly: false,
|
|
||||||
ioc: false,
|
|
||||||
},
|
|
||||||
wallet: {
|
wallet: {
|
||||||
tokens: [],
|
tokens: [],
|
||||||
nfts: {
|
nfts: {
|
||||||
|
@ -673,6 +677,9 @@ const mangoStore = create<MangoStore>()(
|
||||||
const set = get().set
|
const set = get().set
|
||||||
const client = get().client
|
const client = get().client
|
||||||
const group = get().group
|
const group = get().group
|
||||||
|
if (!providedMangoAccount) {
|
||||||
|
await get().actions.reloadMangoAccount()
|
||||||
|
}
|
||||||
const mangoAccount =
|
const mangoAccount =
|
||||||
providedMangoAccount || get().mangoAccount.current
|
providedMangoAccount || get().mangoAccount.current
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ export interface ChartTradeType {
|
||||||
marketAddress: string
|
marketAddress: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Orderbook {
|
export interface OrderbookL2 {
|
||||||
bids: number[][]
|
bids: number[][]
|
||||||
asks: number[][]
|
asks: number[][]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Orderbook } from 'types'
|
import { OrderbookL2 } from 'types'
|
||||||
|
|
||||||
export const calculateMarketPrice = (
|
export const calculateLimitPriceForMarketOrder = (
|
||||||
orderBook: Orderbook,
|
orderBook: OrderbookL2,
|
||||||
size: number,
|
size: number,
|
||||||
side: 'buy' | 'sell'
|
side: 'buy' | 'sell'
|
||||||
): number => {
|
): number => {
|
||||||
|
@ -29,7 +29,7 @@ export const calculateMarketPrice = (
|
||||||
}
|
}
|
||||||
|
|
||||||
export const calculateSlippage = (
|
export const calculateSlippage = (
|
||||||
orderBook: Orderbook,
|
orderBook: OrderbookL2,
|
||||||
size: number,
|
size: number,
|
||||||
side: 'buy' | 'sell',
|
side: 'buy' | 'sell',
|
||||||
markPrice: number
|
markPrice: number
|
||||||
|
@ -39,7 +39,11 @@ export const calculateSlippage = (
|
||||||
const referencePrice = bb && ba ? (bb + ba) / 2 : markPrice
|
const referencePrice = bb && ba ? (bb + ba) / 2 : markPrice
|
||||||
|
|
||||||
if (Number(size)) {
|
if (Number(size)) {
|
||||||
const estimatedPrice = calculateMarketPrice(orderBook, Number(size), side)
|
const estimatedPrice = calculateLimitPriceForMarketOrder(
|
||||||
|
orderBook,
|
||||||
|
Number(size),
|
||||||
|
side
|
||||||
|
)
|
||||||
|
|
||||||
const slippageAbs =
|
const slippageAbs =
|
||||||
Number(size) > 0 ? Math.abs(estimatedPrice - referencePrice) : 0
|
Number(size) > 0 ? Math.abs(estimatedPrice - referencePrice) : 0
|
||||||
|
|
Loading…
Reference in New Issue