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:
parent
f46591be17
commit
f24d96ad75
@ -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",
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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 = (
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
/>
|
/>
|
||||||
|
@ -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
|
||||||
|
@ -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 = (
|
||||||
|
@ -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
|
||||||
|
@ -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' }}
|
||||||
|
@ -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 />
|
||||||
|
@ -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} />,
|
||||||
|
@ -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())}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
)
|
)
|
||||||
|
@ -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>
|
||||||
|
@ -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,25 +141,26 @@ 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',
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
|
>
|
||||||
|
<ChevronDown />
|
||||||
</div>
|
</div>
|
||||||
</SummaryLine>
|
</div>
|
||||||
<Divider className='my-2' />
|
{showSummary && (
|
||||||
</>
|
<>
|
||||||
<SummaryLine label={`Swap fees (${(swapFee || 0.002) * 100}%)`}>
|
<SummaryLine label={`Swap fees (${(swapFee || 0.002) * 100}%)`} className='mt-2'>
|
||||||
<DisplayCurrency coin={BNCoin.fromDenomAndBigNumber(sellAsset.denom, swapFeeValue)} />
|
<DisplayCurrency coin={BNCoin.fromDenomAndBigNumber(sellAsset.denom, swapFeeValue)} />
|
||||||
</SummaryLine>
|
</SummaryLine>
|
||||||
<SummaryLine label='Transaction fees'>
|
<SummaryLine label='Transaction fees'>
|
||||||
@ -149,11 +169,17 @@ export default function TradeSummary(props: Props) {
|
|||||||
<SummaryLine label={`Min receive (${slippage * 100}% slippage)`}>
|
<SummaryLine label={`Min receive (${slippage * 100}% slippage)`}>
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
amount={minReceive.toNumber()}
|
amount={minReceive.toNumber()}
|
||||||
options={{ decimals: buyAsset.decimals, suffix: ` ${buyAsset.symbol}`, maxDecimals: 6 }}
|
options={{
|
||||||
|
decimals: buyAsset.decimals,
|
||||||
|
suffix: ` ${buyAsset.symbol}`,
|
||||||
|
maxDecimals: 6,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</SummaryLine>
|
</SummaryLine>
|
||||||
<Divider className='my-2' />
|
<Divider className='my-2' />
|
||||||
<SummaryLine label='Route'>{parsedRoutes}</SummaryLine>
|
<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>
|
||||||
|
@ -12,15 +12,16 @@ export default function TradeModule(props: Props) {
|
|||||||
const { buyAsset, sellAsset } = props
|
const { buyAsset, sellAsset } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className='row-span-2'>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'relative isolate max-w-full overflow-hidden rounded-base pb-4 z-30',
|
'relative isolate max-w-full overflow-hidden rounded-base pb-4 z-30',
|
||||||
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-base before:p-[1px] before:border-glas',
|
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-base before:p-[1px] before:border-glas',
|
||||||
'h-full',
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<AssetSelector buyAsset={buyAsset} sellAsset={sellAsset} />
|
<AssetSelector buyAsset={buyAsset} sellAsset={sellAsset} />
|
||||||
<SwapForm buyAsset={buyAsset} sellAsset={sellAsset} />
|
<SwapForm buyAsset={buyAsset} sellAsset={sellAsset} />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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 })
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -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) {
|
||||||
|
@ -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,
|
||||||
|
@ -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' && (
|
||||||
|
8
src/types/custom.d.ts
vendored
8
src/types/custom.d.ts
vendored
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user