Mp 3442 sage feedback pre mars v 2 trade page (#520)
* MP-3442: set autolend to true by default * MP-3442: remember margin setting and toggle it on by default * MP-3442: add balances to AssetSelector * MP-3442: close assetOverlay on clickAway * fix: fixed TradeModule z-index * MP-3442: set priority of balance and cap * fix: overlayState * fix: adjusted to comments
This commit is contained in:
parent
8a71f4664a
commit
17f13b6d7c
@ -11,7 +11,7 @@ NEXT_PUBLIC_RED_BANK=osmo1vxpdcw092n9rngvekve8g324c2yjx56496ck98ag4sdxr4p4zd4q0w
|
||||
NEXT_PUBLIC_CREDIT_MANAGER=osmo1m83kw2vehyt9urxf79qa9rxk8chgs4464e5h8s37yhnw3pwauuqq7lux8r
|
||||
NEXT_PUBLIC_INCENTIVES=osmo1r9w7k774vcxeuvq6ctq0z2j6wkkxpskucgjkqt0uu7u07l03s3js6ukge4
|
||||
NEXT_PUBLIC_ZAPPER=osmo1q4kkvuy8wc9fs8sfm7zyeh4k25vssd0l68nrph8s7unvq5jdq67swrepj4
|
||||
NEXT_PUBLIC_SWAPPER=osmo1xmhhdxgk9e83n4kmtlluzx38mya8q9r4hku5nys8cr7jg7sgpx5s8zkkg2
|
||||
NEXT_PUBLIC_SWAPPER=osmo1wee0z8c7tcawyl647eapqs4a88q8jpa7ddy6nn2nrs7t47p2zhxswetwla
|
||||
NEXT_PUBLIC_PARAMS=osmo1pzszwkyy0x9cu6p2uknwa3wccr79xwmqn9gj66fnjnayr28tzp6qh2n4qg
|
||||
NEXT_PUBLIC_PYTH=osmo13ge29x4e2s63a8ytz2px8gurtyznmue4a69n5275692v3qn3ks8q7cwck7
|
||||
NEXT_PUBLIC_API=http://localhost:3000/api
|
||||
|
@ -111,11 +111,12 @@ export default function Index(props: Props) {
|
||||
)
|
||||
|
||||
const formattedAmount = formatAmountToPrecision(amount, MAX_AMOUNT_DECIMALS)
|
||||
const lowAmount = formattedAmount === 0 ? 0 : Math.max(formattedAmount, MIN_AMOUNT)
|
||||
return (
|
||||
<FormattedNumber
|
||||
className={className}
|
||||
smallerThanThreshold={formattedAmount < MIN_AMOUNT}
|
||||
amount={formattedAmount < MIN_AMOUNT ? MIN_AMOUNT : formattedAmount}
|
||||
smallerThanThreshold={formattedAmount !== 0 && formattedAmount < MIN_AMOUNT}
|
||||
amount={lowAmount}
|
||||
options={{
|
||||
maxDecimals: MAX_AMOUNT_DECIMALS,
|
||||
minDecimals: 0,
|
||||
|
@ -1,16 +1,20 @@
|
||||
import AssetImage from 'components/Asset/AssetImage'
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { StarFilled, StarOutlined } from 'components/Icons'
|
||||
import Text from 'components/Text'
|
||||
import { FAVORITE_ASSETS_KEY } from 'constants/localStore'
|
||||
import { BN_ONE } from 'constants/math'
|
||||
import { BN_ONE, BN_ZERO, MAX_AMOUNT_DECIMALS, MIN_AMOUNT } from 'constants/math'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { demagnify, formatAmountToPrecision } from 'utils/formatters'
|
||||
|
||||
interface Props {
|
||||
asset: Asset
|
||||
onSelectAsset: (asset: Asset) => void
|
||||
depositCap?: DepositCap
|
||||
balances: BNCoin[]
|
||||
}
|
||||
|
||||
export default function AssetItem(props: Props) {
|
||||
@ -19,6 +23,9 @@ export default function AssetItem(props: Props) {
|
||||
FAVORITE_ASSETS_KEY,
|
||||
[],
|
||||
)
|
||||
const amount = demagnify(props.balances.find(byDenom(asset.denom))?.amount ?? BN_ZERO, asset)
|
||||
const formattedAmount = formatAmountToPrecision(amount, MAX_AMOUNT_DECIMALS)
|
||||
const lowAmount = formattedAmount === 0 ? 0 : Math.max(formattedAmount, MIN_AMOUNT)
|
||||
|
||||
function handleToggleFavorite(event: React.MouseEvent<HTMLDivElement, MouseEvent>) {
|
||||
event.stopPropagation()
|
||||
@ -50,6 +57,30 @@ export default function AssetItem(props: Props) {
|
||||
<Text size='xs'>{asset.symbol}</Text>
|
||||
</div>
|
||||
</div>
|
||||
{props.balances.length > 0 && (
|
||||
<div className='flex gap-1'>
|
||||
<span className='text-xs text-left text-white/80'>Balance: </span>
|
||||
{amount >= 1 ? (
|
||||
<FormattedNumber
|
||||
className='text-xs text-left text-white/80'
|
||||
amount={amount}
|
||||
options={{ abbreviated: true, maxDecimals: MAX_AMOUNT_DECIMALS }}
|
||||
animate
|
||||
/>
|
||||
) : (
|
||||
<FormattedNumber
|
||||
className='text-xs text-left text-white/80'
|
||||
smallerThanThreshold={formattedAmount !== 0 && formattedAmount < MIN_AMOUNT}
|
||||
amount={lowAmount}
|
||||
options={{
|
||||
maxDecimals: MAX_AMOUNT_DECIMALS,
|
||||
minDecimals: 0,
|
||||
}}
|
||||
animate
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{props.depositCap && (
|
||||
<div className='flex gap-1'>
|
||||
<span className='text-xs text-left text-white/60'>Cap Left: </span>
|
||||
|
@ -3,8 +3,12 @@ import classNames from 'classnames'
|
||||
import { ChevronDown } from 'components/Icons'
|
||||
import Text from 'components/Text'
|
||||
import AssetItem from 'components/Trade/TradeModule/AssetSelector/AssetItem'
|
||||
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||
import useMarketAssets from 'hooks/useMarketAssets'
|
||||
import { useMemo } from 'react'
|
||||
import { getMergedBalancesForAsset } from 'utils/accounts'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { getEnabledMarketAssets } from 'utils/assets'
|
||||
|
||||
interface Props {
|
||||
type: 'buy' | 'sell'
|
||||
@ -15,12 +19,17 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function AssetList(props: Props) {
|
||||
const account = useCurrentAccount()
|
||||
const { data: marketAssets } = useMarketAssets()
|
||||
const balances = useMemo(() => {
|
||||
if (!account) return []
|
||||
return getMergedBalancesForAsset(account, getEnabledMarketAssets())
|
||||
}, [account])
|
||||
|
||||
return (
|
||||
<section>
|
||||
<button
|
||||
className='flex w-full items-center justify-between bg-black/20 p-4'
|
||||
className='flex items-center justify-between w-full p-4 bg-black/20'
|
||||
onClick={props.toggleOpen}
|
||||
>
|
||||
<Text>{props.type === 'buy' ? 'Buy asset' : 'Sell asset'}</Text>
|
||||
@ -35,6 +44,7 @@ export default function AssetList(props: Props) {
|
||||
<ul>
|
||||
{props.assets.map((asset) => (
|
||||
<AssetItem
|
||||
balances={balances}
|
||||
key={`${props.type}-${asset.symbol}`}
|
||||
asset={asset}
|
||||
onSelectAsset={props.onChangeAsset}
|
||||
|
@ -8,8 +8,6 @@ import Text from 'components/Text'
|
||||
import AssetList from 'components/Trade/TradeModule/AssetSelector/AssetList'
|
||||
import useFilteredAssets from 'hooks/useFilteredAssets'
|
||||
|
||||
export type OverlayState = 'buy' | 'sell' | 'closed'
|
||||
|
||||
interface Props {
|
||||
state: OverlayState
|
||||
buyAsset: Asset
|
||||
@ -21,7 +19,6 @@ interface Props {
|
||||
|
||||
export default function AssetOverlay(props: Props) {
|
||||
const { assets, searchString, onChangeSearch } = useFilteredAssets()
|
||||
|
||||
const handleClose = useCallback(() => props.onChangeState('closed'), [props])
|
||||
|
||||
const handleToggle = useCallback(
|
||||
@ -51,8 +48,8 @@ export default function AssetOverlay(props: Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Overlay className='w-full' show={props.state !== 'closed'} setShow={handleClose}>
|
||||
<div className='flex justify-between overflow-hidden p-4'>
|
||||
<Overlay className='inset-0 w-full' show={props.state !== 'closed'} setShow={handleClose}>
|
||||
<div className='flex justify-between p-4 overflow-hidden'>
|
||||
<Text>Select asset</Text>
|
||||
<EscButton onClick={handleClose} enableKeyPress />
|
||||
</div>
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
|
||||
import { SwapIcon } from 'components/Icons'
|
||||
import Text from 'components/Text'
|
||||
import AssetButton from 'components/Trade/TradeModule/AssetSelector/AssetButton'
|
||||
import AssetOverlay, { OverlayState } from 'components/Trade/TradeModule/AssetSelector/AssetOverlay'
|
||||
import AssetOverlay from 'components/Trade/TradeModule/AssetSelector/AssetOverlay'
|
||||
import useStore from 'store'
|
||||
|
||||
interface Props {
|
||||
buyAsset: Asset
|
||||
@ -14,7 +15,7 @@ interface Props {
|
||||
|
||||
export default function AssetSelector(props: Props) {
|
||||
const { buyAsset, sellAsset, onChangeBuyAsset, onChangeSellAsset } = props
|
||||
const [overlayState, setOverlayState] = useState<OverlayState>('closed')
|
||||
const assetOverlayState = useStore((s) => s.assetOverlayState)
|
||||
|
||||
const handleSwapAssets = useCallback(() => {
|
||||
onChangeBuyAsset(sellAsset)
|
||||
@ -24,7 +25,7 @@ export default function AssetSelector(props: Props) {
|
||||
const handleChangeBuyAsset = useCallback(
|
||||
(asset: Asset) => {
|
||||
onChangeBuyAsset(asset)
|
||||
setOverlayState('sell')
|
||||
useStore.setState({ assetOverlayState: 'sell' })
|
||||
},
|
||||
[onChangeBuyAsset],
|
||||
)
|
||||
@ -32,38 +33,41 @@ export default function AssetSelector(props: Props) {
|
||||
const handleChangeSellAsset = useCallback(
|
||||
(asset: Asset) => {
|
||||
onChangeSellAsset(asset)
|
||||
setOverlayState('closed')
|
||||
useStore.setState({ assetOverlayState: 'closed' })
|
||||
},
|
||||
[onChangeSellAsset],
|
||||
)
|
||||
|
||||
const handleChangeState = useCallback(
|
||||
(state: OverlayState) => {
|
||||
setOverlayState(state)
|
||||
},
|
||||
[setOverlayState],
|
||||
)
|
||||
const handleChangeState = useCallback((state: OverlayState) => {
|
||||
useStore.setState({ assetOverlayState: state })
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (overlayState === 'closed') {
|
||||
if (assetOverlayState === 'closed') {
|
||||
onChangeBuyAsset(buyAsset)
|
||||
onChangeSellAsset(sellAsset)
|
||||
}
|
||||
}, [onChangeBuyAsset, onChangeSellAsset, overlayState, buyAsset, sellAsset])
|
||||
}, [onChangeBuyAsset, onChangeSellAsset, assetOverlayState, buyAsset, sellAsset])
|
||||
|
||||
return (
|
||||
<div className='grid-rows-auto relative grid grid-cols-[1fr_min-content_1fr] gap-y-2 bg-white/5 p-3'>
|
||||
<div className='grid-rows-auto grid grid-cols-[1fr_min-content_1fr] gap-y-2 bg-white/5 p-3'>
|
||||
<Text size='sm'>Buy</Text>
|
||||
<Text size='sm' className='col-start-3'>
|
||||
Sell
|
||||
</Text>
|
||||
<AssetButton onClick={() => setOverlayState('buy')} asset={buyAsset} />
|
||||
<AssetButton
|
||||
onClick={() => useStore.setState({ assetOverlayState: 'buy' })}
|
||||
asset={buyAsset}
|
||||
/>
|
||||
<button onClick={handleSwapAssets}>
|
||||
<SwapIcon className='mx-2 w-4 place-self-center' />
|
||||
<SwapIcon className='w-4 mx-2 place-self-center' />
|
||||
</button>
|
||||
<AssetButton onClick={() => setOverlayState('sell')} asset={sellAsset} />
|
||||
<AssetButton
|
||||
onClick={() => useStore.setState({ assetOverlayState: 'sell' })}
|
||||
asset={sellAsset}
|
||||
/>
|
||||
<AssetOverlay
|
||||
state={overlayState}
|
||||
state={assetOverlayState}
|
||||
onChangeState={handleChangeState}
|
||||
buyAsset={buyAsset}
|
||||
sellAsset={sellAsset}
|
||||
|
@ -35,14 +35,15 @@ interface Props {
|
||||
|
||||
export default function SwapForm(props: Props) {
|
||||
const { buyAsset, sellAsset } = props
|
||||
const useMargin = useStore((s) => s.useMargin)
|
||||
const account = useCurrentAccount()
|
||||
const swap = useStore((s) => s.swap)
|
||||
const [slippage] = useLocalStorage(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
|
||||
const { computeMaxSwapAmount } = useHealthComputer(account)
|
||||
const { data: borrowAssets } = useMarketBorrowings()
|
||||
const { data: marketAssets } = useMarketAssets()
|
||||
|
||||
const [isMarginChecked, setMarginChecked] = useToggle()
|
||||
const isBorrowEnabled = !!marketAssets.find(byDenom(sellAsset.denom))?.borrowEnabled
|
||||
const [isMarginChecked, setMarginChecked] = useToggle(isBorrowEnabled ? useMargin : false)
|
||||
const [buyAssetAmount, setBuyAssetAmount] = useState(BN_ZERO)
|
||||
const [sellAssetAmount, setSellAssetAmount] = useState(BN_ZERO)
|
||||
const [maxBuyableAmountEstimation, setMaxBuyableAmountEstimation] = useState(BN_ZERO)
|
||||
@ -173,17 +174,33 @@ export default function SwapForm(props: Props) {
|
||||
[simulateTrade, isAutoLendEnabled],
|
||||
)
|
||||
|
||||
const handleMarginToggleChange = useCallback(
|
||||
(isMargin: boolean) => {
|
||||
if (isBorrowEnabled) useStore.setState({ useMargin: isMargin })
|
||||
setMarginChecked(isBorrowEnabled ? isMargin : false)
|
||||
},
|
||||
[isBorrowEnabled, setMarginChecked],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setBuyAssetAmount(BN_ZERO)
|
||||
setSellAssetAmount(BN_ZERO)
|
||||
setMarginChecked(false)
|
||||
setMarginChecked(isBorrowEnabled ? useMargin : false)
|
||||
simulateTrade(
|
||||
BNCoin.fromDenomAndBigNumber(buyAsset.denom, BN_ZERO),
|
||||
BNCoin.fromDenomAndBigNumber(sellAsset.denom, BN_ZERO),
|
||||
BNCoin.fromDenomAndBigNumber(sellAsset.denom, BN_ZERO),
|
||||
isAutoLendEnabled ? 'lend' : 'deposit',
|
||||
)
|
||||
}, [buyAsset.denom, sellAsset.denom, simulateTrade, isAutoLendEnabled, setMarginChecked])
|
||||
}, [
|
||||
isBorrowEnabled,
|
||||
useMargin,
|
||||
buyAsset.denom,
|
||||
sellAsset.denom,
|
||||
isAutoLendEnabled,
|
||||
simulateTrade,
|
||||
setMarginChecked,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
const removeDepositAmount = sellAssetAmount.isGreaterThanOrEqualTo(sellSideMarginThreshold)
|
||||
@ -229,7 +246,7 @@ export default function SwapForm(props: Props) {
|
||||
<Divider />
|
||||
<MarginToggle
|
||||
checked={isMarginChecked}
|
||||
onChange={setMarginChecked}
|
||||
onChange={handleMarginToggleChange}
|
||||
disabled={!borrowAsset?.isMarket}
|
||||
borrowAssetSymbol={sellAsset.symbol}
|
||||
/>
|
||||
|
@ -16,7 +16,7 @@ export default function TradeModule(props: Props) {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'relative isolate max-w-full overflow-hidden rounded-base pb-4',
|
||||
'relative isolate max-w-full overflow-hidden rounded-base pb-4 z-50',
|
||||
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-base before:p-[1px] before:border-glas',
|
||||
'h-full',
|
||||
)}
|
||||
|
@ -3,7 +3,7 @@ import { ORACLE_DENOM } from 'constants/oracle'
|
||||
|
||||
export const DEFAULT_SETTINGS: Settings = {
|
||||
reduceMotion: false,
|
||||
lendAssets: false,
|
||||
lendAssets: true,
|
||||
preferredAsset: ASSETS[0].denom,
|
||||
displayCurrency: ORACLE_DENOM,
|
||||
slippage: 0.02,
|
||||
|
@ -4,12 +4,14 @@ import MigrationBanner from 'components/MigrationBanner'
|
||||
import AccountDetailsCard from 'components/Trade/AccountDetailsCard'
|
||||
import TradeChart from 'components/Trade/TradeChart'
|
||||
import TradeModule from 'components/Trade/TradeModule'
|
||||
import useStore from 'store'
|
||||
import { getEnabledMarketAssets } from 'utils/assets'
|
||||
|
||||
export default function TradePage() {
|
||||
const enabledMarketAssets = getEnabledMarketAssets()
|
||||
const [buyAsset, setBuyAsset] = useState(enabledMarketAssets[0])
|
||||
const [sellAsset, setSellAsset] = useState(enabledMarketAssets[1])
|
||||
const assetOverlayState = useStore((s) => s.assetOverlayState)
|
||||
|
||||
return (
|
||||
<div className='flex flex-col w-full h-full gap-4'>
|
||||
@ -25,6 +27,13 @@ export default function TradePage() {
|
||||
<div />
|
||||
<AccountDetailsCard />
|
||||
</div>
|
||||
{assetOverlayState !== 'closed' && (
|
||||
<div
|
||||
className='fixed top-0 left-0 z-40 block w-full h-full hover:cursor-pointer'
|
||||
onClick={() => useStore.setState({ assetOverlayState: 'closed' })}
|
||||
role='button'
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -11,5 +11,6 @@ export default function createCommonSlice(set: SetState<CommonSlice>, get: GetSt
|
||||
accountDetailsExpanded: false,
|
||||
migrationBanner: true,
|
||||
tutorial: true,
|
||||
useMargin: true,
|
||||
}
|
||||
}
|
||||
|
@ -15,5 +15,6 @@ export default function createModalSlice(set: SetState<ModalSlice>, get: GetStat
|
||||
vaultModal: null,
|
||||
walletAssetsModal: null,
|
||||
withdrawFromVaultsModal: null,
|
||||
assetOverlayState: 'closed' as OverlayState,
|
||||
}
|
||||
}
|
||||
|
1
src/types/interfaces/components/AssetOverlay.d.ts
vendored
Normal file
1
src/types/interfaces/components/AssetOverlay.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
type OverlayState = 'buy' | 'sell' | 'closed'
|
1
src/types/interfaces/store/common.d.ts
vendored
1
src/types/interfaces/store/common.d.ts
vendored
@ -10,6 +10,7 @@ interface CommonSlice {
|
||||
accountDetailsExpanded: boolean
|
||||
migrationBanner: boolean
|
||||
tutorial: boolean
|
||||
useMargin: boolean
|
||||
}
|
||||
|
||||
interface FocusComponent {
|
||||
|
1
src/types/interfaces/store/modals.d.ts
vendored
1
src/types/interfaces/store/modals.d.ts
vendored
@ -11,6 +11,7 @@ interface ModalSlice {
|
||||
vaultModal: VaultModal | null
|
||||
walletAssetsModal: WalletAssetModal | null
|
||||
withdrawFromVaultsModal: DepositedVault[] | null
|
||||
assetOverlayState: OverlayState
|
||||
}
|
||||
|
||||
interface AlertDialogButton {
|
||||
|
Loading…
Reference in New Issue
Block a user