UX/UI finetuning (#672)

* feat: added Buy/Sell token ratio to the TradingView header

* fix: changed the order in the trading view description

* feat: added minute timeframe to the chart

* fix: changed WBTC to WBTC/USD pyth price feed

* fix: adjusted HLS health curve

* fix: made HLS accounts unselectable

* copy: changed the APY range and Strategy text

* tidy: fix the tables layout to be more readable

* fix: change the precision of the Trading chart header

* feat: added summary collapsable

* fix: removed Debt Column for active HLS positions

* fix: added Memo to TVChart

* fix: adjust Trade page layout

* tidy: refactor table meta

* fix: DisplayCurrency is able to take options now

* tidy: remove unneeded typesafety

* fix: adjusted according feedback

* env: enabled autoRepay and updated version
This commit is contained in:
Linkie Link 2023-12-04 11:51:58 +01:00 committed by GitHub
parent f46591be17
commit f24d96ad75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 219 additions and 95 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "mars-v2-frontend", "name": "mars-v2-frontend",
"version": "2.1.0", "version": "2.1.1",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "yarn validate-env && next build", "build": "yarn validate-env && next build",

View File

@ -39,7 +39,7 @@ export default function AccountDetailsController() {
const address = useStore((s) => s.address) const address = useStore((s) => s.address)
const isHLS = useStore((s) => s.isHLS) const isHLS = useStore((s) => s.isHLS)
const { data: accounts, isLoading } = useAccounts('default', address) const { data: accounts, isLoading } = useAccounts('default', address)
const { data: accountIds } = useAccountIds(address, false) const { data: accountIds } = useAccountIds(address, false, true)
const accountId = useAccountId() const accountId = useAccountId()
const account = useCurrentAccount() const account = useCurrentAccount()

View File

@ -31,7 +31,7 @@ export default function AccountMenuContent() {
const navigate = useNavigate() const navigate = useNavigate()
const { pathname } = useLocation() const { pathname } = useLocation()
const address = useStore((s) => s.address) const address = useStore((s) => s.address)
const { data: accountIds } = useAccountIds(address) const { data: accountIds } = useAccountIds(address, true, true)
const accountId = useAccountId() const accountId = useAccountId()
const createAccount = useStore((s) => s.createAccount) const createAccount = useStore((s) => s.createAccount)

View File

@ -1,7 +1,11 @@
import { FormattedNumber } from 'components/FormattedNumber' import { FormattedNumber } from 'components/FormattedNumber'
import Loading from 'components/Loading' import Loading from 'components/Loading'
export const BORROW_RATE_META = { accessorKey: 'borrowRate', header: 'Borrow Rate APY' } export const BORROW_RATE_META = {
accessorKey: 'borrowRate',
header: 'Borrow Rate APY',
meta: { className: 'w-40' },
}
interface Props { interface Props {
borrowRate: number | null borrowRate: number | null

View File

@ -11,6 +11,7 @@ export const LIQUIDITY_META = {
accessorKey: 'liquidity', accessorKey: 'liquidity',
header: 'Liquidity Available', header: 'Liquidity Available',
id: 'liquidity', id: 'liquidity',
meta: { className: 'w-40' },
} }
export const liquiditySortingFn = ( export const liquiditySortingFn = (

View File

@ -1,8 +1,11 @@
import React from 'react'
import { ChevronDown, ChevronUp } from 'components/Icons' import { ChevronDown, ChevronUp } from 'components/Icons'
export const MANAGE_META = { accessorKey: 'manage', enableSorting: false, header: 'Manage' } export const MANAGE_META = {
accessorKey: 'manage',
enableSorting: false,
header: 'Manage',
meta: { className: 'w-30' },
}
interface Props { interface Props {
isExpanded: boolean isExpanded: boolean

View File

@ -18,6 +18,7 @@ interface Props {
isApproximation?: boolean isApproximation?: boolean
parentheses?: boolean parentheses?: boolean
showZero?: boolean showZero?: boolean
options?: FormatOptions
} }
export default function DisplayCurrency(props: Props) { export default function DisplayCurrency(props: Props) {
@ -73,6 +74,7 @@ export default function DisplayCurrency(props: Props) {
abbreviated: true, abbreviated: true,
prefix, prefix,
suffix, suffix,
...props.options,
}} }}
animate animate
/> />

View File

@ -1,7 +1,7 @@
import AssetRate from 'components/Asset/AssetRate' import AssetRate from 'components/Asset/AssetRate'
import Loading from 'components/Loading' import Loading from 'components/Loading'
export const APY_META = { accessorKey: 'apy.deposit', header: 'APY' } export const APY_META = { accessorKey: 'apy.deposit', header: 'APY', meta: { className: 'w-40' } }
interface Props { interface Props {
apy: number apy: number

View File

@ -9,6 +9,7 @@ export const DEPOSIT_CAP_META = {
accessorKey: 'marketDepositCap', accessorKey: 'marketDepositCap',
header: 'Deposit Cap', header: 'Deposit Cap',
id: 'marketDepositCap', id: 'marketDepositCap',
meta: { className: 'w-40' },
} }
export const marketDepositCapSortingFn = ( export const marketDepositCapSortingFn = (

View File

@ -1,8 +1,13 @@
import React from 'react'
import { ChevronDown, ChevronUp } from 'components/Icons' import { ChevronDown, ChevronUp } from 'components/Icons'
export const MANAGE_META = { accessorKey: 'manage', enableSorting: false, header: 'Manage' } export const MANAGE_META = {
accessorKey: 'manage',
enableSorting: false,
header: 'Manage',
meta: {
className: 'w-30',
},
}
interface Props { interface Props {
isExpanded: boolean isExpanded: boolean

View File

@ -1,5 +1,4 @@
import { Row } from '@tanstack/react-table' import { Row } from '@tanstack/react-table'
import React from 'react'
import { FormattedNumber } from 'components/FormattedNumber' import { FormattedNumber } from 'components/FormattedNumber'
import Loading from 'components/Loading' import Loading from 'components/Loading'
@ -35,13 +34,13 @@ export default function ApyRange(props: Props) {
<TitleAndSubCell <TitleAndSubCell
title={ title={
<> <>
<FormattedNumber amount={minApy} options={{ suffix: ' - ' }} className='inline' /> <FormattedNumber amount={minApy} options={{ suffix: '% to ' }} className='inline' />
<FormattedNumber amount={maxApy} options={{ suffix: '%' }} className='inline' /> <FormattedNumber amount={maxApy} options={{ suffix: '%' }} className='inline' />
</> </>
} }
sub={ sub={
<> <>
<FormattedNumber amount={minApy / 365} options={{ suffix: '-' }} className='inline' /> <FormattedNumber amount={minApy / 365} options={{ suffix: '% to ' }} className='inline' />
<FormattedNumber <FormattedNumber
amount={maxApy / 365} amount={maxApy / 365}
options={{ suffix: '% daily' }} options={{ suffix: '% daily' }}

View File

@ -1,5 +1,3 @@
import React from 'react'
import DoubleLogo from 'components/DoubleLogo' import DoubleLogo from 'components/DoubleLogo'
import Loading from 'components/Loading' import Loading from 'components/Loading'
import TitleAndSubCell from 'components/TitleAndSubCell' import TitleAndSubCell from 'components/TitleAndSubCell'
@ -22,7 +20,7 @@ export default function Name(props: Props) {
<TitleAndSubCell <TitleAndSubCell
className='ml-2 mr-2 text-left' className='ml-2 mr-2 text-left'
title={`${depositAsset.symbol} - ${borrowAsset.symbol}`} title={`${depositAsset.symbol} - ${borrowAsset.symbol}`}
sub='Via MARS' sub='Via Mars Protocol'
/> />
) : ( ) : (
<Loading /> <Loading />

View File

@ -1,15 +1,11 @@
import { ColumnDef } from '@tanstack/react-table' import { ColumnDef } from '@tanstack/react-table'
import React, { useMemo } from 'react' import { useMemo } from 'react'
import Account, { ACCOUNT_META } from 'components/HLS/Staking/Table/Columns/Account' import Account, { ACCOUNT_META } from 'components/HLS/Staking/Table/Columns/Account'
import ActiveApy, { import ActiveApy, {
ACTIVE_APY_META, ACTIVE_APY_META,
activeApySortingFn, activeApySortingFn,
} from 'components/HLS/Staking/Table/Columns/ActiveApy' } from 'components/HLS/Staking/Table/Columns/ActiveApy'
import DebtValue, {
DEBT_VAL_META,
debtValueSorting,
} from 'components/HLS/Staking/Table/Columns/DebtValue'
import DepositCap, { import DepositCap, {
CAP_META, CAP_META,
depositCapSortingFn, depositCapSortingFn,
@ -59,11 +55,6 @@ export default function useDepositedColumns(props: Props) {
cell: ({ row }) => <NetValue account={row.original} />, cell: ({ row }) => <NetValue account={row.original} />,
sortingFn: netValueSorting, sortingFn: netValueSorting,
}, },
{
...DEBT_VAL_META,
cell: ({ row }) => <DebtValue account={row.original} />,
sortingFn: debtValueSorting,
},
{ {
...CAP_META, ...CAP_META,
cell: ({ row }) => <DepositCap account={row.original} />, cell: ({ row }) => <DepositCap account={row.original} />,

View File

@ -9,6 +9,7 @@ interface Props<T> {
rowClickHandler?: () => void rowClickHandler?: () => void
spacingClassName?: string spacingClassName?: string
isBalancesTable?: boolean isBalancesTable?: boolean
className?: string
} }
function getBorderColor(row: AccountBalanceRow) { function getBorderColor(row: AccountBalanceRow) {
@ -17,7 +18,6 @@ function getBorderColor(row: AccountBalanceRow) {
export default function Row<T>(props: Props<T>) { export default function Row<T>(props: Props<T>) {
const canExpand = !!props.renderExpanded const canExpand = !!props.renderExpanded
return ( return (
<> <>
<tr <tr
@ -47,6 +47,7 @@ export default function Row<T>(props: Props<T>) {
isSymbolOrName ? 'text-left' : 'text-right', isSymbolOrName ? 'text-left' : 'text-right',
props.spacingClassName ?? 'px-3 py-4', props.spacingClassName ?? 'px-3 py-4',
borderClasses, borderClasses,
cell.column.columnDef.meta?.className,
)} )}
> >
{flexRender(cell.column.columnDef.cell, cell.getContext())} {flexRender(cell.column.columnDef.cell, cell.getContext())}

View File

@ -68,6 +68,7 @@ export default function Table<T>(props: Props<T>) {
props.spacingClassName ?? 'px-4 py-3', props.spacingClassName ?? 'px-4 py-3',
header.column.getCanSort() && 'hover:cursor-pointer', header.column.getCanSort() && 'hover:cursor-pointer',
header.id === 'symbol' || header.id === 'name' ? 'text-left' : 'text-right', header.id === 'symbol' || header.id === 'name' ? 'text-left' : 'text-right',
header.column.columnDef.meta?.className,
)} )}
> >
<div <div

View File

@ -55,6 +55,8 @@ export class DataFeed implements IDatafeedChartApi {
'1D': '1d', '1D': '1d',
} }
millisecondsPerInterval: { [key: string]: number } = { millisecondsPerInterval: { [key: string]: number } = {
'1': MILLISECONDS_PER_MINUTE * 1,
'5': MILLISECONDS_PER_MINUTE * 5,
'15': MILLISECONDS_PER_MINUTE * 15, '15': MILLISECONDS_PER_MINUTE * 15,
'30': MILLISECONDS_PER_MINUTE * 30, '30': MILLISECONDS_PER_MINUTE * 30,
'60': MILLISECONDS_PER_MINUTE * 60, '60': MILLISECONDS_PER_MINUTE * 60,
@ -64,7 +66,7 @@ export class DataFeed implements IDatafeedChartApi {
pairs: { baseAsset: string; quoteAsset: string }[] = [] pairs: { baseAsset: string; quoteAsset: string }[] = []
pairsWithData: string[] = [] pairsWithData: string[] = []
supportedPools: string[] = [] supportedPools: string[] = []
supportedResolutions = ['15', '30', '60', '240', 'D'] as ResolutionString[] supportedResolutions = ['1', '5', '15', '30', '60', '240', 'D'] as ResolutionString[]
constructor(debug = false, baseDecimals: number, baseDenom: string) { constructor(debug = false, baseDecimals: number, baseDenom: string) {
if (debug) console.log('Start charting library datafeed') if (debug) console.log('Start charting library datafeed')
@ -78,12 +80,11 @@ export class DataFeed implements IDatafeedChartApi {
.filter((poolId) => typeof poolId === 'string') as string[] .filter((poolId) => typeof poolId === 'string') as string[]
} }
getDescription(pairName: string) { getDescription(pairName: string, inverted: boolean) {
const denom1 = pairName.split(PAIR_SEPARATOR)[0] const [denom1, denom2] = pairName.split(PAIR_SEPARATOR)
const denom2 = pairName.split(PAIR_SEPARATOR)[1]
const asset1 = ASSETS.find(byDenom(denom1)) const asset1 = ASSETS.find(byDenom(denom1))
const asset2 = ASSETS.find(byDenom(denom2)) const asset2 = ASSETS.find(byDenom(denom2))
return `${asset2?.symbol}/${asset1?.symbol}` return inverted ? `${asset2?.symbol}/${asset1?.symbol}` : `${asset1?.symbol}/${asset2?.symbol}`
} }
async getPairsWithData() { async getPairsWithData() {
@ -111,9 +112,10 @@ export class DataFeed implements IDatafeedChartApi {
.then((res) => res.json()) .then((res) => res.json())
.then((json) => { .then((json) => {
this.pairs = json.data.pairs this.pairs = json.data.pairs
this.pairsWithData = json.data.pairs.map( this.pairsWithData = json.data.pairs.map(
(pair: { baseAsset: string; quoteAsset: string }) => { (pair: { baseAsset: string; quoteAsset: string }) => {
return `${pair.baseAsset}${PAIR_SEPARATOR}${pair.quoteAsset}` return `${pair.quoteAsset}${PAIR_SEPARATOR}${pair.baseAsset}`
}, },
) )
}) })
@ -139,14 +141,14 @@ export class DataFeed implements IDatafeedChartApi {
setTimeout(() => { setTimeout(() => {
const info: LibrarySymbolInfo = { const info: LibrarySymbolInfo = {
...defaultSymbolInfo, ...defaultSymbolInfo,
name: this.getDescription(pairName), name: this.getDescription(pairName, false),
full_name: this.getDescription(pairName), full_name: this.getDescription(pairName, true),
description: this.getDescription(pairName), description: this.getDescription(pairName, true),
ticker: this.getDescription(pairName), ticker: this.getDescription(pairName, false),
exchange: this.getExchangeName(pairName), exchange: this.getExchangeName(pairName),
listed_exchange: this.getExchangeName(pairName), listed_exchange: this.getExchangeName(pairName),
supported_resolutions: this.supportedResolutions, supported_resolutions: this.supportedResolutions,
base_name: [this.getDescription(pairName)], base_name: [this.getDescription(pairName, false)],
pricescale: this.getPriceScale(pairName), pricescale: this.getPriceScale(pairName),
} as LibrarySymbolInfo } as LibrarySymbolInfo
onResolve(info) onResolve(info)
@ -470,7 +472,13 @@ export class DataFeed implements IDatafeedChartApi {
} }
getPythFeedIds(name: string) { getPythFeedIds(name: string) {
if (name.includes(PAIR_SEPARATOR)) return [] if (name.includes(PAIR_SEPARATOR)) {
const [denom1, denom2] = name.split(PAIR_SEPARATOR)
const denomFeedId1 = ASSETS.find((asset) => asset.denom === denom1)?.pythHistoryFeedId
const denomFeedId2 = ASSETS.find((asset) => asset.denom === denom2)?.pythHistoryFeedId
return [denomFeedId1, denomFeedId2]
}
const [symbol1, symbol2] = name.split('/') const [symbol1, symbol2] = name.split('/')
const feedId1 = ASSETS.find((asset) => asset.symbol === symbol1)?.pythHistoryFeedId const feedId1 = ASSETS.find((asset) => asset.symbol === symbol1)?.pythHistoryFeedId
const feedId2 = ASSETS.find((asset) => asset.symbol === symbol2)?.pythHistoryFeedId const feedId2 = ASSETS.find((asset) => asset.symbol === symbol2)?.pythHistoryFeedId

View File

@ -1,9 +1,17 @@
import { useEffect, useMemo, useRef } from 'react' import { useEffect, useMemo, useRef } from 'react'
import Card from 'components/Card' import Card from 'components/Card'
import { disabledFeatures, enabledFeatures, overrides } from 'components/Trade/TradeChart/constants' import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import Loading from 'components/Loading'
import Text from 'components/Text'
import { DataFeed, PAIR_SEPARATOR } from 'components/Trade/TradeChart/DataFeed' import { DataFeed, PAIR_SEPARATOR } from 'components/Trade/TradeChart/DataFeed'
import { disabledFeatures, enabledFeatures, overrides } from 'components/Trade/TradeChart/constants'
import { BN_ZERO } from 'constants/math'
import usePrices from 'hooks/usePrices'
import useStore from 'store' import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { import {
ChartingLibraryWidgetOptions, ChartingLibraryWidgetOptions,
IChartingLibraryWidget, IChartingLibraryWidget,
@ -11,6 +19,7 @@ import {
Timezone, Timezone,
widget, widget,
} from 'utils/charting_library' } from 'utils/charting_library'
import { magnify } from 'utils/formatters'
interface Props { interface Props {
buyAsset: Asset buyAsset: Asset
@ -28,6 +37,14 @@ export const TVChartContainer = (props: Props) => {
() => new DataFeed(false, baseCurrency.decimals, baseCurrency.denom), () => new DataFeed(false, baseCurrency.decimals, baseCurrency.denom),
[baseCurrency], [baseCurrency],
) )
const { data: prices, isLoading } = usePrices()
const ratio = useMemo(() => {
const priceBuyAsset = prices.find(byDenom(props.buyAsset.denom))?.amount
const priceSellAsset = prices.find(byDenom(props.sellAsset.denom))?.amount
if (!priceBuyAsset || !priceSellAsset) return BN_ZERO
return priceBuyAsset.dividedBy(priceSellAsset)
}, [prices, props.buyAsset.denom, props.sellAsset.denom])
useEffect(() => { useEffect(() => {
const widgetOptions: ChartingLibraryWidgetOptions = { const widgetOptions: ChartingLibraryWidgetOptions = {
@ -102,7 +119,45 @@ export const TVChartContainer = (props: Props) => {
}, [props.buyAsset.denom, props.sellAsset.denom]) }, [props.buyAsset.denom, props.sellAsset.denom])
return ( return (
<Card title='Trading Chart' contentClassName='px-0.5 pb-0.5 h-full' className='h-full'> <Card
title={
<div className='flex items-center w-full bg-white/10'>
<Text size='lg' className='flex items-center flex-1 p-4 font-semibold'>
Trading Chart
</Text>
{ratio.isZero() || isLoading ? (
<Loading className='h-4 mr-4 w-60' />
) : (
<div className='flex items-center gap-1 p-4'>
<Text size='sm'>1 {props.buyAsset.symbol}</Text>
<FormattedNumber
className='text-sm'
amount={Number(ratio.toPrecision(6))}
options={{
prefix: '= ',
suffix: ` ${props.sellAsset.symbol}`,
abbreviated: false,
maxDecimals: props.sellAsset.decimals,
}}
/>
<DisplayCurrency
parentheses
options={{ abbreviated: false }}
className='justify-end pl-2 text-sm text-white/50'
coin={
new BNCoin({
denom: props.buyAsset.denom,
amount: magnify(1, props.buyAsset).toString(),
})
}
/>
</div>
)}
</div>
}
contentClassName='px-0.5 pb-0.5 h-full'
className='min-h-[55vh]'
>
<div ref={chartContainerRef} className='h-full overflow-hidden rounded-b-base' /> <div ref={chartContainerRef} className='h-full overflow-hidden rounded-b-base' />
</Card> </Card>
) )

View File

@ -4,6 +4,8 @@ import { useState } from 'react'
import Card from 'components/Card' import Card from 'components/Card'
import { CircularProgress } from 'components/CircularProgress' import { CircularProgress } from 'components/CircularProgress'
import Loading from 'components/Loading'
import Text from 'components/Text'
const TVChartContainer = dynamic( const TVChartContainer = dynamic(
() => import('components/Trade/TradeChart/TVChartContainer').then((mod) => mod.TVChartContainer), () => import('components/Trade/TradeChart/TVChartContainer').then((mod) => mod.TVChartContainer),
@ -33,7 +35,18 @@ export default function TradeChart(props: Props) {
{isScriptReady ? ( {isScriptReady ? (
<TVChartContainer buyAsset={props.buyAsset} sellAsset={props.sellAsset} /> <TVChartContainer buyAsset={props.buyAsset} sellAsset={props.sellAsset} />
) : ( ) : (
<Card title='Trading Chart' contentClassName='px-0.5 pb-0.5 h-full'> <Card
title={
<div className='flex items-center w-full bg-white/10'>
<Text size='lg' className='flex items-center flex-1 p-4 font-semibold'>
Trading Chart
</Text>
<Loading className='h-4 mr-4 w-60' />
</div>
}
contentClassName='px-0.5 pb-0.5 h-full'
className='min-h-[55vh]'
>
<div className='flex items-center justify-center w-full h-full rounded-b-base bg-chart'> <div className='flex items-center justify-center w-full h-full rounded-b-base bg-chart'>
<CircularProgress size={60} className='opacity-50' /> <CircularProgress size={60} className='opacity-50' />
</div> </div>

View File

@ -7,11 +7,14 @@ import { CircularProgress } from 'components/CircularProgress'
import DisplayCurrency from 'components/DisplayCurrency' import DisplayCurrency from 'components/DisplayCurrency'
import Divider from 'components/Divider' import Divider from 'components/Divider'
import { FormattedNumber } from 'components/FormattedNumber' import { FormattedNumber } from 'components/FormattedNumber'
import { ChevronDown } from 'components/Icons'
import Text from 'components/Text'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings' import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys' import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLocalStorage from 'hooks/useLocalStorage' import useLocalStorage from 'hooks/useLocalStorage'
import usePrice from 'hooks/usePrice' import usePrice from 'hooks/usePrice'
import useSwapFee from 'hooks/useSwapFee' import useSwapFee from 'hooks/useSwapFee'
import useToggle from 'hooks/useToggle'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { getAssetByDenom } from 'utils/assets' import { getAssetByDenom } from 'utils/assets'
import { formatAmountWithSymbol, formatPercent } from 'utils/formatters' import { formatAmountWithSymbol, formatPercent } from 'utils/formatters'
@ -55,6 +58,7 @@ export default function TradeSummary(props: Props) {
const sellAssetPrice = usePrice(sellAsset.denom) const sellAssetPrice = usePrice(sellAsset.denom)
const swapFee = useSwapFee(route.map((r) => r.pool_id)) const swapFee = useSwapFee(route.map((r) => r.pool_id))
const [showSummary, setShowSummary] = useToggle()
const [liquidationPrice, setLiquidationPrice] = useState<number | null>(null) const [liquidationPrice, setLiquidationPrice] = useState<number | null>(null)
const [isUpdatingLiquidationPrice, setIsUpdatingLiquidationPrice] = useState(false) const [isUpdatingLiquidationPrice, setIsUpdatingLiquidationPrice] = useState(false)
const debouncedSetLiqPrice = useMemo( const debouncedSetLiqPrice = useMemo(
@ -99,7 +103,22 @@ export default function TradeSummary(props: Props) {
)} )}
> >
<div className='flex flex-col flex-1 m-3'> <div className='flex flex-col flex-1 m-3'>
<span className='mb-2 text-xs font-bold'>Summary</span> <SummaryLine label='Liquidation Price'>
<div className='flex h-2'>
{isUpdatingLiquidationPrice ? (
<CircularProgress className='opacity-50' />
) : liquidationPrice === null || liquidationPrice === 0 ? (
'-'
) : (
<FormattedNumber
className='inline'
amount={liquidationPrice}
options={{ abbreviated: true, prefix: `${props.buyAsset.symbol} = $ ` }}
/>
)}
</div>
</SummaryLine>
<Divider className='my-2' />
{isMargin && ( {isMargin && (
<> <>
<SummaryLine label='Borrowing'> <SummaryLine label='Borrowing'>
@ -122,38 +141,45 @@ export default function TradeSummary(props: Props) {
<Divider className='my-2' /> <Divider className='my-2' />
</> </>
)} )}
<> <div
<SummaryLine label='Liquidation Price'> className='relative w-full pr-4 hover:pointer'
<div className='flex h-2'> role='button'
{isUpdatingLiquidationPrice ? ( onClick={() => setShowSummary(!showSummary)}
<CircularProgress className='opacity-50' /> >
) : liquidationPrice === null || liquidationPrice === 0 ? ( <Text size='xs' className='font-bold'>
'-' Summary
) : ( </Text>
<FormattedNumber <div
className='inline' className={classNames(
amount={liquidationPrice} 'absolute right-0 w-3 text-center top-1',
options={{ abbreviated: true, prefix: `${props.buyAsset.symbol} = $ ` }} showSummary && 'rotate-180',
/> )}
)} >
</div> <ChevronDown />
</SummaryLine> </div>
<Divider className='my-2' /> </div>
</> {showSummary && (
<SummaryLine label={`Swap fees (${(swapFee || 0.002) * 100}%)`}> <>
<DisplayCurrency coin={BNCoin.fromDenomAndBigNumber(sellAsset.denom, swapFeeValue)} /> <SummaryLine label={`Swap fees (${(swapFee || 0.002) * 100}%)`} className='mt-2'>
</SummaryLine> <DisplayCurrency coin={BNCoin.fromDenomAndBigNumber(sellAsset.denom, swapFeeValue)} />
<SummaryLine label='Transaction fees'> </SummaryLine>
<span>{formatAmountWithSymbol(estimatedFee.amount[0])}</span> <SummaryLine label='Transaction fees'>
</SummaryLine> <span>{formatAmountWithSymbol(estimatedFee.amount[0])}</span>
<SummaryLine label={`Min receive (${slippage * 100}% slippage)`}> </SummaryLine>
<FormattedNumber <SummaryLine label={`Min receive (${slippage * 100}% slippage)`}>
amount={minReceive.toNumber()} <FormattedNumber
options={{ decimals: buyAsset.decimals, suffix: ` ${buyAsset.symbol}`, maxDecimals: 6 }} amount={minReceive.toNumber()}
/> options={{
</SummaryLine> decimals: buyAsset.decimals,
<Divider className='my-2' /> suffix: ` ${buyAsset.symbol}`,
<SummaryLine label='Route'>{parsedRoutes}</SummaryLine> maxDecimals: 6,
}}
/>
</SummaryLine>
<Divider className='my-2' />
<SummaryLine label='Route'>{parsedRoutes}</SummaryLine>
</>
)}
</div> </div>
<ActionButton <ActionButton
disabled={buyButtonDisabled} disabled={buyButtonDisabled}
@ -171,10 +197,11 @@ export default function TradeSummary(props: Props) {
interface SummaryLineProps { interface SummaryLineProps {
children: React.ReactNode children: React.ReactNode
label: string label: string
className?: string
} }
function SummaryLine(props: SummaryLineProps) { function SummaryLine(props: SummaryLineProps) {
return ( return (
<div className={infoLineClasses}> <div className={classNames(infoLineClasses, props.className)}>
<span className='opacity-40'>{props.label}</span> <span className='opacity-40'>{props.label}</span>
<span>{props.children}</span> <span>{props.children}</span>
</div> </div>

View File

@ -12,15 +12,16 @@ export default function TradeModule(props: Props) {
const { buyAsset, sellAsset } = props const { buyAsset, sellAsset } = props
return ( return (
<div <div className='row-span-2'>
className={classNames( <div
'relative isolate max-w-full overflow-hidden rounded-base pb-4 z-30', className={classNames(
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-base before:p-[1px] before:border-glas', 'relative isolate max-w-full overflow-hidden rounded-base pb-4 z-30',
'h-full', 'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-base before:p-[1px] before:border-glas',
)} )}
> >
<AssetSelector buyAsset={buyAsset} sellAsset={sellAsset} /> <AssetSelector buyAsset={buyAsset} sellAsset={sellAsset} />
<SwapForm buyAsset={buyAsset} sellAsset={sellAsset} /> <SwapForm buyAsset={buyAsset} sellAsset={sellAsset} />
</div>
</div> </div>
) )
} }

View File

@ -32,7 +32,11 @@ function Content() {
const urlAccountId = useAccountId() const urlAccountId = useAccountId()
const navigate = useNavigate() const navigate = useNavigate()
const { pathname } = useLocation() const { pathname } = useLocation()
const { data: accountIds, isLoading: isLoadingAccounts } = useAccountIds(address || '') const { data: accountIds, isLoading: isLoadingAccounts } = useAccountIds(
address || '',
true,
true,
)
const { data: walletBalances, isLoading: isLoadingBalances } = useWalletBalances(address) const { data: walletBalances, isLoading: isLoadingBalances } = useWalletBalances(address)
const baseAsset = getBaseAsset() const baseAsset = getBaseAsset()
@ -54,7 +58,9 @@ function Content() {
accountIds.length !== 0 && accountIds.length !== 0 &&
BN(baseBalance).isGreaterThanOrEqualTo(defaultFee.amount[0].amount) BN(baseBalance).isGreaterThanOrEqualTo(defaultFee.amount[0].amount)
) { ) {
navigate(getRoute(page, address, urlAccountId ?? accountIds[0])) const currentAccountIsHLS = urlAccountId && !accountIds.includes(urlAccountId)
const currentAccount = currentAccountIsHLS || !urlAccountId ? accountIds[0] : urlAccountId
navigate(getRoute(page, address, currentAccount))
useStore.setState({ balances: walletBalances, focusComponent: null }) useStore.setState({ balances: walletBalances, focusComponent: null })
} }
}, [ }, [

View File

@ -83,8 +83,8 @@ export const ASSETS: Asset[] = [
isDisplayCurrency: true, isDisplayCurrency: true,
isAutoLendEnabled: true, isAutoLendEnabled: true,
isBorrowEnabled: true, isBorrowEnabled: true,
pythPriceFeedId: 'e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43', pythPriceFeedId: 'c9d8b075a5c69303365ae23633d4e085199bf5c520a3b90fed1322a0342ffc33',
pythHistoryFeedId: 'Crypto.BTC/USD', pythHistoryFeedId: 'Crypto.WBTC/USD',
poolId: 712, poolId: 712,
}, },
{ {

View File

@ -4,7 +4,7 @@ import getAccountIds from 'api/wallets/getAccountIds'
export default function useAccountIdsAndKinds(address?: string, suspense = true, noHls = false) { export default function useAccountIdsAndKinds(address?: string, suspense = true, noHls = false) {
return useSWR( return useSWR(
`wallets/${address}/account-ids`, `wallets/${address}/account-ids${noHls && '-without-hls'}`,
() => () =>
getAccountIds(address).then((accountIdsAndKinds) => { getAccountIds(address).then((accountIdsAndKinds) => {
if (noHls) { if (noHls) {

View File

@ -216,8 +216,9 @@ export default function useHealthComputer(account?: Account) {
) )
const health = useMemo(() => { const health = useMemo(() => {
const slope = account?.kind === 'high_levered_strategy' ? 1.2 : 3.5
const convertedHealth = BN(Math.log(healthFactor)) const convertedHealth = BN(Math.log(healthFactor))
.dividedBy(Math.log(3.5)) .dividedBy(Math.log(slope))
.multipliedBy(100) .multipliedBy(100)
.integerValue() .integerValue()
.toNumber() .toNumber()
@ -226,7 +227,7 @@ export default function useHealthComputer(account?: Account) {
if (convertedHealth === 0 && healthFactor > 1) return 1 if (convertedHealth === 0 && healthFactor > 1) return 1
if (convertedHealth < 0) return 0 if (convertedHealth < 0) return 0
return convertedHealth return convertedHealth
}, [healthFactor]) }, [healthFactor, account?.kind])
return { return {
health, health,

View File

@ -34,7 +34,6 @@ export default function TradePage() {
<div className='grid h-full w-full grid-cols-[346px_auto] gap-4'> <div className='grid h-full w-full grid-cols-[346px_auto] gap-4'>
<TradeModule buyAsset={buyAsset} sellAsset={sellAsset} /> <TradeModule buyAsset={buyAsset} sellAsset={sellAsset} />
<TradeChart buyAsset={buyAsset} sellAsset={sellAsset} /> <TradeChart buyAsset={buyAsset} sellAsset={sellAsset} />
<div />
<AccountDetailsCard /> <AccountDetailsCard />
</div> </div>
{assetOverlayState !== 'closed' && ( {assetOverlayState !== 'closed' && (

View File

@ -1,4 +1,12 @@
import '@tanstack/react-table'
declare module '*.svg' { declare module '*.svg' {
const content: React.FunctionComponent<React.SVGAttributes<SVGElement>> const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>
export default content export default content
} }
declare module '@tanstack/table-core' {
interface ColumnMeta<TData extends RowData, TValue> {
className?: string
}
}

View File

@ -22,4 +22,4 @@ export const DEFAULT_PORTFOLIO_STATS = [
export const ENABLE_HLS = true export const ENABLE_HLS = true
export const ENABLE_PERPS = false export const ENABLE_PERPS = false
export const ENABLE_AUTO_REPAY = false export const ENABLE_AUTO_REPAY = true