This commit is contained in:
Linkie Link
2024-01-25 14:30:01 +01:00
committed by GitHub
parent 68c60c29df
commit f6f46d3372
678 changed files with 11366 additions and 8062 deletions
@@ -1,22 +0,0 @@
import Text from 'components/Text'
export const ASSET_META = { accessorKey: 'symbol', header: 'Asset', id: 'symbol' }
interface Props {
symbol: string
type: 'deposits' | 'borrowing' | 'lending' | 'vault'
}
export const borderColor = (type: Props['type']): string =>
type === 'borrowing' ? 'border-loss' : 'border-profit'
export default function Asset(props: Props) {
const { symbol, type } = props
return (
<Text size='xs'>
{symbol}
{type === 'borrowing' && <span className='ml-1 text-loss'>(debt)</span>}
{type === 'lending' && <span className='ml-1 text-profit'>(lent)</span>}
{type === 'vault' && <span className='ml-1 text-profit'>(farm)</span>}
</Text>
)
}
-50
View File
@@ -1,50 +0,0 @@
import classNames from 'classnames'
import { HTMLAttributes } from 'react'
import { FormattedNumber } from 'components/FormattedNumber'
import { InfoCircle } from 'components/Icons'
import Text from 'components/Text'
import { BNCoin } from 'types/classes/BNCoin'
import { getAssetByDenom } from 'utils/assets'
interface Props extends HTMLAttributes<HTMLDivElement> {
action: 'buy' | 'deposit' | 'fund'
coins: BNCoin[]
showIcon?: boolean
}
export default function DepositCapMessage(props: Props) {
if (!props.coins.length) return null
return (
<div className={classNames('flex items-start', props.className)}>
{props.showIcon && <InfoCircle width={26} className='mr-5' />}
<div className='flex flex-col gap-2'>
<Text size='sm'>Deposit Cap Reached!</Text>
<Text size='xs' className='text-white/40'>{`Unfortunately you're not able to ${
props.action
} more than the following amount${props.coins.length > 1 ? 's' : ''}:`}</Text>
{props.coins.map((coin) => {
const asset = getAssetByDenom(coin.denom)
if (!asset) return null
return (
<div key={coin.denom} className='flex gap-1'>
<Text size='xs'>Cap Left:</Text>
<FormattedNumber
amount={Math.max(coin.amount.toNumber(), 0)}
options={{
decimals: asset.decimals,
maxDecimals: asset.decimals,
suffix: ` ${asset.symbol}`,
}}
className='text-xs text-white/60'
/>
</div>
)
})}
</div>
</div>
)
}
-59
View File
@@ -1,59 +0,0 @@
import classNames from 'classnames'
import Text from 'components/Text'
interface Props {
direction: OrderDirection
onChangeDirection: (direction: OrderDirection) => void
asset?: Asset
}
export function DirectionSelect(props: Props) {
const hasAsset = props.asset
const directions: OrderDirection[] = hasAsset ? ['buy', 'sell'] : ['long', 'short']
return (
<div className='flex rounded-sm bg-black/20'>
<Direction
onClick={() => props.onChangeDirection(directions[0])}
direction={directions[0]}
isActive={props.direction === directions[0]}
asset={props.asset}
/>
<Direction
onClick={() => props.onChangeDirection(directions[1])}
direction={directions[1]}
isActive={props.direction === directions[1]}
asset={props.asset}
/>
</div>
)
}
interface DirectionProps {
direction: 'long' | 'short' | 'buy' | 'sell'
isActive: boolean
onClick: () => void
asset?: Asset
}
function Direction(props: DirectionProps) {
const classString = props.direction === 'long' || props.direction === 'buy' ? 'success' : 'error'
return (
<button
className={classNames(
'px-4 py-3 rounded-sm flex-1',
props.isActive && 'border bg-white/10',
`border-${classString}`,
)}
onClick={props.onClick}
>
<Text
className={classNames(
'text-center first-letter:uppercase',
props.isActive ? `text-${classString}` : 'text-white/20',
)}
>
{props.asset ? `${props.direction} ${props.asset.symbol}` : props.direction}
</Text>
</button>
)
}
-82
View File
@@ -1,82 +0,0 @@
import classNames from 'classnames'
import { useMemo } from 'react'
import { FormattedNumber } from 'components/FormattedNumber'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import { ORACLE_DENOM } from 'constants/oracle'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import { getDisplayCurrencies } from 'utils/assets'
import { getCoinValue } from 'utils/formatters'
import { BN } from 'utils/helpers'
interface Props {
coin: BNCoin
className?: string
isApproximation?: boolean
parentheses?: boolean
showZero?: boolean
options?: FormatOptions
}
export default function DisplayCurrency(props: Props) {
const displayCurrencies = getDisplayCurrencies()
const [displayCurrency] = useLocalStorage<string>(
LocalStorageKeys.DISPLAY_CURRENCY,
DEFAULT_SETTINGS.displayCurrency,
)
const { data: prices } = usePrices()
const displayCurrencyAsset = useMemo(
() =>
displayCurrencies.find((asset) => asset.denom === displayCurrency) ?? displayCurrencies[0],
[displayCurrency, displayCurrencies],
)
const isUSD = displayCurrencyAsset.id === 'USD'
const amount = useMemo(() => {
const coinValue = getCoinValue(props.coin, prices)
if (displayCurrency === ORACLE_DENOM) return coinValue.toNumber()
const displayDecimals = displayCurrencyAsset.decimals
const displayPrice = getCoinValue(
BNCoin.fromDenomAndBigNumber(displayCurrency, BN(1).shiftedBy(displayDecimals)),
prices,
)
return coinValue.div(displayPrice).toNumber()
}, [displayCurrency, displayCurrencyAsset.decimals, prices, props.coin])
const isLessThanACent = (isUSD && amount < 0.01 && amount > 0) || (amount === 0 && props.showZero)
const smallerThanPrefix = isLessThanACent ? '< ' : ''
const prefix = isUSD
? `${props.isApproximation ? '~ ' : smallerThanPrefix}$`
: `${props.isApproximation ? '~ ' : ''}`
const suffix = isUSD
? ''
: ` ${displayCurrencyAsset.symbol ? ` ${displayCurrencyAsset.symbol}` : ''}`
return (
<FormattedNumber
className={classNames(
props.className,
props.parentheses && 'before:content-["("] after:content-[")"]',
)}
amount={isLessThanACent ? 0.01 : amount}
options={{
minDecimals: isUSD ? 2 : 0,
maxDecimals: 2,
abbreviated: true,
prefix,
suffix,
...props.options,
}}
animate
/>
)
}
@@ -1,39 +0,0 @@
import { Suspense } from 'react'
import Card from 'components/Card'
import VaultCard from 'components/Earn/Farm/VaultCard'
import useVaults from 'hooks/useVaults'
function Content() {
const { data: vaults } = useVaults()
if (!vaults) return null
const featuredVaults = vaults.filter((vault) => vault.isFeatured)
if (!featuredVaults.length) return null
return (
<Card
title='Featured vaults'
className='w-full mb-4 h-fit bg-white/5'
contentClassName='grid grid-cols-3'
>
{featuredVaults.map((vault) => (
<VaultCard
key={vault.address}
vault={vault}
title={vault.name}
subtitle='Hot off the presses'
provider={vault.provider}
/>
))}
</Card>
)
}
export default function FeaturedVaults() {
return (
<Suspense fallback={null}>
<Content />
</Suspense>
)
}
-83
View File
@@ -1,83 +0,0 @@
import ActionButton from 'components/Button/ActionButton'
import DoubleLogo from 'components/DoubleLogo'
import Text from 'components/Text'
import TitleAndSubCell from 'components/TitleAndSubCell'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useStore from 'store'
import { getAssetByDenom } from 'utils/assets'
import { formatPercent, formatValue } from 'utils/formatters'
interface Props {
vault: Vault
title: string
subtitle: string
provider?: string
unbondingPeriod?: number
}
export default function VaultCard(props: Props) {
const currentAccount = useCurrentAccount()
function openVaultModal() {
useStore.setState({
vaultModal: {
vault: props.vault,
selectedBorrowDenoms: [props.vault.denoms.secondary],
isCreate: true,
},
})
}
return (
<div className='border-r-[1px] border-r-white/10 p-4'>
<div className='flex justify-between mb-8 align-center'>
<div>
<Text size='xs' className='mb-2 text-white/60'>
{props.subtitle}
</Text>
<span className='flex'>
<Text className='mr-2 font-bold'>{props.title}</Text>
{props.provider && (
<Text size='sm' className='text-white/60'>
via {props.provider}
</Text>
)}
</span>
</div>
<DoubleLogo
primaryDenom={props.vault.denoms.primary}
secondaryDenom={props.vault.denoms.secondary}
/>
</div>
<div className='flex justify-between mb-6'>
<TitleAndSubCell
className='text-xs'
title={props.vault.apy ? formatPercent(props.vault.apy, 2) : '-'}
sub={'APY'}
/>
<TitleAndSubCell
className='text-xs'
title={`${props.vault.lockup.duration} ${props.vault.lockup.timeframe}`}
sub={'Lockup'}
/>
<TitleAndSubCell
className='text-xs'
title={formatValue(props.vault.cap.used.integerValue().toNumber() || '0', {
abbreviated: true,
decimals: getAssetByDenom(props.vault.cap.denom)?.decimals,
})}
sub={'TVL'}
/>
<TitleAndSubCell
className='text-xs'
title={formatValue(props.vault.cap.max.integerValue().toNumber() || '0', {
abbreviated: true,
decimals: getAssetByDenom(props.vault.cap.denom)?.decimals,
})}
sub={'Deposit Cap'}
/>
</div>
<ActionButton onClick={openVaultModal} color='secondary' text='Deposit' className='w-full' />
</div>
)
}
-9
View File
@@ -1,9 +0,0 @@
<svg viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M1.8335 6.00065H11.1668M11.1668 6.00065L6.50016 1.33398M11.1668 6.00065L6.50016 10.6673"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>

Before

Width:  |  Height:  |  Size: 291 B

-68
View File
@@ -1,68 +0,0 @@
// @index(['./*.svg'], f => `export { default as ${f.name} } from 'components/Icons/${f.name}.svg'`)
export { default as Account } from 'components/Icons/Account.svg'
export { default as AccountArrowDown } from 'components/Icons/AccountArrowDown.svg'
export { default as ArrowChartLineUp } from 'components/Icons/ArrowChartLineUp.svg'
export { default as ArrowCircle } from 'components/Icons/ArrowCircle.svg'
export { default as ArrowCircledTopRight } from 'components/Icons/ArrowCircledTopRight.svg'
export { default as ArrowDownLine } from 'components/Icons/ArrowDownLine.svg'
export { default as ArrowRight } from 'components/Icons/ArrowRight.svg'
export { default as ArrowUpLine } from 'components/Icons/ArrowUpLine.svg'
export { default as Chain } from 'components/Icons/Chain.svg'
export { default as Check } from 'components/Icons/Check.svg'
export { default as CheckCircled } from 'components/Icons/CheckCircled.svg'
export { default as ChevronDown } from 'components/Icons/ChevronDown.svg'
export { default as ChevronLeft } from 'components/Icons/ChevronLeft.svg'
export { default as ChevronRight } from 'components/Icons/ChevronRight.svg'
export { default as ChevronUp } from 'components/Icons/ChevronUp.svg'
export { default as Circle } from 'components/Icons/Circle.svg'
export { default as Coins } from 'components/Icons/Coins.svg'
export { default as CoinsSwap } from 'components/Icons/CoinsSwap.svg'
export { default as Compass } from 'components/Icons/Compass.svg'
export { default as Copy } from 'components/Icons/Copy.svg'
export { default as Cross } from 'components/Icons/Cross.svg'
export { default as CrossCircled } from 'components/Icons/CrossCircled.svg'
export { default as Enter } from 'components/Icons/Enter.svg'
export { default as ExclamationMarkCircled } from 'components/Icons/ExclamationMarkCircled.svg'
export { default as ExclamationMarkTriangle } from 'components/Icons/ExclamationMarkTriangle.svg'
export { default as ExternalLink } from 'components/Icons/ExternalLink.svg'
export { default as Flag } from 'components/Icons/Flag.svg'
export { default as Gear } from 'components/Icons/Gear.svg'
export { default as GridGlobe } from 'components/Icons/GridGlobe.svg'
export { default as GridHole } from 'components/Icons/GridHole.svg'
export { default as GridLandscape } from 'components/Icons/GridLandscape.svg'
export { default as GridPlanet } from 'components/Icons/GridPlanet.svg'
export { default as GridTire } from 'components/Icons/GridTire.svg'
export { default as GridWeb } from 'components/Icons/GridWeb.svg'
export { default as HandCoins } from 'components/Icons/HandCoins.svg'
export { default as Heart } from 'components/Icons/Heart.svg'
export { default as InfoCircle } from 'components/Icons/InfoCircle.svg'
export { default as LockLocked } from 'components/Icons/LockLocked.svg'
export { default as LockUnlocked } from 'components/Icons/LockUnlocked.svg'
export { default as Logo } from 'components/Icons/Logo.svg'
export { default as Luggage } from 'components/Icons/Luggage.svg'
export { default as MarsProtocol } from 'components/Icons/MarsProtocol.svg'
export { default as Osmo } from 'components/Icons/Osmo.svg'
export { default as OverlayMark } from 'components/Icons/OverlayMark.svg'
export { default as Plus } from 'components/Icons/Plus.svg'
export { default as PlusCircled } from 'components/Icons/PlusCircled.svg'
export { default as PlusSquared } from 'components/Icons/PlusSquared.svg'
export { default as PythLogoType } from 'components/Icons/PythLogoType.svg'
export { default as Questionmark } from 'components/Icons/Questionmark.svg'
export { default as ReceiptCheck } from 'components/Icons/ReceiptCheck.svg'
export { default as Scale } from 'components/Icons/Scale.svg'
export { default as Search } from 'components/Icons/Search.svg'
export { default as Shield } from 'components/Icons/Shield.svg'
export { default as SortAsc } from 'components/Icons/SortAsc.svg'
export { default as SortDesc } from 'components/Icons/SortDesc.svg'
export { default as SortNone } from 'components/Icons/SortNone.svg'
export { default as StarFilled } from 'components/Icons/StarFilled.svg'
export { default as StarOutlined } from 'components/Icons/StarOutlined.svg'
export { default as Subtract } from 'components/Icons/Subtract.svg'
export { default as SwapIcon } from 'components/Icons/SwapIcon.svg'
export { default as ThreeDots } from 'components/Icons/ThreeDots.svg'
export { default as TooltipArrow } from 'components/Icons/TooltipArrow.svg'
export { default as TrashBin } from 'components/Icons/TrashBin.svg'
export { default as Twitter } from 'components/Icons/Twitter.svg'
export { default as VerticalThreeLine } from 'components/Icons/VerticalThreeLine.svg'
export { default as Wallet } from 'components/Icons/Wallet.svg'
// @endindex
@@ -1,6 +1,6 @@
import { useEffect } from 'react'
import { Enter, InfoCircle } from 'components/Icons'
import { Enter, InfoCircle } from 'components/common/Icons'
import useAlertDialog from 'hooks/useAlertDialog'
interface Props {
@@ -1,13 +1,14 @@
import { useCallback, useMemo } from 'react'
import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom'
import AssetBalanceRow from 'components/Asset/AssetBalanceRow'
import { ArrowRight, ExclamationMarkCircled } from 'components/Icons'
import AssetBalanceRow from 'components/common/assets/AssetBalanceRow'
import { ArrowRight, ExclamationMarkCircled } from 'components/common/Icons'
import AccountDeleteAlertDialog from 'components/Modals/Account/AccountDeleteAlertDialog'
import Text from 'components/Text'
import Text from 'components/common/Text'
import useAllAssets from 'hooks/assets/useAllAssets'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { getAssetByDenom } from 'utils/assets'
import { byDenom } from 'utils/array'
import { combineBNCoins } from 'utils/parsers'
import { getPage, getRoute } from 'utils/route'
@@ -31,7 +32,7 @@ function AccountDeleteModal(props: Props) {
const { address } = useParams()
const { debts, vaults, id: accountId } = modal || {}
const [searchParams] = useSearchParams()
const assets = useAllAssets()
const closeDeleteAccountModal = useCallback(() => {
useStore.setState({ accountDeleteModal: null })
}, [])
@@ -117,7 +118,7 @@ function AccountDeleteModal(props: Props) {
<div className='flex flex-col w-full gap-4 py-4 overflow-y-scroll max-h-100 scrollbar-hide'>
{depositsAndLends.map((position, index) => {
const coin = BNCoin.fromDenomAndBigNumber(position.denom, position.amount)
const asset = getAssetByDenom(position.denom)
const asset = assets.find(byDenom(position.denom))
if (!asset) return null
return <AssetBalanceRow key={index} asset={asset} coin={coin} />
})}
@@ -1,9 +1,9 @@
import { useCallback, useMemo, useState } from 'react'
import AssetSelectTable from 'components/Modals/AssetsSelect/AssetSelectTable'
import SearchBar from 'components/SearchBar'
import Text from 'components/Text'
import useMarketBorrowings from 'hooks/useMarketBorrowings'
import AssetsSelect from 'components/Modals/AssetsSelect'
import SearchBar from 'components/common/SearchBar'
import Text from 'components/common/Text'
import useMarketBorrowings from 'hooks/markets/useMarketBorrowings'
import useStore from 'store'
interface Props {
@@ -89,11 +89,11 @@ export default function AddVaultAssetsModalContent(props: Props) {
Leverage will be set at 50% for both assets by default
</Text>
</div>
<AssetSelectTable
isBorrow={true}
<AssetsSelect
assets={poolAssets}
onChangeSelected={onChangePoolDenoms}
selectedDenoms={selectedPoolDenoms}
isBorrow
/>
<div className='p-4'>
<Text>Assets not in the liquidity pool</Text>
@@ -102,11 +102,11 @@ export default function AddVaultAssetsModalContent(props: Props) {
these assets below.
</Text>
</div>
<AssetSelectTable
isBorrow={true}
<AssetsSelect
assets={stableAssets}
onChangeSelected={onChangeOtherDenoms}
selectedDenoms={selectedOtherDenoms}
isBorrow
/>
</div>
</>
@@ -1,10 +1,10 @@
import { useCallback, useState } from 'react'
import Button from 'components/Button'
import { CircularProgress } from 'components/CircularProgress'
import Modal from 'components/Modal'
import Button from 'components/common/Button'
import { CircularProgress } from 'components/common/CircularProgress'
import Modal from 'components/Modals/Modal'
import AddVaultAssetsModalContent from 'components/Modals/AddVaultAssets/AddVaultBorrowAssetsModalContent'
import Text from 'components/Text'
import Text from 'components/common/Text'
import useStore from 'store'
export default function AddVaultBorrowAssetsModal() {
@@ -1,4 +1,4 @@
import { Enter } from 'components/Icons'
import { Enter } from 'components/common/Icons'
export function NoIcon() {
return (
+4 -4
View File
@@ -1,10 +1,10 @@
import classNames from 'classnames'
import Button from 'components/Button'
import Checkbox from 'components/Checkbox'
import Modal from 'components/Modal'
import Button from 'components/common/Button'
import Checkbox from 'components/common/Checkbox'
import Modal from 'components/Modals/Modal'
import { NoIcon, YesIcon } from 'components/Modals/AlertDialog/ButtonIcons'
import Text from 'components/Text'
import Text from 'components/common/Text'
import useAlertDialog from 'hooks/useAlertDialog'
import useToggle from 'hooks/useToggle'
@@ -1,22 +1,23 @@
import { useCallback, useState } from 'react'
import CurrentAccountSummary from 'components/Account/CurrentAccountSummary'
import AssetImage from 'components/Asset/AssetImage'
import Button from 'components/Button'
import Card from 'components/Card'
import Divider from 'components/Divider'
import { ArrowRight } from 'components/Icons'
import Modal from 'components/Modal'
import Text from 'components/Text'
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import Modal from 'components/Modals/Modal'
import CurrentAccountSummary from 'components/account/CurrentAccountSummary'
import Button from 'components/common/Button'
import Card from 'components/common/Card'
import Divider from 'components/common/Divider'
import { ArrowRight } from 'components/common/Icons'
import Text from 'components/common/Text'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import AssetImage from 'components/common/assets/AssetImage'
import { BN_ZERO } from 'constants/math'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { BN } from 'utils/helpers'
interface Props {
asset: Asset
title: string
coinBalances: Coin[]
coinBalances: BNCoin[]
actionButtonText: string
contentHeader?: JSX.Element
onClose: () => void
@@ -1,154 +0,0 @@
import {
flexRender,
getCoreRowModel,
getSortedRowModel,
RowSelectionState,
SortingState,
useReactTable,
} from '@tanstack/react-table'
import classNames from 'classnames'
import { useEffect, useMemo, useState } from 'react'
import { SortAsc, SortDesc, SortNone } from 'components/Icons'
import useAssetTableColumns from 'components/Modals/AssetsSelect/useAssetTableColumns'
import Text from 'components/Text'
import useMarketAssets from 'hooks/useMarketAssets'
import useStore from 'store'
import { byDenom } from 'utils/array'
interface Props {
assets: Asset[] | BorrowAsset[]
selectedDenoms: string[]
onChangeSelected: (denoms: string[]) => void
isBorrow: boolean
}
export default function AssetSelectTable(props: Props) {
const { data: markets } = useMarketAssets()
const defaultSelected = useMemo(() => {
const assets = props.assets as BorrowAsset[]
return assets.reduce(
(acc, asset, index) => {
if (props.selectedDenoms?.includes(asset.denom)) {
acc[index] = true
}
return acc
},
{} as { [key: number]: boolean },
)
}, [props.selectedDenoms, props.assets])
const [sorting, setSorting] = useState<SortingState>([{ id: 'symbol', desc: false }])
const [selected, setSelected] = useState<RowSelectionState>(defaultSelected)
const balances = useStore((s) => s.balances)
const columns = useAssetTableColumns(props.isBorrow)
const tableData: AssetTableRow[] = useMemo(() => {
return props.assets.map((asset) => {
const balancesForAsset = balances.find(byDenom(asset.denom))
return {
asset,
balance: balancesForAsset?.amount ?? '0',
market: markets.find((market) => market.denom === asset.denom),
}
})
}, [balances, props.assets, markets])
const table = useReactTable({
data: tableData,
columns,
state: {
sorting,
rowSelection: selected,
},
onRowSelectionChange: setSelected,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
})
useEffect(() => {
const newSelectedDenoms = props.assets
.filter((_, index) => selected[index])
.map((asset) => asset.denom)
if (
props.selectedDenoms.length === newSelectedDenoms.length &&
newSelectedDenoms.every((denom) => props.selectedDenoms.includes(denom))
)
return
props.onChangeSelected(newSelectedDenoms)
}, [selected, props])
return (
<table className='w-full'>
<thead className='border-b border-white/10'>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<th
key={header.id}
onClick={header.column.getToggleSortingHandler()}
className={classNames(
'p-2',
header.column.getCanSort() && 'hover:cursor-pointer',
header.id === 'symbol' ? 'text-left' : 'text-right',
)}
>
<div
className={classNames(
'flex',
header.id === 'symbol' ? 'justify-start' : 'justify-end',
'align-center',
)}
>
<span className='w-6 h-6 text-white'>
{header.column.getCanSort()
? {
asc: <SortAsc />,
desc: <SortDesc />,
false: <SortNone />,
}[header.column.getIsSorted() as string] ?? null
: null}
</span>
<Text
tag='span'
size='sm'
className='flex items-center font-normal text-white/40'
>
{flexRender(header.column.columnDef.header, header.getContext())}
</Text>
</div>
</th>
)
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => {
return (
<tr
key={row.id}
className='hover:cursor-pointer text-white/60'
onClick={() => row.toggleSelected()}
>
{row.getVisibleCells().map((cell) => {
return (
<td
key={cell.id}
className={classNames(
cell.column.id === 'select' ? `` : 'text-right',
'px-4 py-3',
)}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
)
}
@@ -0,0 +1,55 @@
import { Row } from '@tanstack/react-table'
import Checkbox from 'components/common/Checkbox'
import Text from 'components/common/Text'
import AssetImage from 'components/common/assets/AssetImage'
import AssetRate from 'components/common/assets/AssetRate'
export const ASSET_META = { id: 'name', header: 'Asset', accessorKey: 'asset.symbol' }
interface Props {
row: Row<AssetTableRow>
}
function isBorrowAsset(object?: any): object is BorrowAsset {
if (!object) return false
return 'borrowRate' in object
}
export default function Asset(props: Props) {
const { row } = props
const asset = row.original.asset
const market = row.original.market
const isBorrow = isBorrowAsset(asset)
const showRate = !isBorrow && market?.borrowEnabled
const apy = isBorrow ? market?.apy.borrow : market?.apy.deposit
return (
<div className='flex items-center'>
<Checkbox
name={`asset-${asset.id.toLowerCase()}`}
checked={row.getIsSelected()}
onChange={row.getToggleSelectedHandler()}
noMouseEvents
/>
<AssetImage asset={asset} size={24} className='ml-4' />
<div className='ml-2 text-left'>
<Text size='sm' className='mb-0.5 text-white'>
{asset.symbol}
</Text>
{showRate && market ? (
<AssetRate
rate={apy ?? 0}
isEnabled={market.borrowEnabled}
className='text-xs'
type='apy'
orientation='rtl'
suffix
/>
) : (
<Text size='xs'>{asset.name}</Text>
)}
</div>
</div>
)
}
@@ -0,0 +1,38 @@
import { Row } from '@tanstack/react-table'
import DisplayCurrency from 'components/common/DisplayCurrency'
import { FormattedNumber } from 'components/common/FormattedNumber'
import { BN_ZERO } from 'constants/math'
import { BNCoin } from 'types/classes/BNCoin'
import { demagnify } from 'utils/formatters'
import { BN } from 'utils/helpers'
export const BALANCE_META = { id: 'value', header: 'Balance', accessorKey: 'value' }
interface Props {
row: Row<AssetTableRow>
}
export const valueSortingFn = (a: Row<AssetTableRow>, b: Row<AssetTableRow>): number => {
const valueA = a.original.value ?? BN_ZERO
const valueB = b.original.value ?? BN_ZERO
return valueA.minus(valueB).toNumber()
}
export default function Balance(props: Props) {
const { row } = props
const asset = row.original.asset
const balance = BN(row.original.balance ?? '0')
const coin = BNCoin.fromDenomAndBigNumber(asset.denom, balance)
return (
<div className='flex flex-wrap items-center'>
<DisplayCurrency coin={coin} className='mb-0.5 w-full text-white' />
<FormattedNumber
className='w-full text-xs'
options={{ minDecimals: 2, maxDecimals: asset.decimals }}
amount={demagnify(balance, asset)}
/>
</div>
)
}
@@ -0,0 +1,25 @@
import { Row } from '@tanstack/react-table'
import Text from 'components/common/Text'
import { formatPercent } from 'utils/formatters'
export const BORROW_RATE_META = {
id: 'asset.borrowRate',
header: 'BorrowRate',
accessorKey: 'asset.borrowRate',
}
interface Props {
row: Row<AssetTableRow>
}
export default function BorrowRate(props: Props) {
const { row } = props
const asset = row.original.asset as BorrowAsset
return (
<Text size='sm' className='mb-0.5 text-white'>
{formatPercent(asset.borrowRate ?? 0)}
</Text>
)
}
@@ -0,0 +1,30 @@
import { ColumnDef } from '@tanstack/react-table'
import { useMemo } from 'react'
import Asset, { ASSET_META } from 'components/Modals/AssetsSelect/Columns/Asset'
import Balance, {
BALANCE_META,
valueSortingFn,
} from 'components/Modals/AssetsSelect/Columns/Balance'
import BorrowRate, { BORROW_RATE_META } from 'components/Modals/AssetsSelect/Columns/BorrowRate'
export default function useAssetSelectColumns(isBorrow?: boolean) {
return useMemo<ColumnDef<AssetTableRow>[]>(() => {
return [
{
...ASSET_META,
cell: ({ row }) => <Asset row={row} />,
},
isBorrow
? {
...BORROW_RATE_META,
cell: ({ row }) => <BorrowRate row={row} />,
}
: {
...BALANCE_META,
cell: ({ row }) => <Balance row={row} />,
sortingFn: valueSortingFn,
},
]
}, [isBorrow])
}
@@ -0,0 +1,81 @@
import { RowSelectionState } from '@tanstack/react-table'
import { useEffect, useMemo, useState } from 'react'
import useAssetSelectColumns from 'components/Modals/AssetsSelect/Columns/useAssetSelectColumns'
import Table from 'components/common/Table'
import useGetCoinValue from 'hooks/assets/useGetCoinValue'
import useMarketAssets from 'hooks/markets/useMarketAssets'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { BN } from 'utils/helpers'
interface Props {
assets: Asset[]
onChangeSelected: (selected: string[]) => void
selectedDenoms: string[]
isBorrow?: boolean
}
export default function AssetsSelect(props: Props) {
const { assets, onChangeSelected, selectedDenoms, isBorrow } = props
const columns = useAssetSelectColumns(isBorrow)
const { data: markets } = useMarketAssets()
const getCoinValue = useGetCoinValue()
const defaultSelected = useMemo(() => {
const selectableAssets = assets
return selectableAssets.reduce(
(acc, asset, index) => {
if (selectedDenoms?.includes(asset.denom)) {
acc[index] = true
}
return acc
},
{} as { [key: number]: boolean },
)
}, [selectedDenoms, assets])
const [selected, setSelected] = useState<RowSelectionState>(defaultSelected)
const balances = useStore((s) => s.balances)
const tableData: AssetTableRow[] = useMemo(() => {
return assets.map((asset) => {
const balancesForAsset = balances.find(byDenom(asset.denom))
const coin = BNCoin.fromDenomAndBigNumber(asset.denom, BN(balancesForAsset?.amount ?? '0'))
const value = getCoinValue(coin)
return {
asset,
balance: balancesForAsset?.amount ?? '0',
value,
market: markets.find((market) => market.denom === asset.denom),
}
})
}, [balances, assets, markets, getCoinValue])
useEffect(() => {
const selectedAssets = assets.filter((_, index) => selected[index])
const newSelectedDenoms = selectedAssets
.sort((a, b) => a.symbol.localeCompare(b.symbol))
.map((asset) => asset.denom)
if (
selectedDenoms.length === newSelectedDenoms.length &&
newSelectedDenoms.every((denom) => selectedDenoms.includes(denom))
)
return
onChangeSelected(newSelectedDenoms)
}, [selected, props, assets, selectedDenoms, onChangeSelected])
return (
<Table
title='Assets'
hideCard={true}
columns={columns}
data={tableData}
initialSorting={[{ id: isBorrow ? 'asset.borrowRate' : 'value', desc: !isBorrow }]}
setRowSelection={setSelected}
selectedRows={selected}
/>
)
}
@@ -1,96 +0,0 @@
import { ColumnDef } from '@tanstack/react-table'
import React from 'react'
import AssetImage from 'components/Asset/AssetImage'
import AssetRate from 'components/Asset/AssetRate'
import Checkbox from 'components/Checkbox'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import Text from 'components/Text'
import { BNCoin } from 'types/classes/BNCoin'
import { getAssetByDenom } from 'utils/assets'
import { demagnify, formatPercent } from 'utils/formatters'
function showBorrowRate(data: AssetTableRow[]) {
const assetData = data.length && (data[0].asset as BorrowAsset)
return !!(assetData && assetData?.borrowRate)
}
export default function useAssetTableColumns(isBorrow: boolean) {
return React.useMemo<ColumnDef<AssetTableRow>[]>(
() => [
{
header: 'Asset',
accessorKey: 'symbol',
id: 'symbol',
cell: ({ row }) => {
const asset = getAssetByDenom(row.original.asset.denom) as Asset
const market = row.original.market
const borrowAsset = row.original.asset as BorrowAsset
const showRate = !borrowAsset?.borrowRate
const apy = isBorrow ? market?.apy.borrow : market?.apy.deposit
return (
<div className='flex items-center'>
<Checkbox
name={`asset-${asset.id.toLowerCase()}`}
checked={row.getIsSelected()}
onChange={row.getToggleSelectedHandler()}
noMouseEvents
/>
<AssetImage asset={asset} size={24} className='ml-4' />
<div className='ml-2 text-left'>
<Text size='sm' className='mb-0.5 text-white'>
{asset.symbol}
</Text>
{showRate && market ? (
<AssetRate
rate={apy ?? 0}
isEnabled={market.borrowEnabled}
className='text-xs'
type='apy'
orientation='rtl'
suffix
/>
) : (
<Text size='xs'>{asset.name}</Text>
)}
</div>
</div>
)
},
},
{
id: 'details',
header: (data) => {
const tableData = data.table.options.data as AssetTableRow[]
if (showBorrowRate(tableData)) return 'Borrow Rate'
return 'Balance'
},
cell: ({ row }) => {
const asset = row.original.asset as BorrowAsset
const balance = row.original.balance
if (asset?.borrowRate)
return (
<Text size='sm' className='mb-0.5 text-white'>
{formatPercent(asset.borrowRate ?? 0)}
</Text>
)
if (!balance) return null
const coin = new BNCoin({ denom: row.original.asset.denom, amount: balance })
return (
<div className='flex flex-wrap items-center'>
<DisplayCurrency coin={coin} className='mb-0.5 w-full text-white' />
<FormattedNumber
className='w-full text-xs'
options={{ minDecimals: 2, maxDecimals: asset.decimals }}
amount={demagnify(balance, asset)}
/>
</div>
)
},
},
],
[isBorrow],
)
}
+15 -15
View File
@@ -1,24 +1,24 @@
import BigNumber from 'bignumber.js'
import { useCallback, useEffect, useMemo, useState } from 'react'
import AccountSummary from 'components/Account/AccountSummary'
import AssetImage from 'components/Asset/AssetImage'
import Button from 'components/Button'
import Card from 'components/Card'
import DisplayCurrency from 'components/DisplayCurrency'
import Divider from 'components/Divider'
import { FormattedNumber } from 'components/FormattedNumber'
import { ArrowRight, InfoCircle } from 'components/Icons'
import Modal from 'components/Modal'
import Switch from 'components/Switch'
import Text from 'components/Text'
import TitleAndSubCell from 'components/TitleAndSubCell'
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import AccountSummary from 'components/account/AccountSummary'
import AssetImage from 'components/common/assets/AssetImage'
import Button from 'components/common/Button'
import Card from 'components/common/Card'
import DisplayCurrency from 'components/common/DisplayCurrency'
import Divider from 'components/common/Divider'
import { FormattedNumber } from 'components/common/FormattedNumber'
import { ArrowRight, InfoCircle } from 'components/common/Icons'
import Switch from 'components/common/Switch'
import Text from 'components/common/Text'
import TitleAndSubCell from 'components/common/TitleAndSubCell'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import Modal from 'components/Modals/Modal'
import { BN_ZERO } from 'constants/math'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useMarketAssets from 'hooks/markets/useMarketAssets'
import useAutoLend from 'hooks/useAutoLend'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useHealthComputer from 'hooks/useHealthComputer'
import useMarketAssets from 'hooks/useMarketAssets'
import useToggle from 'hooks/useToggle'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import { getDepositAndLendCoinsToSpend } from 'hooks/useUpdatedAccount/functions'
@@ -1,4 +1,4 @@
import AccountFundContent from 'components/Account/AccountFund/AccountFundContent'
import AccountFundContent from 'components/account/AccountFund/AccountFundContent'
import useStore from 'store'
interface Props {
@@ -1,14 +1,15 @@
import BigNumber from 'bignumber.js'
import { useEffect, useState } from 'react'
import Button from 'components/Button'
import Divider from 'components/Divider'
import { ArrowRight } from 'components/Icons'
import Switch from 'components/Switch'
import Text from 'components/Text'
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import { ASSETS } from 'constants/assets'
import Button from 'components/common/Button'
import Divider from 'components/common/Divider'
import { ArrowRight } from 'components/common/Icons'
import Switch from 'components/common/Switch'
import Text from 'components/common/Text'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import { BN_ZERO } from 'constants/math'
import useAllAssets from 'hooks/assets/useAllAssets'
import useMarketEnabledAssets from 'hooks/assets/useMarketEnabledAssets'
import useHealthComputer from 'hooks/useHealthComputer'
import useToggle from 'hooks/useToggle'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
@@ -16,7 +17,6 @@ import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { cloneAccount, getMergedBalancesForAsset, removeDepositsAndLends } from 'utils/accounts'
import { byDenom } from 'utils/array'
import { getEnabledMarketAssets } from 'utils/assets'
interface Props {
account: Account
@@ -24,8 +24,9 @@ interface Props {
export default function WithdrawFromAccount(props: Props) {
const { account } = props
const assets = useAllAssets()
const defaultAsset =
ASSETS.find(byDenom(account.deposits[0]?.denom || account.lends[0]?.denom)) ?? ASSETS[0]
assets.find(byDenom(account.deposits[0]?.denom || account.lends[0]?.denom)) ?? assets[0]
const withdraw = useStore((s) => s.withdraw)
const [withdrawWithBorrowing, setWithdrawWithBorrowing] = useToggle()
const [currentAsset, setCurrentAsset] = useState(defaultAsset)
@@ -35,7 +36,8 @@ export default function WithdrawFromAccount(props: Props) {
const accountClone = cloneAccount(account)
const borrowAccount = removeDepositsAndLends(accountClone, currentAsset.denom)
const { computeMaxBorrowAmount } = useHealthComputer(borrowAccount)
const balances = getMergedBalancesForAsset(account, getEnabledMarketAssets())
const marketEnabledAssets = useMarketEnabledAssets()
const balances = getMergedBalancesForAsset(account, marketEnabledAssets)
const maxWithdrawAmount = computeMaxWithdrawAmount(currentAsset.denom)
const maxWithdrawWithBorrowAmount = computeMaxBorrowAmount(currentAsset.denom, 'wallet').plus(
maxWithdrawAmount,
@@ -46,10 +48,12 @@ export default function WithdrawFromAccount(props: Props) {
const max = withdrawWithBorrowing ? maxWithdrawWithBorrowAmount : maxWithdrawAmount
const accountDeposit = account.deposits.find(byDenom(currentAsset.denom))?.amount ?? BN_ZERO
const shouldReclaim =
amount.isGreaterThan(accountDeposit) && !withdrawWithBorrowing && currentAsset.isAutoLendEnabled
const reclaimAmount = shouldReclaim ? amount.minus(accountDeposit) : BN_ZERO
const isReclaimingMaxAmount = maxWithdrawAmount.isEqualTo(amount)
const accountLent = account.lends.find(byDenom(currentAsset.denom))?.amount ?? BN_ZERO
const shouldReclaim = amount.isGreaterThan(accountDeposit) && !accountLent.isZero()
const isReclaimingMaxAmount = accountLent.isLessThanOrEqualTo(amount.minus(accountDeposit))
const reclaimAmount = isReclaimingMaxAmount
? amount
: accountLent.minus(amount).minus(accountDeposit)
function onChangeAmount(val: BigNumber) {
setAmount(val)
@@ -65,13 +69,14 @@ export default function WithdrawFromAccount(props: Props) {
const borrow = !debtAmount.isZero()
? [BNCoin.fromDenomAndBigNumber(currentAsset.denom, debtAmount)]
: []
const reclaims = !reclaimAmount.isZero()
? [
BNCoin.fromDenomAndBigNumber(currentAsset.denom, reclaimAmount).toActionCoin(
isReclaimingMaxAmount,
),
]
: []
const reclaims =
shouldReclaim && !reclaimAmount.isZero()
? [
BNCoin.fromDenomAndBigNumber(currentAsset.denom, reclaimAmount).toActionCoin(
isReclaimingMaxAmount,
),
]
: []
withdraw({
accountId: account.id,
+2 -2
View File
@@ -1,7 +1,7 @@
import FundWithdrawModalContent from 'components/Modals/FundWithdraw/FundAndWithdrawModalContent'
import ModalContentWithSummary from 'components/Modals/ModalContentWithSummary'
import Text from 'components/Text'
import useAccount from 'hooks/useAccount'
import Text from 'components/common/Text'
import useAccount from 'hooks/accounts/useAccount'
import useAccountId from 'hooks/useAccountId'
import useStore from 'store'
+3 -3
View File
@@ -1,8 +1,8 @@
import { useCallback } from 'react'
import { ChevronRight, Compass, HandCoins, Luggage } from 'components/Icons'
import Modal from 'components/Modal'
import Text from 'components/Text'
import { ChevronRight, Compass, HandCoins, Luggage } from 'components/common/Icons'
import Modal from 'components/Modals/Modal'
import Text from 'components/common/Text'
import useStore from 'store'
import { DocURL } from 'types/enums/docURL'
@@ -1,10 +1,10 @@
import React, { useState } from 'react'
import { mutate } from 'swr'
import Button from 'components/Button'
import DocsLink from 'components/DocsLink'
import { ArrowRight } from 'components/Icons'
import Text from 'components/Text'
import Button from 'components/common/Button'
import DocsLink from 'components/common/DocsLink'
import { ArrowRight } from 'components/common/Icons'
import Text from 'components/common/Text'
import useStore from 'store'
export default function CreateAccount() {
@@ -1,9 +1,9 @@
import React, { useMemo } from 'react'
import Button from 'components/Button'
import { ArrowRight } from 'components/Icons'
import Button from 'components/common/Button'
import { ArrowRight } from 'components/common/Icons'
import LeverageSummary from 'components/Modals/HLS/Deposit/LeverageSummary'
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import { getLeveragedApy } from 'utils/math'
interface Props {
@@ -1,6 +1,6 @@
import React, { useMemo } from 'react'
import SummaryItems from 'components/SummaryItems'
import SummaryItems from 'components/common/SummaryItems'
import useBorrowAsset from 'hooks/useBorrowAsset'
interface Props {
@@ -1,8 +1,8 @@
import React from 'react'
import Button from 'components/Button'
import { ArrowRight } from 'components/Icons'
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import Button from 'components/common/Button'
import { ArrowRight } from 'components/common/Icons'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
interface Props {
amount: BigNumber
@@ -1,9 +1,9 @@
import classNames from 'classnames'
import React from 'react'
import Button from 'components/Button'
import { ArrowRight } from 'components/Icons'
import Radio from 'components/Radio'
import Button from 'components/common/Button'
import { ArrowRight } from 'components/common/Icons'
import Radio from 'components/common/Radio'
interface Props {
hlsAccounts: Account[]
@@ -1,10 +1,11 @@
import classNames from 'classnames'
import React from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import { ExclamationMarkTriangle } from 'components/Icons'
import Text from 'components/Text'
import WarningMessages from 'components/WarningMessages'
import DisplayCurrency from 'components/common/DisplayCurrency'
import { ExclamationMarkTriangle } from 'components/common/Icons'
import Text from 'components/common/Text'
import WarningMessages from 'components/common/WarningMessages'
import useAllAssets from 'hooks/assets/useAllAssets'
import { BNCoin } from 'types/classes/BNCoin'
import { formatAmountWithSymbol, formatLeverage } from 'utils/formatters'
@@ -33,6 +34,7 @@ interface CollateralSubTitleProps {
}
export function CollateralSubTitle(props: CollateralSubTitleProps) {
const assets = useAllAssets()
if (props.isOpen) return null
if (!props.isOpen && props.amount.isZero()) {
@@ -50,10 +52,13 @@ export function CollateralSubTitle(props: CollateralSubTitleProps) {
<div className='flex items-center gap-1'>
<WarningMessages messages={props.warningMessages} />
<SubTitle
text={formatAmountWithSymbol({
denom: props.denom,
amount: props.amount.toString(),
})}
text={formatAmountWithSymbol(
{
denom: props.denom,
amount: props.amount.toString(),
},
assets,
)}
color={props.warningMessages.length > 0 ? 'text-warning' : ''}
/>
</div>
@@ -1,7 +1,7 @@
import React from 'react'
import { FormattedNumber } from 'components/FormattedNumber'
import Text from 'components/Text'
import { FormattedNumber } from 'components/common/FormattedNumber'
import Text from 'components/common/Text'
interface Props {
items: { title: string; amount: number }[]
@@ -1,10 +1,10 @@
import React from 'react'
import AmountAndValue from 'components/AmountAndValue'
import AssetImage from 'components/Asset/AssetImage'
import { FormattedNumber } from 'components/FormattedNumber'
import AmountAndValue from 'components/common/AmountAndValue'
import AssetImage from 'components/common/assets/AssetImage'
import { FormattedNumber } from 'components/common/FormattedNumber'
import Container from 'components/Modals/HLS/Deposit/Summary/Container'
import Text from 'components/Text'
import Text from 'components/common/Text'
interface Props {
amount: BigNumber
@@ -1,6 +1,6 @@
import React from 'react'
import Text from 'components/Text'
import Text from 'components/common/Text'
interface Props {
children: React.ReactNode
@@ -1,12 +1,12 @@
import React, { useMemo } from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import { InfoCircle } from 'components/Icons'
import DisplayCurrency from 'components/common/DisplayCurrency'
import { FormattedNumber } from 'components/common/FormattedNumber'
import { InfoCircle } from 'components/common/Icons'
import AprBreakdown from 'components/Modals/HLS/Deposit/Summary/ApyBreakdown'
import Container from 'components/Modals/HLS/Deposit/Summary/Container'
import Text from 'components/Text'
import { Tooltip } from 'components/Tooltip'
import Text from 'components/common/Text'
import { Tooltip } from 'components/common/Tooltip'
import { BNCoin } from 'types/classes/BNCoin'
interface Props {
@@ -1,7 +1,7 @@
import React from 'react'
import Button from 'components/Button'
import { ArrowRight } from 'components/Icons'
import Button from 'components/common/Button'
import { ArrowRight } from 'components/common/Icons'
import AssetSummary from 'components/Modals/HLS/Deposit/Summary/AssetSummary'
import YourPosition from 'components/Modals/HLS/Deposit/Summary/YourPosition'
import useBorrowAsset from 'hooks/useBorrowAsset'
+2 -2
View File
@@ -1,11 +1,11 @@
import React, { useMemo, useState } from 'react'
import Accordion from 'components/Accordion'
import Accordion from 'components/common/Accordion'
import useStakingController from 'components/Modals/HLS/Deposit//useStakingController'
import useVaultController from 'components/Modals/HLS/Deposit//useVaultController'
import useAccordionItems from 'components/Modals/HLS/Deposit/useAccordionItems'
import { EMPTY_ACCOUNT_HLS } from 'constants/accounts'
import useAccounts from 'hooks/useAccounts'
import useAccounts from 'hooks/accounts/useAccounts'
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
import useIsOpenArray from 'hooks/useIsOpenArray'
import useVault from 'hooks/useVault'
@@ -11,6 +11,7 @@ import {
} from 'components/Modals/HLS/Deposit/SubTitles'
import Summary from 'components/Modals/HLS/Deposit/Summary'
import { BN_ZERO } from 'constants/math'
import useAllAssets from 'hooks/assets/useAllAssets'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import { getCoinAmount, getCoinValue } from 'utils/formatters'
@@ -47,6 +48,7 @@ interface Props {
export default function useAccordionItems(props: Props) {
const { data: prices } = usePrices()
const assets = useAllAssets()
const depositCapLeft = useMemo(() => {
if (!props.strategy) return BN_ZERO
@@ -62,9 +64,10 @@ export default function useAccordionItems(props: Props) {
const value = getCoinValue(
BNCoin.fromDenomAndBigNumber(props.borrowAsset.denom, props.borrowAmount),
prices,
assets,
)
return getCoinAmount(props.collateralAsset.denom, value, prices)
}, [prices, props.borrowAmount, props.borrowAsset.denom, props.collateralAsset])
return getCoinAmount(props.collateralAsset.denom, value, prices, assets)
}, [assets, prices, props.borrowAmount, props.borrowAsset.denom, props.collateralAsset.denom])
const collateralWarningMessages = useMemo(() => {
const messages: string[] = []
@@ -73,11 +76,14 @@ export default function useAccordionItems(props: Props) {
}
if (props.depositAmount.isGreaterThan(depositCapLeft)) {
messages.push(getDepositCapMessage(props.collateralAsset.denom, depositCapLeft, 'deposit'))
messages.push(
getDepositCapMessage(props.collateralAsset.denom, depositCapLeft, 'deposit', assets),
)
}
return messages
}, [
assets,
depositCapLeft,
props.collateralAsset.denom,
props.collateralAsset.symbol,
@@ -90,21 +96,22 @@ export default function useAccordionItems(props: Props) {
if (props.borrowAmount.isGreaterThan(props.maxBorrowAmount)) {
messages.push(
getHealthFactorMessage(props.borrowAsset.denom, props.maxBorrowAmount, 'borrow'),
getHealthFactorMessage(props.borrowAsset.denom, props.maxBorrowAmount, 'borrow', assets),
)
}
if (props.borrowAmount.isGreaterThan(borrowLiquidity)) {
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity))
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity, assets))
}
if (additionalDepositFromSwap.plus(props.depositAmount).isGreaterThan(props.maxBorrowAmount)) {
messages.push(getDepositCapMessage(props.borrowAsset.denom, depositCapLeft, 'borrow'))
messages.push(getDepositCapMessage(props.borrowAsset.denom, depositCapLeft, 'borrow', assets))
}
return messages
}, [
additionalDepositFromSwap,
assets,
borrowLiquidity,
depositCapLeft,
props.borrowAmount,
+3 -3
View File
@@ -1,8 +1,8 @@
import React from 'react'
import DoubleLogo from 'components/DoubleLogo'
import HLSTag from 'components/HLS/HLSTag'
import Text from 'components/Text'
import DoubleLogo from 'components/common/DoubleLogo'
import HLSTag from 'components/hls/HLSTag'
import Text from 'components/common/Text'
interface Props {
primaryAsset: Asset
@@ -1,13 +1,14 @@
import React, { useCallback, useMemo, useState } from 'react'
import Button from 'components/Button'
import Button from 'components/common/Button'
import LeverageSummary from 'components/Modals/HLS/Deposit/LeverageSummary'
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import { BN_ZERO } from 'constants/math'
import useAllAssets from 'hooks/assets/useAllAssets'
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useHealthComputer from 'hooks/useHealthComputer'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import useStore from 'store'
@@ -28,6 +29,7 @@ interface Props {
export default function ChangeLeverage(props: Props) {
const { data: prices } = usePrices()
const assets = useAllAssets()
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
const {
updatedAccount,
@@ -83,10 +85,11 @@ export default function ChangeLeverage(props: Props) {
const [deposits, lends, debts, vaults] = getAccountPositionValues(
updatedAccount || props.account,
prices,
assets,
)
return deposits.plus(lends).plus(debts).plus(vaults)
}, [prices, props.account, updatedAccount])
}, [assets, prices, props.account, updatedAccount])
const handleOnClick = useCallback(() => {
useStore.setState({ hlsManageModal: null })
@@ -98,17 +101,19 @@ export default function ChangeLeverage(props: Props) {
props.borrowAsset.denom,
slippage,
prices,
assets,
)
changeHlsStakingLeverage({ accountId: props.account.id, actions })
}, [
currentDebt,
changeHlsStakingLeverage,
previousDebt,
prices,
props.account.id,
props.borrowAsset.denom,
props.collateralAsset.denom,
props.borrowAsset.denom,
props.account.id,
slippage,
prices,
assets,
changeHlsStakingLeverage,
])
const addedDepositAmount = useMemo(
@@ -132,20 +137,25 @@ export default function ChangeLeverage(props: Props) {
const borrowLiquidity = props.borrowAsset.liquidity?.amount || BN_ZERO
if (borrowLiquidity.isLessThan(currentDebt.minus(previousDebt))) {
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity))
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity, assets))
}
if (maxBorrowAmount.isLessThan(currentDebt)) {
messages.push(getHealthFactorMessage(props.borrowAsset.denom, maxBorrowAmount, 'borrow'))
messages.push(
getHealthFactorMessage(props.borrowAsset.denom, maxBorrowAmount, 'borrow', assets),
)
}
if (addedDepositAmount.isGreaterThan(depositCapLeft)) {
messages.push(getDepositCapMessage(props.collateralAsset.denom, depositCapLeft, 'borrow'))
messages.push(
getDepositCapMessage(props.collateralAsset.denom, depositCapLeft, 'borrow', assets),
)
}
return messages
}, [
addedDepositAmount,
assets,
currentDebt,
depositCapLeft,
maxBorrowAmount,
+21 -13
View File
@@ -1,14 +1,15 @@
import BigNumber from 'bignumber.js'
import React, { useCallback, useMemo } from 'react'
import Button from 'components/Button'
import Divider from 'components/Divider'
import SummaryItems from 'components/SummaryItems'
import Switch from 'components/Switch'
import Text from 'components/Text'
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import Button from 'components/common/Button'
import Divider from 'components/common/Divider'
import SummaryItems from 'components/common/SummaryItems'
import Switch from 'components/common/Switch'
import Text from 'components/common/Text'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import { BN_ZERO } from 'constants/math'
import useDepositActions from 'hooks/HLS/useDepositActions'
import useAllAssets from 'hooks/assets/useAllAssets'
import useDepositActions from 'hooks/hls/useDepositActions'
import useBorrowAsset from 'hooks/useBorrowAsset'
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
import useHealthComputer from 'hooks/useHealthComputer'
@@ -40,6 +41,7 @@ export default function Deposit(props: Props) {
useUpdatedAccount(props.account)
const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount)
const assets = useAllAssets()
const { data: prices } = usePrices()
const [keepLeverage, toggleKeepLeverage] = useToggle(true)
const collateralAssetAmountInWallet = BN(
@@ -49,8 +51,8 @@ export default function Deposit(props: Props) {
const borrowRate = useBorrowAsset(props.borrowAsset.denom)?.borrowRate || 0
const currentLeverage = useMemo(
() => calculateAccountLeverage(props.account, prices).toNumber(),
[prices, props.account],
() => calculateAccountLeverage(props.account, prices, assets).toNumber(),
[prices, props.account, assets],
)
const depositCoin = useMemo(
@@ -83,7 +85,7 @@ export default function Deposit(props: Props) {
)
if (capLeft.isLessThan(depositCoin.amount.plus(addedDepositFromSwap))) {
messages.push(getDepositCapMessage(props.collateralAsset.denom, capLeft, 'deposit'))
messages.push(getDepositCapMessage(props.collateralAsset.denom, capLeft, 'deposit', assets))
}
if (collateralAssetAmountInWallet.isZero()) {
@@ -99,6 +101,7 @@ export default function Deposit(props: Props) {
props.account.strategy.depositCap.used,
props.collateralAsset.denom,
props.collateralAsset.symbol,
assets,
])
const maxBorrowAmount = useMemo(
@@ -109,17 +112,20 @@ export default function Deposit(props: Props) {
const borrowWarningMessages = useMemo(() => {
let messages: string[] = []
if (borrowCoin.amount.isGreaterThan(maxBorrowAmount)) {
messages.push(getHealthFactorMessage(props.borrowAsset.denom, maxBorrowAmount, 'borrow'))
messages.push(
getHealthFactorMessage(props.borrowAsset.denom, maxBorrowAmount, 'borrow', assets),
)
}
const borrowLiquidity = props.borrowAsset.liquidity?.amount || BN_ZERO
if (borrowCoin.amount.isGreaterThan(borrowLiquidity)) {
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity))
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity, assets))
}
return messages
}, [
assets,
borrowCoin.amount,
maxBorrowAmount,
props.borrowAsset.denom,
@@ -151,9 +157,10 @@ export default function Deposit(props: Props) {
const depositValue = getCoinValue(
BNCoin.fromDenomAndBigNumber(props.collateralAsset.denom, amount),
prices,
assets,
)
const borrowValue = BN(currentLeverage - 1).times(depositValue)
additionalDebt = getCoinAmount(props.borrowAsset.denom, borrowValue, prices)
additionalDebt = getCoinAmount(props.borrowAsset.denom, borrowValue, prices, assets)
}
simulateHlsStakingDeposit(
@@ -168,6 +175,7 @@ export default function Deposit(props: Props) {
props.borrowAsset.denom,
props.collateralAsset.denom,
simulateHlsStakingDeposit,
assets,
],
)
+4 -4
View File
@@ -1,9 +1,9 @@
import BigNumber from 'bignumber.js'
import { useCallback, useMemo } from 'react'
import Button from 'components/Button'
import SummaryItems from 'components/SummaryItems'
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import Button from 'components/common/Button'
import SummaryItems from 'components/common/SummaryItems'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import { BN_ZERO } from 'constants/math'
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
@@ -28,7 +28,7 @@ export default function Repay(props: Props) {
const repay = useStore((s) => s.repay)
const currentDebt: BigNumber = useMemo(
() => props.account.debts.find(byDenom(props.borrowAsset.denom)).amount || BN_ZERO,
() => props.account.debts.find(byDenom(props.borrowAsset.denom))?.amount || BN_ZERO,
[props.account.debts, props.borrowAsset.denom],
)
+10 -5
View File
@@ -1,8 +1,9 @@
import React, { useCallback, useMemo } from 'react'
import { useCallback, useMemo } from 'react'
import Button from 'components/Button'
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import Button from 'components/common/Button'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import { BN_ZERO } from 'constants/math'
import useAllAssets from 'hooks/assets/useAllAssets'
import useHealthComputer from 'hooks/useHealthComputer'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import useStore from 'store'
@@ -20,6 +21,7 @@ interface Props {
export default function Withdraw(props: Props) {
const { removedDeposits, removeDeposits, updatedAccount } = useUpdatedAccount(props.account)
const { computeMaxWithdrawAmount } = useHealthComputer(updatedAccount)
const assets = useAllAssets()
const withdraw = useStore((s) => s.withdraw)
const handleChange = useCallback(
(amount: BigNumber) =>
@@ -40,6 +42,7 @@ export default function Withdraw(props: Props) {
const onClick = useCallback(() => {
useStore.setState({ hlsManageModal: null })
if (!removedDeposit) return
withdraw({
accountId: props.account.id,
coins: [{ coin: removedDeposit }],
@@ -52,10 +55,12 @@ export default function Withdraw(props: Props) {
const warningMessages = useMemo(() => {
if (maxWithdrawAmount.isLessThan(withdrawAmount) || maxWithdrawAmount.isZero()) {
return [getHealthFactorMessage(props.collateralAsset.denom, maxWithdrawAmount, 'withdraw')]
return [
getHealthFactorMessage(props.collateralAsset.denom, maxWithdrawAmount, 'withdraw', assets),
]
}
return []
}, [maxWithdrawAmount, props.collateralAsset.denom, withdrawAmount])
}, [assets, maxWithdrawAmount, props.collateralAsset.denom, withdrawAmount])
return (
<>
+3 -3
View File
@@ -6,15 +6,15 @@ import Deposit from 'components/Modals/HLS/Manage/Deposit'
import Repay from 'components/Modals/HLS/Manage/Repay'
import Withdraw from 'components/Modals/HLS/Manage/Withdraw'
import ModalContentWithSummary from 'components/Modals/ModalContentWithSummary'
import useAccount from 'hooks/useAccount'
import useAccount from 'hooks/accounts/useAccount'
import useAsset from 'hooks/assets/useAsset'
import useBorrowAsset from 'hooks/useBorrowAsset'
import useStore from 'store'
import { getAssetByDenom } from 'utils/assets'
export default function HlsManageModalController() {
const modal = useStore((s) => s.hlsManageModal)
const { data: account } = useAccount(modal?.accountId)
const collateralAsset = getAssetByDenom(modal?.staking.strategy.denoms.deposit || '')
const collateralAsset = useAsset(modal?.staking.strategy.denoms.deposit || '')
const borrowAsset = useBorrowAsset(modal?.staking.strategy.denoms.borrow || '')
if (!modal || !collateralAsset || !borrowAsset || !account) return null
+3 -3
View File
@@ -1,16 +1,16 @@
import React from 'react'
import Modal from 'components/Modal'
import Modal from 'components/Modals/Modal'
import Content from 'components/Modals/HLS/Deposit'
import Header from 'components/Modals/HLS/Header'
import useAsset from 'hooks/assets/useAsset'
import useBorrowAsset from 'hooks/useBorrowAsset'
import useStore from 'store'
import { getAssetByDenom } from 'utils/assets'
export default function HlsModalController() {
const modal = useStore((s) => s.hlsModal)
const primaryAsset = getAssetByDenom(
const primaryAsset = useAsset(
modal?.vault?.denoms.primary || modal?.strategy?.denoms.deposit || '',
)
@@ -1,6 +1,6 @@
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import TitleAndSubCell from 'components/TitleAndSubCell'
import DisplayCurrency from 'components/common/DisplayCurrency'
import { FormattedNumber } from 'components/common/FormattedNumber'
import TitleAndSubCell from 'components/common/TitleAndSubCell'
import useAssetIncentivesApy from 'hooks/useAssetIncentiveApy'
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
import { BNCoin } from 'types/classes/BNCoin'
@@ -15,7 +15,7 @@ function DetailsHeader({ data }: Props) {
const balanceInWallet = useCurrentWalletBalance(asset.denom)
return (
<div className='flex gap-6 border-b border-white/5 px-6 py-4 gradient-header'>
<div className='flex gap-6 px-6 py-4 border-b border-white/5 gradient-header'>
{assetApy && (
<>
<TitleAndSubCell
@@ -40,7 +40,7 @@ function DetailsHeader({ data }: Props) {
<TitleAndSubCell
title={
<DisplayCurrency
coin={new BNCoin({ denom: asset.denom, amount: accountLentAmount })}
coin={new BNCoin({ denom: asset.denom, amount: accountLentAmount.toString() })}
/>
}
sub={'Deposited'}
@@ -2,7 +2,7 @@ import { useCallback } from 'react'
import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal'
import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import useStore from 'store'
@@ -1,8 +1,8 @@
import classNames from 'classnames'
import { ReactNode, useEffect, useRef } from 'react'
import EscButton from 'components/Button/EscButton'
import Card from 'components/Card'
import EscButton from 'components/common/Button/EscButton'
import Card from 'components/common/Card'
export interface ModalProps {
header: string | ReactNode
@@ -1,10 +1,10 @@
import classNames from 'classnames'
import React from 'react'
import AccountSummary from 'components/Account/AccountSummary'
import Card from 'components/Card'
import { CircularProgress } from 'components/CircularProgress'
import Modal, { ModalProps } from 'components/Modal'
import AccountSummary from 'components/account/AccountSummary'
import Card from 'components/common/Card'
import { CircularProgress } from 'components/common/CircularProgress'
import Modal, { ModalProps } from 'components/Modals/Modal'
import useStore from 'store'
interface Props extends ModalProps {
@@ -1,7 +1,7 @@
import classNames from 'classnames'
import { ReactNode } from 'react'
import Text from 'components/Text'
import Text from 'components/common/Text'
interface Props {
label: string
@@ -1,7 +1,7 @@
import classNames from 'classnames'
import Switch from 'components/Switch'
import Text from 'components/Text'
import Switch from 'components/common/Switch'
import Text from 'components/common/Text'
interface Props {
onChange: (value: boolean) => void
+39 -22
View File
@@ -1,24 +1,26 @@
import classNames from 'classnames'
import { useCallback, useMemo, useState } from 'react'
import AssetImage from 'components/Asset/AssetImage'
import Button from 'components/Button'
import { ArrowCircle, Enter } from 'components/Icons'
import Modal from 'components/Modal'
import Modal from 'components/Modals/Modal'
import SettingsOptions from 'components/Modals/Settings/SettingsOptions'
import SettingsSwitch from 'components/Modals/Settings/SettingsSwitch'
import NumberInput from 'components/NumberInput'
import Select from 'components/Select'
import Text from 'components/Text'
import { TextLink } from 'components/TextLink'
import Button from 'components/common/Button'
import { ArrowCircle, Enter } from 'components/common/Icons'
import NumberInput from 'components/common/NumberInput'
import Select from 'components/common/Select'
import Text from 'components/common/Text'
import { TextLink } from 'components/common/TextLink'
import AssetImage from 'components/common/assets/AssetImage'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import { BN_ZERO } from 'constants/math'
import useDisplayCurrencyAssets from 'hooks/assets/useDisplayCurrencyAssets'
import useDisplayCurrency from 'hooks/localStorage/useDisplayCurrency'
import useEnableAutoLendGlobal from 'hooks/localStorage/useEnableAutoLendGlobal'
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useAlertDialog from 'hooks/useAlertDialog'
import useAutoLend from 'hooks/useAutoLend'
import useLocalStorage from 'hooks/useLocalStorage'
import useStore from 'store'
import { getDisplayCurrencies } from 'utils/assets'
import { BN } from 'utils/helpers'
const slippages = [0.02, 0.03]
@@ -26,15 +28,13 @@ const slippages = [0.02, 0.03]
export default function SettingsModal() {
const modal = useStore((s) => s.settingsModal)
const { open: showResetDialog } = useAlertDialog()
const displayCurrencies = getDisplayCurrencies()
const displayCurrencies = useDisplayCurrencyAssets()
const { setAutoLendOnAllAccounts } = useAutoLend()
const [customSlippage, setCustomSlippage] = useState<number>(0)
const [inputRef, setInputRef] = useState<React.RefObject<HTMLInputElement>>()
const [isCustom, setIsCustom] = useState(false)
const [displayCurrency, setDisplayCurrency] = useLocalStorage<string>(
LocalStorageKeys.DISPLAY_CURRENCY,
DEFAULT_SETTINGS.displayCurrency,
)
const [displayCurrency, setDisplayCurrency] = useDisplayCurrency()
const [reduceMotion, setReduceMotion] = useLocalStorage<boolean>(
LocalStorageKeys.REDUCE_MOTION,
DEFAULT_SETTINGS.reduceMotion,
@@ -43,14 +43,15 @@ export default function SettingsModal() {
LocalStorageKeys.TUTORIAL,
DEFAULT_SETTINGS.tutorial,
)
const [lendAssets, setLendAssets] = useLocalStorage<boolean>(
LocalStorageKeys.LEND_ASSETS,
DEFAULT_SETTINGS.lendAssets,
)
const [enableAutoLendGlobal, setLendAssets] = useEnableAutoLendGlobal()
const [slippage, setSlippage] = useLocalStorage<number>(
LocalStorageKeys.SLIPPAGE,
DEFAULT_SETTINGS.slippage,
)
const [updateOracle, setUpdateOracle] = useLocalStorage<boolean>(
LocalStorageKeys.UPDATE_ORACLE,
DEFAULT_SETTINGS.updateOracle,
)
const displayCurrenciesOptions = useMemo(
() =>
@@ -148,9 +149,16 @@ export default function SettingsModal() {
handleDisplayCurrency(DEFAULT_SETTINGS.displayCurrency)
handleSlippage(DEFAULT_SETTINGS.slippage)
handleReduceMotion(DEFAULT_SETTINGS.reduceMotion)
handleLendAssets(DEFAULT_SETTINGS.lendAssets)
handleLendAssets(DEFAULT_SETTINGS.enableAutoLendGlobal)
}, [handleDisplayCurrency, handleReduceMotion, handleLendAssets, handleSlippage])
const handleUpdateOracle = useCallback(
(value: boolean) => {
setUpdateOracle(value)
},
[setUpdateOracle],
)
const showResetModal = useCallback(() => {
showResetDialog({
icon: (
@@ -193,12 +201,21 @@ export default function SettingsModal() {
>
<SettingsSwitch
onChange={handleLendAssets}
name='lendAssets'
value={lendAssets}
name='enableAutoLendGlobal'
value={enableAutoLendGlobal}
label='Lend assets in Credit Accounts'
description='By turning this on you will automatically lend out all the assets you deposit into your Credit Accounts to earn yield.'
withStatus
/>
<SettingsSwitch
onChange={handleUpdateOracle}
name='updateOracle'
value={updateOracle}
label='Update Pyth Oracles on transactions'
description={`When this setting is on, your Mars transactions will automatically trigger an update of Pyth's price oracles. This increases the accuracy of the prices shown in the UI. In some instances, this could cause your transactions to fail when using a Ledger hardware wallet. If you encounter transaction failures with Ledger, please turn this setting off.`}
withStatus
/>
<SettingsSwitch
onChange={handleReduceMotion}
name='reduceMotion'
@@ -1,6 +1,6 @@
import Button from 'components/Button'
import Button from 'components/common/Button'
import { NoIcon, YesIcon } from 'components/Modals/AlertDialog/ButtonIcons'
import Text from 'components/Text'
import Text from 'components/common/Text'
import useAccountId from 'hooks/useAccountId'
import useStore from 'store'
+2 -2
View File
@@ -1,5 +1,5 @@
import { LockUnlocked } from 'components/Icons'
import Modal from 'components/Modal'
import { LockUnlocked } from 'components/common/Icons'
import Modal from 'components/Modals/Modal'
import UnlockModalContent from 'components/Modals/Unlock/UnlockModalContent'
import useStore from 'store'
+16 -14
View File
@@ -1,24 +1,25 @@
import BigNumber from 'bignumber.js'
import React, { useEffect, useMemo, useState } from 'react'
import Button from 'components/Button'
import DepositCapMessage from 'components/DepositCapMessage'
import DisplayCurrency from 'components/DisplayCurrency'
import Divider from 'components/Divider'
import { ArrowRight, ExclamationMarkCircled } from 'components/Icons'
import Index from 'components/Slider'
import Text from 'components/Text'
import TokenInput from 'components/TokenInput'
import Button from 'components/common/Button'
import DepositCapMessage from 'components/common/DepositCapMessage'
import DisplayCurrency from 'components/common/DisplayCurrency'
import Divider from 'components/common/Divider'
import { ArrowRight, ExclamationMarkCircled } from 'components/common/Icons'
import Index from 'components/common/Slider'
import Text from 'components/common/Text'
import TokenInput from 'components/common/TokenInput'
import { BN_ZERO } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle'
import useAllAssets from 'hooks/assets/useAllAssets'
import useMarketAssets from 'hooks/markets/useMarketAssets'
import useHealthComputer from 'hooks/useHealthComputer'
import useMarketAssets from 'hooks/useMarketAssets'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { byDenom } from 'utils/array'
import { findCoinByDenom, getAssetByDenom } from 'utils/assets'
import { findCoinByDenom } from 'utils/assets'
import { formatPercent } from 'utils/formatters'
import { getValueFromBNCoins, mergeBNCoinArrays } from 'utils/helpers'
@@ -36,6 +37,7 @@ export interface VaultBorrowingsProps {
}
export default function VaultBorrowings(props: VaultBorrowingsProps) {
const assets = useAllAssets()
const { borrowings, onChangeBorrowings } = props
const { data: marketAssets } = useMarketAssets()
const { data: prices } = usePrices()
@@ -85,8 +87,8 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
}, [maxBorrowAmountsRaw, props.borrowings])
const totalValue = useMemo(
() => getValueFromBNCoins(mergeBNCoinArrays(props.deposits, props.borrowings), prices),
[props.borrowings, props.deposits, prices],
() => getValueFromBNCoins(mergeBNCoinArrays(props.deposits, props.borrowings), prices, assets),
[props.deposits, props.borrowings, prices, assets],
)
useEffect(() => {
@@ -176,7 +178,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
return (
<div className='flex flex-col flex-1 gap-4 p-4'>
{props.borrowings.map((coin) => {
const asset = getAssetByDenom(coin.denom)
const asset = assets.find(byDenom(coin.denom))
const maxAmount = maxBorrowAmounts.find(byDenom(coin.denom))?.amount
if (!asset || !maxAmount)
return <React.Fragment key={`input-${coin.denom}`}></React.Fragment>
@@ -224,7 +226,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
/>
</div>
{props.borrowings.map((coin) => {
const asset = getAssetByDenom(coin.denom)
const asset = assets.find(byDenom(coin.denom))
const borrowRate = marketAssets?.find((market) => market.denom === coin.denom)?.apy.borrow
if (!asset || !borrowRate)
@@ -1,13 +1,14 @@
import classNames from 'classnames'
import { useMemo } from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import Text from 'components/Text'
import DisplayCurrency from 'components/common/DisplayCurrency'
import Text from 'components/common/Text'
import { BN_ZERO } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle'
import useAllAssets from 'hooks/assets/useAllAssets'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import { formatAmountWithSymbol, getCoinValue } from 'utils/formatters'
import { ORACLE_DENOM } from 'constants/oracle'
interface Props {
borrowings: BNCoin[]
@@ -16,16 +17,16 @@ interface Props {
export default function VaultBorrowingsSubTitle(props: Props) {
const { data: prices } = usePrices()
const assets = useAllAssets()
const borrowingValue = useMemo(() => {
let borrowingValue = BN_ZERO
props.borrowings.map((coin) => {
const price = prices.find((p) => p.denom === coin.denom)?.amount
if (!price || coin.amount.isZero()) return
borrowingValue = getCoinValue(coin, prices)
borrowingValue = getCoinValue(coin, prices, assets)
})
return borrowingValue
}, [props.borrowings, prices])
}, [props.borrowings, prices, assets])
const borrowingTexts = useMemo(
() =>
@@ -38,10 +39,10 @@ export default function VaultBorrowingsSubTitle(props: Props) {
index !== 0 && 'ml-1 before:pr-1 before:content-["+"]',
)}
>
{formatAmountWithSymbol(borrowing.toCoin())}
{formatAmountWithSymbol(borrowing.toCoin(), assets)}
</Text>
)),
[props.borrowings],
[assets, props.borrowings],
)
return (
+16 -14
View File
@@ -1,18 +1,19 @@
import BigNumber from 'bignumber.js'
import { useMemo, useState } from 'react'
import Button from 'components/Button'
import DepositCapMessage from 'components/DepositCapMessage'
import DisplayCurrency from 'components/DisplayCurrency'
import Divider from 'components/Divider'
import { Gauge } from 'components/Gauge'
import { ArrowRight, ExclamationMarkCircled } from 'components/Icons'
import Slider from 'components/Slider'
import Switch from 'components/Switch'
import Text from 'components/Text'
import TokenInput from 'components/TokenInput'
import Button from 'components/common/Button'
import DepositCapMessage from 'components/common/DepositCapMessage'
import DisplayCurrency from 'components/common/DisplayCurrency'
import Divider from 'components/common/Divider'
import { Gauge } from 'components/common/Gauge'
import { ArrowRight, ExclamationMarkCircled } from 'components/common/Icons'
import Slider from 'components/common/Slider'
import Switch from 'components/common/Switch'
import Text from 'components/common/Text'
import TokenInput from 'components/common/TokenInput'
import { BN_ZERO } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle'
import useAllAssets from 'hooks/assets/useAllAssets'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import { accumulateAmounts } from 'utils/accounts'
@@ -41,6 +42,7 @@ export default function VaultDeposit(props: Props) {
[account.deposits, account.lends, primaryAsset.denom, secondaryAsset.denom],
)
const { data: prices } = usePrices()
const assets = useAllAssets()
const primaryPrice = prices.find(byDenom(primaryAsset.denom))?.amount ?? BN_ZERO
const secondaryPrice = prices.find(byDenom(secondaryAsset.denom))?.amount ?? BN_ZERO
@@ -55,13 +57,13 @@ export default function VaultDeposit(props: Props) {
}, [deposits, secondaryAsset.denom])
const primaryValue = useMemo(
() => getValueFromBNCoins([primaryCoin], prices),
[primaryCoin, prices],
() => getValueFromBNCoins([primaryCoin], prices, assets),
[primaryCoin, prices, assets],
)
const totalValue = useMemo(
() => getValueFromBNCoins([primaryCoin, secondaryCoin], prices),
[primaryCoin, secondaryCoin, prices],
() => getValueFromBNCoins([primaryCoin, secondaryCoin], prices, assets),
[primaryCoin, secondaryCoin, prices, assets],
)
const primaryValuePercentage = useMemo(() => {
@@ -2,13 +2,14 @@ import BigNumber from 'bignumber.js'
import classNames from 'classnames'
import { useMemo } from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import Text from 'components/Text'
import DisplayCurrency from 'components/common/DisplayCurrency'
import Text from 'components/common/Text'
import { ORACLE_DENOM } from 'constants/oracle'
import useAllAssets from 'hooks/assets/useAllAssets'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import { formatAmountWithSymbol } from 'utils/formatters'
import { getValueFromBNCoins } from 'utils/helpers'
import { ORACLE_DENOM } from 'constants/oracle'
interface Props {
primaryAmount: BigNumber
@@ -20,28 +21,35 @@ interface Props {
export default function VaultDepositSubTitle(props: Props) {
const { data: prices } = usePrices()
const assets = useAllAssets()
const primaryText = useMemo(
() => (
<Text size='xs' className='inline mt-1 text-white/60'>
{formatAmountWithSymbol({
denom: props.primaryAsset.denom,
amount: props.primaryAmount.toString(),
})}
{formatAmountWithSymbol(
{
denom: props.primaryAsset.denom,
amount: props.primaryAmount.toString(),
},
assets,
)}
</Text>
),
[props.primaryAmount, props.primaryAsset.denom],
[assets, props.primaryAmount, props.primaryAsset.denom],
)
const secondaryText = useMemo(
() => (
<Text size='xs' className='inline mt-1 text-white/60 ml-1 before:pr-1 before:content-["+"]'>
{formatAmountWithSymbol({
denom: props.secondaryAsset.denom,
amount: props.secondaryAmount.toString(),
})}
{formatAmountWithSymbol(
{
denom: props.secondaryAsset.denom,
amount: props.secondaryAmount.toString(),
},
assets,
)}
</Text>
),
[props.secondaryAmount, props.secondaryAsset.denom],
[assets, props.secondaryAmount, props.secondaryAsset.denom],
)
const positionValue = getValueFromBNCoins(
@@ -50,6 +58,7 @@ export default function VaultDepositSubTitle(props: Props) {
BNCoin.fromDenomAndBigNumber(props.secondaryAsset.denom, props.secondaryAmount),
],
prices,
assets,
)
const showPrimaryText = !props.primaryAmount.isZero()
@@ -1,19 +1,18 @@
import { useCallback, useMemo, useState } from 'react'
import Accordion from 'components/Accordion'
import AccountSummary from 'components/Account/AccountSummary'
import Accordion from 'components/common/Accordion'
import AccountSummary from 'components/account/AccountSummary'
import VaultBorrowings from 'components/Modals/Vault/VaultBorrowings'
import VaultBorrowingsSubTitle from 'components/Modals/Vault/VaultBorrowingsSubTitle'
import VaultDeposit from 'components/Modals/Vault/VaultDeposits'
import VaultDepositSubTitle from 'components/Modals/Vault/VaultDepositsSubTitle'
import Text from 'components/Text'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import Text from 'components/common/Text'
import { BN_ZERO } from 'constants/math'
import useAllAssets from 'hooks/assets/useAllAssets'
import useDepositVault from 'hooks/broadcast/useDepositVault'
import useDisplayCurrency from 'hooks/localStorage/useDisplayCurrency'
import useDisplayAsset from 'hooks/useDisplayAsset'
import useIsOpenArray from 'hooks/useIsOpenArray'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import { BNCoin } from 'types/classes/BNCoin'
@@ -33,12 +32,10 @@ export default function VaultModalContent(props: Props) {
const { addedDebts, removedDeposits, removedLends, simulateVaultDeposit } = useUpdatedAccount(
props.account,
)
const assets = useAllAssets()
const { data: prices } = usePrices()
const [displayCurrency] = useLocalStorage<string>(
LocalStorageKeys.DISPLAY_CURRENCY,
DEFAULT_SETTINGS.displayCurrency,
)
const [displayCurrency] = useDisplayCurrency()
const [isOpen, toggleOpen] = useIsOpenArray(2, false)
const [isCustomRatio, setIsCustomRatio] = useState(false)
const [depositCoins, setDepositCoins] = useState<BNCoin[]>([])
@@ -60,6 +57,7 @@ export default function VaultModalContent(props: Props) {
getCoinValue(
BNCoin.fromDenomAndBigNumber(props.vault.cap.denom, capLeft),
prices,
assets,
).toString(),
displayAsset,
)
@@ -67,7 +65,7 @@ export default function VaultModalContent(props: Props) {
return [BNCoin.fromDenomAndBigNumber(displayAsset.denom, amount)]
}
return []
}, [displayAsset, prices, props.vault.cap, totalValue])
}, [assets, displayAsset, prices, props.vault.cap, totalValue])
const onChangeIsCustomRatio = useCallback(
(isCustomRatio: boolean) => setIsCustomRatio(isCustomRatio),
@@ -1,8 +1,8 @@
import { useMemo } from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import TitleAndSubCell from 'components/TitleAndSubCell'
import DisplayCurrency from 'components/common/DisplayCurrency'
import { FormattedNumber } from 'components/common/FormattedNumber'
import TitleAndSubCell from 'components/common/TitleAndSubCell'
import { BN_ZERO } from 'constants/math'
import { PRICE_ORACLE_DECIMALS } from 'constants/query'
import { BNCoin } from 'types/classes/BNCoin'
+10 -9
View File
@@ -1,22 +1,23 @@
import moment from 'moment'
import { useCallback, useMemo } from 'react'
import DoubleLogo from 'components/DoubleLogo'
import { InfoCircle } from 'components/Icons'
import Modal from 'components/Modal'
import DoubleLogo from 'components/common/DoubleLogo'
import { InfoCircle } from 'components/common/Icons'
import Text from 'components/common/Text'
import { Tooltip } from 'components/common/Tooltip'
import Modal from 'components/Modals/Modal'
import VaultModalContent from 'components/Modals/Vault/VaultModalContent'
import VaultModalContentHeader from 'components/Modals/Vault/VaultModalContentHeader'
import Text from 'components/Text'
import { Tooltip } from 'components/Tooltip'
import { ASSETS } from 'constants/assets'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useAsset from 'hooks/assets/useAsset'
import useStore from 'store'
export default function VaultModalController() {
const currentAccount = useCurrentAccount()
const modal = useStore((s) => s.vaultModal)
const primaryAsset = ASSETS.find((asset) => asset.denom === modal?.vault.denoms.primary)
const secondaryAsset = ASSETS.find((asset) => asset.denom === modal?.vault.denoms.secondary)
const primaryAsset = useAsset(modal?.vault.denoms.primary || '')
const secondaryAsset = useAsset(modal?.vault.denoms.secondary || '')
if (!modal || !currentAccount || !primaryAsset || !secondaryAsset) return null
@@ -1,10 +1,10 @@
import { useCallback, useMemo, useState } from 'react'
import AssetSelectTable from 'components/Modals/AssetsSelect/AssetSelectTable'
import SearchBar from 'components/SearchBar'
import AssetsSelect from 'components/Modals/AssetsSelect'
import SearchBar from 'components/common/SearchBar'
import useAllAssets from 'hooks/assets/useAllAssets'
import useStore from 'store'
import { byDenom } from 'utils/array'
import { getAssetByDenom } from 'utils/assets'
interface Props {
onChangeDenoms: (denoms: string[]) => void
@@ -14,25 +14,26 @@ export default function WalletAssetsModalContent(props: Props) {
const { onChangeDenoms } = props
const [searchString, setSearchString] = useState<string>('')
const balances = useStore((s) => s.balances)
const assets = useAllAssets()
const assets = useMemo(() => {
const assetsInWallet: Asset[] = []
const assetsInWallet = useMemo(() => {
const assetsInWalletInWallet: Asset[] = []
balances.forEach((balance) => {
const asset = getAssetByDenom(balance.denom)
if (asset && asset.isMarket) assetsInWallet.push(asset)
const asset = assets.find(byDenom(balance.denom))
if (asset && asset.isMarket) assetsInWalletInWallet.push(asset)
})
return assetsInWallet
}, [balances])
return assetsInWalletInWallet
}, [assets, balances])
const filteredAssets: Asset[] = useMemo(() => {
return assets.filter(
return assetsInWallet.filter(
(asset) =>
asset.name.toLowerCase().includes(searchString.toLowerCase()) ||
asset.denom.toLowerCase().includes(searchString.toLowerCase()) ||
asset.symbol.toLowerCase().includes(searchString.toLowerCase()),
)
}, [assets, searchString])
}, [assetsInWallet, searchString])
const isBorrow = useStore((s) => s.walletAssetsModal?.isBorrow ?? false)
const currentSelectedDenom = useStore((s) => s.walletAssetsModal?.selectedDenoms ?? [])
@@ -58,8 +59,7 @@ export default function WalletAssetsModalContent(props: Props) {
/>
</div>
<div className='max-h-[446px] overflow-y-scroll scrollbar-hide'>
<AssetSelectTable
isBorrow={isBorrow}
<AssetsSelect
assets={filteredAssets}
onChangeSelected={onChangeSelect}
selectedDenoms={selectedDenoms}
+3 -3
View File
@@ -1,9 +1,9 @@
import { useCallback, useState } from 'react'
import Button from 'components/Button'
import Modal from 'components/Modal'
import Button from 'components/common/Button'
import Modal from 'components/Modals/Modal'
import WalletAssetsModalContent from 'components/Modals/WalletAssets/WalletAssetsModalContent'
import Text from 'components/Text'
import Text from 'components/common/Text'
import useStore from 'store'
export default function WalletAssetsModal() {
@@ -1,20 +1,20 @@
import Button from 'components/Button'
import { CircularProgress } from 'components/CircularProgress'
import DisplayCurrency from 'components/DisplayCurrency'
import DoubleLogo from 'components/DoubleLogo'
import { FormattedNumber } from 'components/FormattedNumber'
import Modal from 'components/Modal'
import Text from 'components/Text'
import Modal from 'components/Modals/Modal'
import Button from 'components/common/Button'
import { CircularProgress } from 'components/common/CircularProgress'
import DisplayCurrency from 'components/common/DisplayCurrency'
import DoubleLogo from 'components/common/DoubleLogo'
import { FormattedNumber } from 'components/common/FormattedNumber'
import Text from 'components/common/Text'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import { ORACLE_DENOM } from 'constants/oracle'
import useAllAssets from 'hooks/assets/useAllAssets'
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useAccountId from 'hooks/useAccountId'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { getAssetByDenom } from 'utils/assets'
export default function WithdrawFromVaultsModal() {
const modal = useStore((s) => s.withdrawFromVaultsModal)
@@ -22,6 +22,7 @@ export default function WithdrawFromVaultsModal() {
const { data: prices } = usePrices()
const withdrawFromVaults = useStore((s) => s.withdrawFromVaults)
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
const assets = useAllAssets()
function onClose() {
useStore.setState({ withdrawFromVaultsModal: null })
@@ -56,8 +57,8 @@ export default function WithdrawFromVaultsModal() {
{modal.map((vault) => {
const positionValue = vault.values.unlocking
const coin = BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, positionValue)
const primaryAsset = getAssetByDenom(vault.denoms.primary)
const secondaryAsset = getAssetByDenom(vault.denoms.secondary)
const primaryAsset = assets.find(byDenom(vault.denoms.primary))
const secondaryAsset = assets.find(byDenom(vault.denoms.secondary))
if (!primaryAsset || !secondaryAsset) return null
const primaryAssetPrice = prices.find(byDenom(primaryAsset.denom))?.amount ?? 1
@@ -72,17 +73,19 @@ export default function WithdrawFromVaultsModal() {
primaryDenom={vault.denoms.primary}
secondaryDenom={vault.denoms.secondary}
/>
<div className='flex flex-wrap flex-1'>
<Text className='w-full'>{vault.name}</Text>
<Text size='sm' className='w-full text-white/50'>
<div className='flex flex-wrap flex-grow'>
<Text size='sm' className='w-full'>
{vault.name}
</Text>
<Text size='xs' className='w-full text-white/50'>
Unlocked
</Text>
</div>
<div className='flex flex-wrap'>
<DisplayCurrency coin={coin} className='w-full text-right' />
<div className='flex flex-wrap flex-shrink max-w-1/2'>
<DisplayCurrency coin={coin} className='w-full text-sm text-right' />
<FormattedNumber
amount={Number(primaryAssetAmount.toPrecision(4))}
className='w-full text-sm text-right text-white/50'
className='w-full text-xs text-right text-white/50'
options={{
suffix: ` ${vault.symbols.primary}`,
maxDecimals: primaryAsset.decimals,
@@ -91,7 +94,7 @@ export default function WithdrawFromVaultsModal() {
/>
<FormattedNumber
amount={Number(secondaryAssetAmount.toPrecision(4))}
className='w-full text-sm text-right text-white/50'
className='w-full text-xs text-right text-white/50'
options={{
suffix: ` ${vault.symbols.secondary}`,
maxDecimals: secondaryAsset.decimals,
-80
View File
@@ -1,80 +0,0 @@
import classNames from 'classnames'
import Button from 'components/Button'
import Divider from 'components/Divider'
import { ChevronDown } from 'components/Icons'
import { NavLink } from 'components/Navigation//NavLink'
import { getIsActive } from 'components/Navigation/DesktopNavigation'
import Text from 'components/Text'
import useToggle from 'hooks/useToggle'
interface Props {
item: MenuTreeEntry
}
export const NavMenu = (props: Props) => {
const { item } = props
const [showMenu, setShowMenu] = useToggle()
if (!item.submenu) return null
return (
<div className='relative flex items-center'>
<Button
rightIcon={<ChevronDown className='w-3' />}
color='quaternary'
variant='transparent'
onClick={() => setShowMenu(!showMenu)}
text={item.label}
className={classNames(
'!text-base !p-0 !min-h-0',
(getIsActive(item.pages) || showMenu) && '!text-white',
)}
/>
{showMenu && (
<>
<div className='absolute left-0 top-[calc(100%+4px)] z-50'>
<ul
className={classNames(
'py-4 list-none flex flex-wrap gap-2 bg-white/10 backdrop-blur-lg',
'relative isolate max-w-full overflow-hidden rounded-sm',
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-sm before:p-[1px] before:border-glas',
)}
>
{item.submenu.map((submenuitem, index) => (
<li className='w-full m-0 group/submenuitem' key={index}>
{index !== 0 && <Divider className='mb-2' />}
<NavLink
item={{ pages: [submenuitem.page], label: submenuitem.label }}
onClick={() => setShowMenu(false)}
className='flex items-center w-full gap-4 px-4 whitespace-nowrap'
>
{submenuitem.icon && <div className='w-6'>{submenuitem.icon}</div>}
<Text className='flex flex-wrap'>
{submenuitem.label}
{submenuitem.subtitle && (
<span
className={classNames(
'w-full text-sm group-hover/submenuitem:text-white',
getIsActive([submenuitem.page]) ? 'text-white' : 'text-white/40',
)}
>
{submenuitem.subtitle}
</span>
)}
</Text>
</NavLink>
</li>
))}
</ul>
</div>
<div
className='fixed -top-6 -left-[55px] z-40 w-screen h-screen hover:cursor-pointer'
onClick={() => setShowMenu(false)}
role='button'
/>
</>
)}
</div>
)
}
@@ -1,59 +0,0 @@
import { useState } from 'react'
import Button from 'components/Button'
import Card from 'components/Card'
import { DirectionSelect } from 'components/DirectionSelect'
import { ChevronDown } from 'components/Icons'
import { LeverageButtons } from 'components/Perps/Module/LeverageButtons'
import { Or } from 'components/Perps/Module/Or'
import RangeInput from 'components/RangeInput'
import { Spacer } from 'components/Spacer'
import Text from 'components/Text'
import AssetAmountInput from 'components/Trade/TradeModule/SwapForm/AssetAmountInput'
import OrderTypeSelector from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector'
import { AvailableOrderType } from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector/types'
import { ASSETS } from 'constants/assets'
import { BN_ZERO } from 'constants/math'
export function PerpsModule() {
const [selectedOrderType, setSelectedOrderType] = useState<AvailableOrderType>('Market')
const [selectedOrderDirection, setSelectedOrderDirection] = useState<OrderDirection>('long')
return (
<Card
contentClassName='px-4 gap-5 flex flex-col'
title={
<div className='flex items-center justify-between py-4 pl-4 pr-2 bg-white/10'>
<Text>
ETH<span className='text-white/60'>/USD</span>
</Text>
<Button color='quaternary' variant='transparent' rightIcon={<ChevronDown />}>
All Markets
</Button>
</div>
}
className='mb-4'
>
<OrderTypeSelector selected={selectedOrderType} onChange={setSelectedOrderType} />
<DirectionSelect
direction={selectedOrderDirection}
onChangeDirection={setSelectedOrderDirection}
/>
<AssetAmountInput
label='Amount'
max={BN_ZERO}
amount={BN_ZERO}
setAmount={() => {}}
asset={ASSETS[0]}
maxButtonLabel='Max:'
disabled={false}
/>
<Or />
<Text size='sm'>Position Leverage</Text>
<RangeInput max={0} value={0} onChange={() => {}} />
<LeverageButtons />
<Spacer />
<Button>{selectedOrderDirection} ETH</Button>
</Card>
)
}
-12
View File
@@ -1,12 +0,0 @@
import React from 'react'
import TradeChart from 'components/Trade/TradeChart'
import { ASSETS } from 'constants/assets'
export function PerpsChart() {
return (
<div className='h-full'>
<TradeChart buyAsset={ASSETS[0]} sellAsset={ASSETS[1]} />
</div>
)
}
-99
View File
@@ -1,99 +0,0 @@
import React, { useMemo } from 'react'
import AssetSymbol from 'components/Asset/AssetSymbol'
import Card from 'components/Card'
import DisplayCurrency from 'components/DisplayCurrency'
import Divider from 'components/Divider'
import { FormattedNumber } from 'components/FormattedNumber'
import Loading from 'components/Loading'
import Text from 'components/Text'
import usePerpsMarket from 'hooks/perps/usePerpsMarket'
import usePrice from 'hooks/usePrice'
import { BNCoin } from 'types/classes/BNCoin'
export function PerpsInfo() {
const { data: market } = usePerpsMarket()
const assetPrice = usePrice(market?.asset.denom || '')
const items = useMemo(
() => [
...(!assetPrice.isZero()
? [<DisplayCurrency key='price' coin={BNCoin.fromDenomAndBigNumber('usd', assetPrice)} />]
: [<Loading key='price' className='w-14 h-4' />]),
<InfoItem
key='openInterestLong'
label='Open Interest (L)'
item={<InterestItem market={market} type='long' />}
/>,
<InfoItem
key='openInterestShort'
label='Open Interest (S)'
item={<InterestItem market={market} type='short' />}
/>,
<InfoItem
key='fundingRate'
label='Funding rate'
item={
market ? (
<FormattedNumber
className='text-sm inline'
amount={market.fundingRate.toNumber()}
options={{ minDecimals: 6, maxDecimals: 6, suffix: '%' }}
/>
) : (
<Loading />
)
}
/>,
],
[assetPrice, market],
)
return (
<Card contentClassName='bg-white/10 py-3.5 px-4'>
<div className='flex gap-4 items-center'>
{items.map((item, index) => (
<React.Fragment key={index}>
{item}
{index !== items.length - 1 && <Divider orientation='vertical' className='h-9' />}
</React.Fragment>
))}
</div>
</Card>
)
}
interface InfoItemProps {
item: React.ReactNode
label: string
}
function InfoItem(props: InfoItemProps) {
return (
<div className='flex flex-col gap-1'>
<Text size='xs' className='text-white/40'>
{props.label}
</Text>
{props.item}
</div>
)
}
interface InterestItemProps {
market: PerpsMarket | null
type: 'long' | 'short'
}
function InterestItem(props: InterestItemProps) {
if (!props.market) return <Loading />
return (
<div className='flex gap-1 items-center'>
<FormattedNumber
className='text-sm inline'
amount={props.market.openInterest[props.type].toNumber()}
options={{ decimals: props.market.asset.decimals }}
/>
<AssetSymbol symbol={props.market.asset.symbol} />
</div>
)
}
-5
View File
@@ -1,5 +0,0 @@
import AccountDetailsCard from 'components/Trade/AccountDetailsCard'
export function PerpsPositions() {
return <AccountDetailsCard />
}
-63
View File
@@ -1,63 +0,0 @@
import { flexRender, Row as TanstackRow, Table as TanstackTable } from '@tanstack/react-table'
import classNames from 'classnames'
interface Props<T> {
row: TanstackRow<T>
table: TanstackTable<T>
renderExpanded?: (row: TanstackRow<T>, table: TanstackTable<T>) => JSX.Element
rowClassName?: string
rowClickHandler?: () => void
spacingClassName?: string
isBalancesTable?: boolean
className?: string
}
function getBorderColor(row: AccountBalanceRow) {
return row.type === 'borrowing' ? 'border-loss' : 'border-profit'
}
export default function Row<T>(props: Props<T>) {
const canExpand = !!props.renderExpanded
return (
<>
<tr
key={`${props.row.id}-row`}
className={classNames(
'group/row transition-bg',
props.renderExpanded && 'hover:cursor-pointer',
canExpand && props.row.getIsExpanded() ? 'is-expanded bg-black/20' : 'hover:bg-white/5',
)}
onClick={(e) => {
e.preventDefault()
const isExpanded = props.row.getIsExpanded()
props.table.resetExpanded()
!isExpanded && props.row.toggleExpanded()
}}
>
{props.row.getVisibleCells().map((cell) => {
const isSymbolOrName = cell.column.id === 'symbol' || cell.column.id === 'name'
const borderClasses =
props.isBalancesTable && isSymbolOrName
? classNames('border-l', getBorderColor(cell.row.original as AccountBalanceRow))
: ''
return (
<td
key={cell.id}
className={classNames(
isSymbolOrName ? 'text-left' : 'text-right',
props.spacingClassName ?? 'px-3 py-4',
borderClasses,
cell.column.columnDef.meta?.className,
)}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
)
})}
</tr>
{props.row.getIsExpanded() &&
props.renderExpanded &&
props.renderExpanded(props.row, props.table)}
</>
)
}
-500
View File
@@ -1,500 +0,0 @@
import { defaultSymbolInfo } from 'components/Trade/TradeChart/constants'
import { ASSETS } from 'constants/assets'
import { ENV } from 'constants/env'
import { MILLISECONDS_PER_MINUTE } from 'constants/math'
import { byDenom } from 'utils/array'
import { getAssetByDenom, getEnabledMarketAssets } from 'utils/assets'
import {
Bar,
ErrorCallback,
HistoryCallback,
IDatafeedChartApi,
LibrarySymbolInfo,
OnReadyCallback,
PeriodParams,
ResolutionString,
ResolveCallback,
} from 'utils/charting_library'
import { BN } from 'utils/helpers'
import { devideByPotentiallyZero } from 'utils/math'
interface PythBarQueryData {
s: string
t: number[]
o: number[]
h: number[]
l: number[]
c: number[]
v: number[]
}
interface TheGraphBarQueryData {
close: string
high: string
low: string
open: string
timestamp: string
volume: string
}
export const PAIR_SEPARATOR = '<>'
export class DataFeed implements IDatafeedChartApi {
candlesEndpoint = ENV.CANDLES_ENDPOINT_PYTH
candlesEndpointTheGraph = ENV.CANDLES_ENDPOINT_THE_GRAPH
debug = false
enabledMarketAssetDenoms: string[] = []
batchSize = 1000
baseDecimals: number = 6
baseDenom: string = 'uosmo'
intervalsTheGraph: { [key: string]: string } = {
'15': '15m',
'30': '30m',
'60': '1h',
'240': '4h',
'1D': '1d',
}
millisecondsPerInterval: { [key: string]: number } = {
'1': MILLISECONDS_PER_MINUTE * 1,
'5': MILLISECONDS_PER_MINUTE * 5,
'15': MILLISECONDS_PER_MINUTE * 15,
'30': MILLISECONDS_PER_MINUTE * 30,
'60': MILLISECONDS_PER_MINUTE * 60,
'240': MILLISECONDS_PER_MINUTE * 240,
'1D': MILLISECONDS_PER_MINUTE * 1440,
}
pairs: { baseAsset: string; quoteAsset: string }[] = []
pairsWithData: string[] = []
supportedPools: string[] = []
supportedResolutions = ['1', '5', '15', '30', '60', '240', 'D'] as ResolutionString[]
constructor(debug = false, baseDecimals: number, baseDenom: string) {
if (debug) console.log('Start charting library datafeed')
this.debug = debug
this.baseDecimals = baseDecimals
this.baseDenom = baseDenom
const enabledMarketAssets = getEnabledMarketAssets()
this.enabledMarketAssetDenoms = enabledMarketAssets.map((asset) => asset.denom)
this.supportedPools = enabledMarketAssets
.map((asset) => asset.poolId?.toString())
.filter((poolId) => typeof poolId === 'string') as string[]
}
getDescription(pairName: string, inverted: boolean) {
const [denom1, denom2] = pairName.split(PAIR_SEPARATOR)
const asset1 = ASSETS.find(byDenom(denom1))
const asset2 = ASSETS.find(byDenom(denom2))
return inverted ? `${asset2?.symbol}/${asset1?.symbol}` : `${asset1?.symbol}/${asset2?.symbol}`
}
async getPairsWithData() {
const query = `
{
pairs(first: ${this.batchSize},
orderBy: symbol,
orderDirection: asc,
where: {
baseAsset_in: ${JSON.stringify(this.enabledMarketAssetDenoms)},
quoteAsset_in: ${JSON.stringify(this.enabledMarketAssetDenoms)},
poolId_in: ${JSON.stringify(this.supportedPools)}
}
) {
baseAsset
quoteAsset
}
}`
return fetch(this.candlesEndpointTheGraph, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query }),
})
.then((res) => res.json())
.then((json) => {
this.pairs = json.data.pairs
this.pairsWithData = json.data.pairs.map(
(pair: { baseAsset: string; quoteAsset: string }) => {
return `${pair.quoteAsset}${PAIR_SEPARATOR}${pair.baseAsset}`
},
)
})
.catch((err) => {
if (this.debug) console.error(err)
throw err
})
}
onReady(callback: OnReadyCallback) {
const configurationData = {
supported_resolutions: this.supportedResolutions,
}
setTimeout(async () => {
await this.getPairsWithData()
})
callback(configurationData)
}
resolveSymbol(pairName: string, onResolve: ResolveCallback, onError: ErrorCallback) {
pairName = this.getPairName(pairName)
setTimeout(() => {
const info: LibrarySymbolInfo = {
...defaultSymbolInfo,
name: this.getDescription(pairName, false),
full_name: this.getDescription(pairName, true),
description: this.getDescription(pairName, true),
ticker: this.getDescription(pairName, false),
exchange: this.getExchangeName(pairName),
listed_exchange: this.getExchangeName(pairName),
supported_resolutions: this.supportedResolutions,
base_name: [this.getDescription(pairName, false)],
pricescale: this.getPriceScale(pairName),
} as LibrarySymbolInfo
onResolve(info)
})
}
async getBars(
symbolInfo: LibrarySymbolInfo,
resolution: ResolutionString,
periodParams: PeriodParams,
onResult: HistoryCallback,
): Promise<void> {
try {
let bars = [] as Bar[]
const pythFeedIds = this.getPythFeedIds(symbolInfo.full_name)
const now = new Date().getTime()
const to = BN(now).dividedBy(1000).integerValue().toNumber()
const from = BN(now)
.minus(this.batchSize * this.millisecondsPerInterval[resolution])
.dividedBy(1000)
.integerValue()
.toNumber()
const pythFeedId1 = pythFeedIds[0]
const pythFeedId2 = pythFeedIds[1]
if (pythFeedId1 && pythFeedId2) {
const asset1Bars = this.queryBarData(pythFeedId1, resolution, from, to)
const asset2Bars = this.queryBarData(pythFeedId2, resolution, from, to)
await Promise.all([asset1Bars, asset2Bars]).then(([asset1Bars, asset2Bars]) => {
bars = this.combineBars(asset1Bars, asset2Bars)
onResult(bars)
})
} else {
//await this.getBarsFromTheGraph(symbolInfo, resolution, to).then((bars) => onResult(bars))
onResult([], { noData: true })
}
} catch (error) {
console.error(error)
return onResult([], { noData: true })
}
}
async getBarsFromTheGraph(
symbolInfo: LibrarySymbolInfo,
resolution: ResolutionString,
to: number,
) {
let pair1 = this.getPairName(symbolInfo.full_name)
let pair2: string = ''
let pair3: string = ''
let theGraphBars = [] as Bar[]
if (!this.pairsWithData.includes(pair1)) {
if (this.debug) console.log('Pair does not have data, need to combine with other pairs')
const [buyAssetDenom, sellAssetDenom] = pair1.split(PAIR_SEPARATOR)
const pair1Pools = this.pairs.filter((pair) => pair.baseAsset === buyAssetDenom)
const pair2Pools = this.pairs.filter((pair) => pair.quoteAsset === sellAssetDenom)
const matchedPools = pair1Pools.filter((pool) => {
const asset = pool.quoteAsset
return !!pair2Pools.find((pool) => pool.baseAsset === asset)
})
if (matchedPools.length) {
pair1 = `${buyAssetDenom}${PAIR_SEPARATOR}${matchedPools[0].quoteAsset}`
pair2 = `${matchedPools[0].quoteAsset}${PAIR_SEPARATOR}${sellAssetDenom}`
} else {
const middlePair = this.pairs.filter(
(pair) =>
pair1Pools.map((pairs) => pairs.quoteAsset).includes(pair.baseAsset) &&
pair2Pools.map((pairs) => pairs.baseAsset).includes(pair.quoteAsset),
)
pair1 = `${buyAssetDenom}${PAIR_SEPARATOR}${middlePair[0].baseAsset}`
pair2 = `${middlePair[0].baseAsset}${PAIR_SEPARATOR}${middlePair[0].quoteAsset}`
pair3 = `${middlePair[0].quoteAsset}${PAIR_SEPARATOR}${sellAssetDenom}`
}
}
const pair1Bars = this.queryBarDataTheGraph(
pair1.split(PAIR_SEPARATOR)[0],
pair1.split(PAIR_SEPARATOR)[1],
resolution,
to,
)
let pair2Bars: Promise<Bar[]> | null = null
if (pair2) {
pair2Bars = this.queryBarDataTheGraph(
pair2.split(PAIR_SEPARATOR)[0],
pair2.split(PAIR_SEPARATOR)[1],
resolution,
to,
)
}
let pair3Bars: Promise<Bar[]> | null = null
if (pair3) {
pair3Bars = this.queryBarDataTheGraph(
pair3.split(PAIR_SEPARATOR)[0],
pair3.split(PAIR_SEPARATOR)[1],
resolution,
to,
)
}
await Promise.all([pair1Bars, pair2Bars, pair3Bars]).then(
([pair1Bars, pair2Bars, pair3Bars]) => {
let bars = pair1Bars
if (!bars.length) {
return
}
if (pair2Bars) {
bars = this.combineBars(pair1Bars, pair2Bars)
}
if (pair3Bars) {
bars = this.combineBars(bars, pair3Bars)
}
const filler = Array.from({ length: this.batchSize - bars.length }).map((_, index) => ({
time:
(bars[0]?.time || new Date().getTime()) -
(index * this.millisecondsPerInterval[resolution]) / 1000,
close: 0,
open: 0,
high: 0,
low: 0,
volume: 0,
}))
theGraphBars = [...filler, ...bars]
},
)
return theGraphBars
}
async queryBarData(
feedSymbol: string,
resolution: ResolutionString,
from: PeriodParams['from'],
to: PeriodParams['to'],
): Promise<Bar[]> {
const URI = new URL('/v1/shims/tradingview/history', this.candlesEndpoint)
const params = new URLSearchParams(URI.search)
params.append('to', to.toString())
params.append('from', from.toString())
params.append('resolution', resolution)
params.append('symbol', feedSymbol)
URI.search = params.toString()
return fetch(URI)
.then((res) => res.json())
.then((json) => {
return this.resolveBarData(json, resolution, to)
})
.catch((err) => {
if (this.debug) console.error(err)
throw err
})
}
async queryBarDataTheGraph(
quote: string,
base: string,
resolution: ResolutionString,
to: PeriodParams['to'],
): Promise<Bar[]> {
const interval = this.intervalsTheGraph[resolution]
const query = `
{
candles(
first: ${this.batchSize},
orderBy: "timestamp",
orderDirection: "desc",
where: {
interval: "${interval}",
quote: "${quote}",
base: "${base}"
poolId_in: ${JSON.stringify(this.supportedPools)}
}
) {
timestamp
open
high
low
close
volume
}
}
`
return fetch(this.candlesEndpointTheGraph, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query }),
})
.then((res) => res.json())
.then((json: { data?: { candles: TheGraphBarQueryData[] } }) => {
return this.resolveBarDataTheGraph(
json.data?.candles.reverse() || [],
base,
quote,
resolution,
to,
)
})
.catch((err) => {
if (this.debug) console.error(err)
throw err
})
}
resolveBarData(data: PythBarQueryData, resolution: ResolutionString, to: number) {
let barData = [] as Bar[]
if (data['s'] === 'ok') {
barData = data['t'].map((timestamp, index) => ({
time: timestamp * 1000,
close: data['c'][index],
open: data['o'][index],
high: data['h'][index],
low: data['l'][index],
}))
}
return this.fillBarData(barData, resolution, to)
}
resolveBarDataTheGraph(
bars: TheGraphBarQueryData[],
toDenom: string,
fromDenom: string,
resolution: ResolutionString,
to: number,
) {
let barData = [] as Bar[]
const toDecimals = getAssetByDenom(toDenom)?.decimals || 6
const fromDecimals = getAssetByDenom(fromDenom)?.decimals || 6
const additionalDecimals = toDecimals - fromDecimals
barData = bars.map((bar) => ({
time: BN(bar.timestamp).multipliedBy(1000).toNumber(),
close: BN(bar.close).shiftedBy(additionalDecimals).toNumber(),
open: BN(bar.open).shiftedBy(additionalDecimals).toNumber(),
high: BN(bar.high).shiftedBy(additionalDecimals).toNumber(),
low: BN(bar.low).shiftedBy(additionalDecimals).toNumber(),
volume: BN(bar.volume).shiftedBy(additionalDecimals).toNumber(),
}))
return this.fillBarData(barData, resolution, to)
}
fillBarData(barData: Bar[], resolution: ResolutionString, to: number) {
if (barData.length < this.batchSize) {
const filler = Array.from({ length: this.batchSize - barData.length }).map((_, index) => ({
time: (barData[0]?.time || to) - index * this.millisecondsPerInterval[resolution],
close: 0,
open: 0,
high: 0,
low: 0,
volume: 0,
}))
barData = [...filler, ...barData]
}
return barData.length > this.batchSize ? barData.slice(0, this.batchSize) : barData
}
combineBars(pair1Bars: Bar[], pair2Bars: Bar[]): Bar[] {
const bars: Bar[] = []
pair1Bars.forEach((pair1Bar, index) => {
const pair2Bar = pair2Bars[index]
bars.push({
time: pair1Bar.time,
open: devideByPotentiallyZero(pair1Bar.open, pair2Bar.open),
close: devideByPotentiallyZero(pair1Bar.close, pair2Bar.close),
high: devideByPotentiallyZero(pair1Bar.high, pair2Bar.high),
low: devideByPotentiallyZero(pair1Bar.low, pair2Bar.low),
})
})
return bars
}
getPairName(name: string) {
if (name.includes(PAIR_SEPARATOR)) return name
const [symbol1, symbol2] = name.split('/')
const asset1 = ASSETS.find((asset) => asset.symbol === symbol1)
const asset2 = ASSETS.find((asset) => asset.symbol === symbol2)
return `${asset1?.denom}${PAIR_SEPARATOR}${asset2?.denom}`
}
getPriceScale(name: string) {
const denoms = name.split(PAIR_SEPARATOR)
const asset2 = ASSETS.find(byDenom(denoms[1]))
const decimalsOut = asset2?.decimals ?? 6
return BN(1)
.shiftedBy(decimalsOut > 8 ? 8 : decimalsOut)
.toNumber()
}
getExchangeName(name: string) {
const denoms = name.split(PAIR_SEPARATOR)
const pythFeedId1 = ASSETS.find(byDenom(denoms[0]))?.pythHistoryFeedId
const pythFeedId2 = ASSETS.find(byDenom(denoms[1]))?.pythHistoryFeedId
//if (!pythFeedId1 || !pythFeedId2) return 'Osmosis'
return 'Pyth Oracle'
}
getPythFeedIds(name: string) {
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 feedId1 = ASSETS.find((asset) => asset.symbol === symbol1)?.pythHistoryFeedId
const feedId2 = ASSETS.find((asset) => asset.symbol === symbol2)?.pythHistoryFeedId
return [feedId1, feedId2]
}
searchSymbols(): void {
// Don't allow to search for symbols
}
subscribeBars(): void {
// TheGraph doesn't support websockets yet
}
unsubscribeBars(listenerGuid: string): void {
// TheGraph doesn't support websockets yet
}
}
@@ -1,166 +0,0 @@
import { useEffect, useMemo, useRef } from 'react'
import Card from 'components/Card'
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 PoweredByPyth from 'components/Trade/TradeChart/PoweredByPyth'
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 { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import {
ChartingLibraryWidgetOptions,
IChartingLibraryWidget,
ResolutionString,
Timezone,
widget,
} from 'utils/charting_library'
import { magnify } from 'utils/formatters'
interface Props {
buyAsset: Asset
sellAsset: Asset
}
export const TVChartContainer = (props: Props) => {
const chartContainerRef = useRef<HTMLDivElement>() as React.MutableRefObject<HTMLInputElement>
const widgetRef = useRef<IChartingLibraryWidget>()
const defaultSymbol = useRef<string>(
`${props.sellAsset.denom}${PAIR_SEPARATOR}${props.buyAsset.denom}`,
)
const baseCurrency = useStore((s) => s.baseCurrency)
const dataFeed = useMemo(
() => new DataFeed(false, baseCurrency.decimals, baseCurrency.denom),
[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(() => {
const widgetOptions: ChartingLibraryWidgetOptions = {
symbol: defaultSymbol.current,
datafeed: dataFeed,
interval: '1h' as ResolutionString,
library_path: '/charting_library/',
locale: 'en',
time_scale: {
min_bar_spacing: 12,
},
toolbar_bg: '#220E1D',
disabled_features: disabledFeatures,
enabled_features: enabledFeatures,
charts_storage_api_version: '1.1',
client_id: 'sample-implementation',
timezone: 'Etc/UTC' as Timezone,
user_id: 'not-set',
fullscreen: false,
autosize: true,
container: chartContainerRef.current,
custom_css_url: '/tradingview.css',
settings_overrides: {
'paneProperties.background': '#220E1D',
'paneProperties.backgroundType': 'solid',
'paneProperties.vertGridProperties.color': '#220E1D',
'paneProperties.horzGridProperties.color': '#220E1D',
'mainSeriesProperties.candleStyle.upColor': '#3DAE9A',
'mainSeriesProperties.candleStyle.downColor': '#AE3D3D',
'mainSeriesProperties.candleStyle.borderColor': '#232834',
'mainSeriesProperties.candleStyle.borderUpColor': '#3DAE9A',
'mainSeriesProperties.candleStyle.borderDownColor': '#AE3D3D',
'mainSeriesProperties.candleStyle.wickUpColor': '#3DAE9A',
'mainSeriesProperties.candleStyle.wickDownColor': '#AE3D3D',
'mainSeriesProperties.candleStyle.barColorsOnPrevClose': false,
'scalesProperties.textColor': 'rgba(255, 255, 255, 0.7)',
'paneProperties.legendProperties.showSeriesTitle': true,
'paneProperties.legendProperties.showVolume': false,
'paneProperties.legendProperties.showStudyValues': false,
'paneProperties.legendProperties.showStudyTitles': false,
'scalesProperties.axisHighlightColor': '#381730',
'linetooltrendline.color': '#3DAE9A',
'linetooltrendline.linewidth': 10,
},
overrides,
loading_screen: {
backgroundColor: '#220E1D',
foregroundColor: 'rgba(255, 255, 255, 0.3)',
},
theme: 'dark',
}
const tvWidget = new widget(widgetOptions)
tvWidget.onChartReady(() => {
widgetRef.current = tvWidget
})
return () => {
tvWidget.remove()
}
}, [dataFeed, defaultSymbol])
useEffect(() => {
if (widgetRef?.current) {
widgetRef.current.setSymbol(
`${props.sellAsset.denom}${PAIR_SEPARATOR}${props.buyAsset.denom}`,
widgetRef.current.chart().resolution() || ('1h' as ResolutionString),
() => {},
)
}
}, [props.buyAsset.denom, props.sellAsset.denom])
return (
<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 bg-chart'
className='h-[70dvh] max-h-[980px] min-h-[560px]'
>
<div ref={chartContainerRef} className='h-[calc(100%-32px)] overflow-hidden' />
<PoweredByPyth />
</Card>
)
}
-59
View File
@@ -1,59 +0,0 @@
import dynamic from 'next/dynamic'
import Script from 'next/script'
import { useState } from 'react'
import Card from 'components/Card'
import { CircularProgress } from 'components/CircularProgress'
import Loading from 'components/Loading'
import Text from 'components/Text'
import PoweredByPyth from 'components/Trade/TradeChart/PoweredByPyth'
const TVChartContainer = dynamic(
() => import('components/Trade/TradeChart/TVChartContainer').then((mod) => mod.TVChartContainer),
{ ssr: false },
)
interface Props {
buyAsset: Asset
sellAsset: Asset
}
export default function TradeChart(props: Props) {
const [isScriptReady, setIsScriptReady] = useState(false)
return (
<>
<Script
src='/datafeeds/udf/dist/bundle.js'
strategy='lazyOnload'
onReady={() => {
setIsScriptReady(true)
}}
onLoad={() => {
setIsScriptReady(true)
}}
/>
{isScriptReady ? (
<TVChartContainer buyAsset={props.buyAsset} sellAsset={props.sellAsset} />
) : (
<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='h-[70dvh] max-h-[980px] min-h-[560px]'
>
<div className='flex items-center justify-center w-full h-[calc(100%-32px)] rounded-b-base bg-chart'>
<CircularProgress size={60} className='opacity-50' />
</div>
<PoweredByPyth />
</Card>
)}
</>
)
}
@@ -1,161 +0,0 @@
import { useCallback, useMemo, useState } from 'react'
import Button from 'components/Button'
import EscButton from 'components/Button/EscButton'
import Divider from 'components/Divider'
import Overlay from 'components/Overlay'
import SearchBar from 'components/SearchBar'
import Text from 'components/Text'
import AssetList from 'components/Trade/TradeModule/AssetSelector/AssetList'
import PairsList from 'components/Trade/TradeModule/AssetSelector/PairsList'
import useFilteredAssets from 'hooks/useFilteredAssets'
import { getAllAssets } from 'utils/assets'
interface Props {
state: OverlayState
buyAsset: Asset
sellAsset: Asset
onChangeBuyAsset?: (asset: Asset) => void
onChangeSellAsset?: (asset: Asset) => void
onChangeTradingPair?: (tradingPair: TradingPair) => void
onChangeState: (state: OverlayState) => void
}
interface StablesFilterProps {
stables: Asset[]
selectedStables: Asset[]
onFilter: (stables: Asset[]) => void
}
function StablesFilter(props: StablesFilterProps) {
const { stables, selectedStables, onFilter } = props
const isAllSelected = selectedStables.length > 1
return (
<>
<Divider />
<div className='flex items-center w-full py-2 justify-evenly'>
<Button
onClick={() => onFilter(stables)}
text='All'
color={isAllSelected ? 'secondary' : 'quaternary'}
variant='transparent'
className={isAllSelected ? '!text-white !bg-white/10 border-white' : ''}
/>
{stables.map((stable) => {
const isCurrent = !isAllSelected && selectedStables[0].denom === stable.denom
return (
<Button
key={stable.symbol}
onClick={() => onFilter([stable])}
text={stable.symbol}
color={isCurrent ? 'secondary' : 'quaternary'}
variant='transparent'
className={isCurrent ? '!text-white !bg-white/10 border-white' : ''}
/>
)
})}
</div>
</>
)
}
export default function AssetOverlay(props: Props) {
const isPairSelector = !!props.onChangeTradingPair
const { assets, searchString, onChangeSearch } = useFilteredAssets()
const allAssets = getAllAssets()
const stableAssets = useMemo(() => allAssets.filter((asset) => asset.isStable), [allAssets])
const handleClose = useCallback(() => props.onChangeState('closed'), [props])
const handleToggle = useCallback(() => props.onChangeState(props.state), [props])
const [selectedStables, setSelectedStables] = useState<Asset[]>([stableAssets[0]])
const buyAssets = useMemo(
() =>
isPairSelector ? assets : assets.filter((asset) => asset.denom !== props.sellAsset.denom),
[assets, props.sellAsset, isPairSelector],
)
const sellAssets = useMemo(
() => assets.filter((asset) => asset.denom !== props.buyAsset.denom),
[assets, props.buyAsset],
)
function onChangeBuyAsset(asset: AssetPair | Asset) {
const selectedAsset = asset as Asset
if (!props.onChangeBuyAsset) return
props.onChangeBuyAsset(selectedAsset)
props.onChangeState('sell')
onChangeSearch('')
}
function onChangeSellAsset(asset: AssetPair | Asset) {
const selectedAsset = asset as Asset
if (!props.onChangeSellAsset) return
props.onChangeSellAsset(selectedAsset)
onChangeSearch('')
}
function onChangeAssetPair(assetPair: AssetPair | Asset) {
const selectedPair = assetPair as AssetPair
if (!props.onChangeTradingPair) return
props.onChangeTradingPair({ buy: selectedPair.buy.denom, sell: selectedPair.sell.denom })
props.onChangeState('closed')
onChangeSearch('')
}
return (
<Overlay
className='inset-0 w-full overflow-y-scroll scrollbar-hide'
show={props.state !== 'closed'}
setShow={handleClose}
>
<div className='flex justify-between p-4 overflow-hidden'>
<Text>{isPairSelector ? 'Select a market' : 'Select asset'}</Text>
<EscButton onClick={handleClose} enableKeyPress />
</div>
{isPairSelector && (
<StablesFilter
stables={stableAssets}
selectedStables={selectedStables}
onFilter={setSelectedStables}
/>
)}
<Divider />
<div className='p-4'>
<SearchBar
key={props.state}
value={searchString}
onChange={onChangeSearch}
placeholder='Search for e.g. "ETH" or "Ethereum"'
autoFocus
/>
</div>
<Divider />
{isPairSelector ? (
<PairsList
assets={buyAssets}
stables={selectedStables}
isOpen={props.state === 'pair'}
toggleOpen={handleToggle}
onChangeAssetPair={onChangeAssetPair}
/>
) : (
<>
<AssetList
type='buy'
assets={buyAssets}
isOpen={props.state === 'buy'}
toggleOpen={handleToggle}
onChangeAsset={onChangeBuyAsset}
/>
<AssetList
type='sell'
assets={sellAssets}
isOpen={props.state === 'sell'}
toggleOpen={handleToggle}
onChangeAsset={onChangeSellAsset}
/>
</>
)}
</Overlay>
)
}
+10 -16
View File
@@ -1,24 +1,18 @@
import classNames from 'classnames'
import moment from 'moment'
import Card from 'components/Card'
import Divider from 'components/Divider'
import Text from 'components/Text'
import { TextLink } from 'components/TextLink'
import { generateToastContent } from 'components/Toaster'
import { EXPLORER_TX_URL } from 'constants/explorer'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLocalStorage from 'hooks/useLocalStorage'
import Card from 'components/common/Card'
import Divider from 'components/common/Divider'
import Text from 'components/common/Text'
import { TextLink } from 'components/common/TextLink'
import { generateToastContent } from 'components/common/Toaster'
import useTransactions from 'hooks/localStorage/useTransactions'
import useStore from 'store'
export default function RecentTransactions() {
const address = useStore((s) => s.address)
const [transactions, setTransactions] = useLocalStorage<ToastStore>(
LocalStorageKeys.TRANSACTIONS,
{
recent: [],
},
)
const [transactions, setTransactions] = useTransactions()
const chainConfig = useStore((s) => s.chainConfig)
const recentTransactions = transactions.recent
.filter((tx) => tx.address === address)
.sort((a, b) => (a.timestamp > b.timestamp ? -1 : 1))
@@ -47,7 +41,7 @@ export default function RecentTransactions() {
)}
contentClassName='p-4'
onClick={() => {
if (hash) window.open(`${EXPLORER_TX_URL}${hash}`, '_blank')
if (hash) window.open(`${chainConfig.endpoints.explorer}${hash}`, '_blank')
}}
key={hash}
>
@@ -62,7 +56,7 @@ export default function RecentTransactions() {
{message}
</Text>
)}
{content?.length > 0 && generateToastContent(content)}
{content?.length > 0 && generateToastContent(content, chainConfig.assets)}
</Card>
)
})}
+10 -31
View File
@@ -2,28 +2,22 @@ import { useShuttle } from '@delphi-labs/shuttle-react'
import Image from 'next/image'
import { useCallback, useEffect, useMemo } from 'react'
import Button from 'components/Button'
import FullOverlayContent from 'components/FullOverlayContent'
import { ChevronRight } from 'components/Icons'
import Text from 'components/Text'
import Button from 'components/common/Button'
import FullOverlayContent from 'components/common/FullOverlayContent'
import { ChevronRight } from 'components/common/Icons'
import Text from 'components/common/Text'
import WalletFetchBalancesAndAccounts from 'components/Wallet/WalletFetchBalancesAndAccounts'
import WalletSelect from 'components/Wallet/WalletSelect'
import { BRIDGES } from 'constants/bridges'
import { CHAINS } from 'constants/chains'
import { ENV } from 'constants/env'
import useBaseAsset from 'hooks/assets/useBasetAsset'
import useCurrentWallet from 'hooks/useCurrentWallet'
import useToggle from 'hooks/useToggle'
import useWalletBalances from 'hooks/useWalletBalances'
import useStore from 'store'
import { NETWORK } from 'types/enums/network'
import { byDenom } from 'utils/array'
import { getBaseAsset } from 'utils/assets'
import { defaultFee } from 'utils/constants'
import { BN } from 'utils/helpers'
const currentChainId = ENV.CHAIN_ID
const currentChain = CHAINS[currentChainId]
function Bridge({ name, url, image }: Bridge) {
return (
<Button
@@ -41,11 +35,13 @@ function Bridge({ name, url, image }: Bridge) {
}
export default function WalletBridges() {
const chainConfig = useStore((s) => s.chainConfig)
const address = useStore((s) => s.address)
const currentWallet = useCurrentWallet()
const { disconnectWallet } = useShuttle()
const { data: walletBalances, isLoading } = useWalletBalances(address)
const baseAsset = getBaseAsset()
const baseAsset = useBaseAsset()
const [hasFunds, setHasFunds] = useToggle(false)
const baseBalance = useMemo(
@@ -71,9 +67,9 @@ export default function WalletBridges() {
return (
<FullOverlayContent
title={`${currentChain.defaultCurrency?.coinDenom ?? 'Gas token'} required!`}
title={`${chainConfig.defaultCurrency?.coinDenom ?? 'Gas token'} required!`}
copy={`To get started, you'll need at least a small amount of ${
currentChain.defaultCurrency?.coinDenom ?? 'the current chains gas token'
chainConfig.defaultCurrency?.coinDenom ?? 'the current chains gas token'
} to cover transaction fees on Mars. Fund your wallet or bridge some in from another chain.`}
button={{
className: 'w-full mt-4',
@@ -89,23 +85,6 @@ export default function WalletBridges() {
<Bridge key={bridge.name} {...bridge} />
))}
</div>
{ENV.NETWORK !== NETWORK.MAINNET && (
<div className='flex flex-wrap w-full gap-3'>
<Text size='lg' className='mt-4 text-white'>
Need Testnet Funds?
</Text>
<Bridge
key='osmosis-faucet'
name='Osmosis Testnet Faucet'
url={
ENV.NETWORK === NETWORK.TESTNET
? 'https://faucet.osmotest5.osmosis.zone/'
: 'https://faucet.devnet.osmosis.zone/'
}
image='/images/tokens/osmo.svg'
/>
</div>
)}
</FullOverlayContent>
)
}
@@ -1,11 +1,11 @@
import { ReactNode, useCallback } from 'react'
import Button from 'components/Button'
import { Wallet } from 'components/Icons'
import TermsOfService from 'components/TermsOfService'
import Button from 'components/common/Button'
import { Wallet } from 'components/common/Icons'
import TermsOfService from 'components/common/TermsOfService'
import WalletSelect from 'components/Wallet/WalletSelect'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLocalStorage from 'hooks/useLocalStorage'
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useStore from 'store'
interface Props {
+41 -10
View File
@@ -8,29 +8,59 @@ import {
LeapMetamaskCosmosSnapExtensionProvider,
ShuttleProvider,
StationExtensionProvider,
VectisCosmosExtensionProvider,
WalletExtensionProvider,
WalletMobileProvider,
XDEFICosmosExtensionProvider,
} from '@delphi-labs/shuttle-react'
import { FC } from 'react'
import { CHAINS } from 'constants/chains'
import { ENV } from 'constants/env'
import chains from 'configs/chains'
import { WALLETS } from 'constants/wallets'
import { WalletID } from 'types/enums/wallet'
import { ChainInfoID, WalletID } from 'types/enums/wallet'
type Props = {
children?: React.ReactNode
}
function mapChainConfigToChainInfo(chainConfig: ChainConfig): ChainInfo {
const chainInfo: ChainInfo = {
rpc: chainConfig.endpoints.rpc,
rest: chainConfig.endpoints.rest,
explorer: chainConfig.endpoints.explorer,
explorerName: chainConfig.explorerName,
chainId: chainConfig.id,
name: chainConfig.name,
gasPrice: chainConfig.gasPrice,
bech32Config: chainConfig.bech32Config,
defaultCurrency: chainConfig.defaultCurrency,
features: ['ibc-transfer', 'ibc-go'],
}
switch (chainConfig.id) {
case ChainInfoID.Osmosis1:
chainInfo.rpc = process.env.NEXT_PUBLIC_OSMOSIS_RPC ?? chainInfo.rpc
chainInfo.rest = process.env.NEXT_PUBLIC_OSMOSIS_REST ?? chainInfo.rpc
break
case ChainInfoID.OsmosisDevnet:
chainInfo.rpc = process.env.NEXT_PUBLIC_OSMOSIS_TEST_RPC ?? chainInfo.rpc
chainInfo.rest = process.env.NEXT_PUBLIC_OSMOSIS_TEST_REST ?? chainInfo.rpc
break
case ChainInfoID.Pion1:
chainInfo.rpc = process.env.NEXT_PUBLIC_NEUTRON_TEST_RPC ?? chainInfo.rpc
chainInfo.rest = process.env.NEXT_PUBLIC_NEUTRON_TEST_REST ?? chainInfo.rpc
break
}
return chainInfo
}
function getSupportedChainsInfos(walletId: WalletID) {
return WALLETS[walletId].supportedChains.map((chain) => {
const chainInfo: ChainInfo = CHAINS[chain]
// Check if supported chain equals current chain
if (ENV.CHAIN_ID !== chainInfo.chainId) return chainInfo
// Override rpc and rest urls for the current chain if they are defined in .env
if (ENV.URL_RPC) chainInfo.rpc = ENV.URL_RPC
if (ENV.URL_REST) chainInfo.rest = ENV.URL_REST
const chainInfo = mapChainConfigToChainInfo(chains[chain])
return chainInfo
})
}
@@ -56,12 +86,13 @@ const extensionProviders: WalletExtensionProvider[] = [
new CosmostationExtensionProvider({ networks: getSupportedChainsInfos(WalletID.Cosmostation) }),
new XDEFICosmosExtensionProvider({ networks: getSupportedChainsInfos(WalletID.Xdefi) }),
new StationExtensionProvider({ networks: getSupportedChainsInfos(WalletID.Station) }),
new VectisCosmosExtensionProvider({ networks: getSupportedChainsInfos(WalletID.Vectis) }),
]
export const WalletConnectProvider: FC<Props> = ({ children }) => {
return (
<ShuttleProvider
walletConnectProjectId={ENV.WALLET_CONNECT_ID}
walletConnectProjectId={process.env.NEXT_PUBLIC_WALLET_CONNECT_ID}
mobileProviders={mobileProviders}
extensionProviders={extensionProviders}
persistent
+20 -19
View File
@@ -6,17 +6,18 @@ import { useCallback, useEffect, useState } from 'react'
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
import useClipboard from 'react-use-clipboard'
import Button from 'components/Button'
import { CircularProgress } from 'components/CircularProgress'
import { FormattedNumber } from 'components/FormattedNumber'
import { Check, Copy, ExternalLink, Osmo } from 'components/Icons'
import Overlay from 'components/Overlay'
import Text from 'components/Text'
import Button from 'components/common/Button'
import { CircularProgress } from 'components/common/CircularProgress'
import { FormattedNumber } from 'components/common/FormattedNumber'
import { Check, Copy, ExternalLink, Wallet } from 'components/common/Icons'
import Overlay from 'components/common/Overlay'
import Text from 'components/common/Text'
import RecentTransactions from 'components/Wallet/RecentTransactions'
import WalletSelect from 'components/Wallet/WalletSelect'
import { CHAINS } from 'constants/chains'
import { ENV } from 'constants/env'
import chains from 'configs/chains'
import { BN_ZERO } from 'constants/math'
import useBaseAsset from 'hooks/assets/useBasetAsset'
import useMarketEnabledAssets from 'hooks/assets/useMarketEnabledAssets'
import useCurrentWallet from 'hooks/useCurrentWallet'
import useICNSDomain from 'hooks/useICNSDomain'
import useToggle from 'hooks/useToggle'
@@ -24,7 +25,6 @@ import useWalletBalances from 'hooks/useWalletBalances'
import useStore from 'store'
import { NETWORK } from 'types/enums/network'
import { ChainInfoID } from 'types/enums/wallet'
import { getBaseAsset, getEnabledMarketAssets } from 'utils/assets'
import { truncate } from 'utils/formatters'
import { getPage, getRoute } from 'utils/route'
@@ -32,14 +32,16 @@ export default function WalletConnectedButton() {
// ---------------
// EXTERNAL HOOKS
// ---------------
const marketAssets = getEnabledMarketAssets()
const marketAssets = useMarketEnabledAssets()
const currentWallet = useCurrentWallet()
const { disconnectWallet } = useShuttle()
const address = useStore((s) => s.address)
const address = currentWallet?.account.address
const userDomain = useStore((s) => s.userDomain)
const focusComponent = useStore((s) => s.focusComponent)
const network = useStore((s) => s.client?.connectedWallet.network)
const baseAsset = getBaseAsset()
const chainConfig = useStore((s) => s.chainConfig)
const baseAsset = useBaseAsset()
const { data: walletBalances, isLoading } = useWalletBalances(address)
const { data: icnsData, isLoading: isLoadingICNS } = useICNSDomain(address)
const navigate = useNavigate()
@@ -57,10 +59,10 @@ export default function WalletConnectedButton() {
// ---------------
// VARIABLES
// ---------------
const explorerName = network && CHAINS[network.chainId as ChainInfoID].explorerName
const explorerName = network && chains[network.chainId as ChainInfoID].explorerName
const viewOnFinder = useCallback(() => {
const explorerUrl = network && CHAINS[network.chainId as ChainInfoID].explorer
const explorerUrl = network && chains[network.chainId as ChainInfoID].endpoints.explorer
window.open(`${explorerUrl}/account/${address}`, '_blank')
}, [network, address])
@@ -72,7 +74,6 @@ export default function WalletConnectedButton() {
client: undefined,
address: undefined,
userDomain: undefined,
accounts: null,
balances: [],
focusComponent: null,
})
@@ -128,21 +129,21 @@ export default function WalletConnectedButton() {
}, [walletBalances, baseAsset.denom, baseAsset.decimals, marketAssets, walletAmount])
return (
<div className={'relative'}>
{ENV.NETWORK !== NETWORK.MAINNET && (
<div className='relative'>
{chainConfig.network !== NETWORK.MAINNET && (
<Text
className='absolute -right-2 -top-2.5 z-10 rounded-sm p-0.5 px-2 gradient-primary-to-secondary'
size='3xs'
uppercase
>
{network?.chainId}
{chainConfig.id}
</Text>
)}
<Button
variant='solid'
leftIcon={<Wallet />}
color='tertiary'
leftIcon={<Osmo />}
onClick={() => {
setShowDetails(!showDetails)
}}
+105 -55
View File
@@ -1,32 +1,24 @@
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import {
useShuttle,
WalletExtensionProvider,
WalletMobileProvider,
} from '@delphi-labs/shuttle-react'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useShuttle } from '@delphi-labs/shuttle-react'
import { useCallback, useEffect, useMemo } from 'react'
import { CircularProgress } from 'components/CircularProgress'
import FullOverlayContent from 'components/FullOverlayContent'
import { CircularProgress } from 'components/common/CircularProgress'
import FullOverlayContent from 'components/common/FullOverlayContent'
import WalletSelect from 'components/Wallet//WalletSelect'
import WalletFetchBalancesAndAccounts from 'components/Wallet/WalletFetchBalancesAndAccounts'
import { CHAINS } from 'constants/chains'
import { ENV } from 'constants/env'
import useChainConfig from 'hooks/useChainConfig'
import useCurrentWallet from 'hooks/useCurrentWallet'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
interface Props {
autoConnect?: boolean
providerId?: string
}
const currentChainId = ENV.CHAIN_ID
const currentChain = CHAINS[currentChainId]
const mapErrorMessages = (providerId: string, errorMessage: string) => {
const mapErrorMessages = (providerId: string, errorMessage: string, name: string) => {
if (providerId === 'station') {
if (errorMessage.match('Wallet not connected to the network with chainId')) {
return `Your wallet is not connected to the correct network. Please switch your wallet to the ${currentChain.name} network`
return `Your wallet is not connected to the correct network. Please switch your wallet to the ${name} network`
}
}
@@ -34,32 +26,26 @@ const mapErrorMessages = (providerId: string, errorMessage: string) => {
}
export default function WalletConnecting(props: Props) {
const { recentWallet, connect, simulate, sign, broadcast, mobileProviders, extensionProviders } =
const { connect, mobileConnect, simulate, sign, broadcast, mobileProviders, extensionProviders } =
useShuttle()
const providers = useMemo(
() => [...mobileProviders, ...extensionProviders],
[mobileProviders, extensionProviders],
)
const chainConfig = useChainConfig()
const [isConnecting, setIsConnecting] = useToggle()
const recentWallet = useCurrentWallet()
const providerId = props.providerId ?? recentWallet?.providerId
const refTimer = useRef<number | null>(null)
const isAutoConnect = props.autoConnect
const client = useStore((s) => s.client)
const address = useStore((s) => s.address)
const clearTimer = useCallback(() => {
if (refTimer.current !== null) window.clearTimeout(refTimer.current)
}, [refTimer])
const handleConnect = useCallback(
(extensionProviderId: string) => {
async function handleConnectAsync() {
if (client || isConnecting) return
setIsConnecting(true)
clearTimer()
try {
const response = await connect({ extensionProviderId, chainId: currentChainId })
const cosmClient = await CosmWasmClient.connect(response.network.rpc)
const response = await connect({ extensionProviderId, chainId: chainConfig.id })
const cosmClient = await CosmWasmClient.connect(chainConfig.endpoints.rpc)
const walletClient: WalletClient = {
broadcast,
cosmWasmClient: cosmClient,
@@ -71,6 +57,7 @@ export default function WalletConnecting(props: Props) {
useStore.setState({
client: walletClient,
address: response.account.address,
chainConfig: chainConfig,
focusComponent: {
component: <WalletFetchBalancesAndAccounts />,
onClose: () => {
@@ -85,13 +72,16 @@ export default function WalletConnecting(props: Props) {
client: undefined,
address: undefined,
userDomain: undefined,
accounts: null,
focusComponent: {
component: (
<WalletSelect
error={{
title: 'Failed to connect to wallet',
message: mapErrorMessages(extensionProviderId, error.message),
message: mapErrorMessages(
extensionProviderId,
error.message,
chainConfig.name,
),
}}
/>
),
@@ -102,43 +92,103 @@ export default function WalletConnecting(props: Props) {
}
if (!isConnecting) handleConnectAsync()
},
[broadcast, connect, client, isConnecting, setIsConnecting, sign, simulate, clearTimer],
[isConnecting, client, setIsConnecting, connect, chainConfig, broadcast, sign, simulate],
)
const startTimer = useCallback(
(provider?: WalletMobileProvider | WalletExtensionProvider) => {
if (refTimer.current !== null || !window) return
refTimer.current = window.setTimeout(() => handleConnect(provider?.id ?? ''), 5000)
const handleMobileConnect = useCallback(
(mobileProviderId: string) => {
async function handleMobileConnectAsync() {
if (!recentWallet) {
useStore.setState({
client: undefined,
address: undefined,
userDomain: undefined,
focusComponent: {
component: <WalletSelect />,
},
})
return
}
if (client || isConnecting) return
setIsConnecting(true)
try {
await mobileConnect({ mobileProviderId, chainId: chainConfig.id })
const cosmClient = await CosmWasmClient.connect(chainConfig.endpoints.rpc)
const walletClient: WalletClient = {
broadcast,
cosmWasmClient: cosmClient,
connectedWallet: recentWallet,
sign,
simulate,
}
setIsConnecting(false)
useStore.setState({
client: walletClient,
address: recentWallet.account.address,
userDomain: undefined,
chainConfig: chainConfig,
})
} catch (error) {
setIsConnecting(false)
if (error instanceof Error) {
useStore.setState({
client: undefined,
address: undefined,
userDomain: undefined,
focusComponent: {
component: (
<WalletSelect
error={{
title: 'Failed to connect to wallet',
message: mapErrorMessages(mobileProviderId, error.message, chainConfig.name),
}}
/>
),
},
})
}
}
}
if (!isConnecting) handleMobileConnectAsync()
},
[refTimer, handleConnect],
[
isConnecting,
client,
recentWallet,
setIsConnecting,
mobileConnect,
chainConfig,
broadcast,
sign,
simulate,
],
)
useEffect(() => {
if (!providerId) {
useStore.setState({
client: undefined,
address: undefined,
userDomain: undefined,
focusComponent: {
component: <WalletSelect />,
},
})
return
}
const provider = providers.find((p) => p.id === providerId)
if (
!provider ||
!provider.initialized ||
isConnecting ||
(recentWallet && recentWallet.account.address === address)
) {
if (isAutoConnect) startTimer(provider)
if (!provider || provider.initializing || !provider.initialized || isConnecting) {
return
}
const isMobileProvider = provider.id.split('-')[0] === 'mobile'
if (isMobileProvider) {
handleMobileConnect(provider.id)
return
}
handleConnect(provider.id)
return () => clearTimer()
}, [
handleConnect,
isConnecting,
providerId,
providers,
recentWallet,
address,
isAutoConnect,
startTimer,
clearTimer,
])
}, [handleConnect, isConnecting, providerId, providers, handleMobileConnect])
return (
<FullOverlayContent
@@ -1,16 +1,16 @@
import { Suspense, useEffect, useMemo } from 'react'
import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom'
import AccountCreateFirst from 'components/Account/AccountCreateFirst'
import { CircularProgress } from 'components/CircularProgress'
import FullOverlayContent from 'components/FullOverlayContent'
import AccountCreateFirst from 'components/account/AccountCreateFirst'
import { CircularProgress } from 'components/common/CircularProgress'
import FullOverlayContent from 'components/common/FullOverlayContent'
import WalletBridges from 'components/Wallet/WalletBridges'
import useAccountIds from 'hooks/accounts/useAccountIds'
import useBaseAsset from 'hooks/assets/useBasetAsset'
import useAccountId from 'hooks/useAccountId'
import useAccountIds from 'hooks/useAccountIds'
import useWalletBalances from 'hooks/useWalletBalances'
import useStore from 'store'
import { byDenom } from 'utils/array'
import { getBaseAsset } from 'utils/assets'
import { defaultFee } from 'utils/constants'
import { BN } from 'utils/helpers'
import { getPage, getRoute } from 'utils/route'
@@ -19,7 +19,7 @@ function FetchLoading() {
return (
<FullOverlayContent
title='Fetching Wallet Data'
copy='Please wait, while your wallet balances and accounts are beeing analyzed'
copy='Please wait, while your wallet balances and accounts are being analyzed'
>
<CircularProgress size={40} />
</FullOverlayContent>
@@ -40,7 +40,7 @@ function Content() {
true,
)
const { data: walletBalances, isLoading: isLoadingBalances } = useWalletBalances(address)
const baseAsset = getBaseAsset()
const baseAsset = useBaseAsset()
const baseBalance = useMemo(
() => walletBalances.find(byDenom(baseAsset.denom))?.amount ?? '0',
+10 -14
View File
@@ -1,16 +1,14 @@
import { useShuttle } from '@delphi-labs/shuttle-react'
import moment from 'moment'
import Image from 'next/image'
import React, { useEffect, useState } from 'react'
import QRCode from 'react-qr-code'
import moment from 'moment'
import Button from 'components/Button'
import FullOverlayContent from 'components/FullOverlayContent'
import { ChevronLeft, ChevronRight } from 'components/Icons'
import Text from 'components/Text'
import Button from 'components/common/Button'
import FullOverlayContent from 'components/common/FullOverlayContent'
import { ChevronLeft, ChevronRight } from 'components/common/Icons'
import Text from 'components/common/Text'
import WalletConnecting from 'components/Wallet/WalletConnecting'
import { CHAINS } from 'constants/chains'
import { ENV } from 'constants/env'
import { WALLETS } from 'constants/wallets'
import useStore from 'store'
import { WalletID } from 'types/enums/wallet'
@@ -32,9 +30,6 @@ interface WalletOptionProps {
showLoader?: boolean
}
const currentChainId = ENV.CHAIN_ID
const currentChain = CHAINS[currentChainId]
function WalletOption(props: WalletOptionProps) {
return (
<Button
@@ -57,6 +52,7 @@ function WalletOption(props: WalletOptionProps) {
}
export default function WalletSelect(props: Props) {
const chainConfig = useStore((s) => s.chainConfig)
const { extensionProviders, mobileProviders, mobileConnect } = useShuttle()
const [qrCodeUrl, setQRCodeUrl] = useState('')
const [error, setError] = useState(props.error)
@@ -138,7 +134,7 @@ export default function WalletSelect(props: Props) {
return (
<FullOverlayContent
title={'Connect your wallet'}
copy={`Deposit assets from your ${currentChain.name} address to your Mars Credit Account.`}
copy={`Deposit assets from your ${chainConfig.name} address to your Mars Credit Account.`}
docs='wallet'
>
<div className='flex flex-wrap w-full gap-3'>
@@ -149,7 +145,7 @@ export default function WalletSelect(props: Props) {
return (
<React.Fragment key={walletId}>
{Array.from(provider.networks.values())
.filter((network) => network.chainId === currentChainId)
.filter((network) => network.chainId === chainConfig.id)
.map((network) => {
if (!provider.initialized && !provider.initializing) {
return (
@@ -169,7 +165,7 @@ export default function WalletSelect(props: Props) {
key={`${walletId}-${network.chainId}`}
handleClick={() => handleConnectClick(walletId)}
imageSrc={WALLETS[walletId].imageURL}
name={WALLETS[walletId].name ?? 'Conenct Wallet'}
name={WALLETS[walletId].name ?? 'Connect Wallet'}
/>
)
})}
@@ -183,7 +179,7 @@ export default function WalletSelect(props: Props) {
return (
<React.Fragment key={walletId}>
{Array.from(provider.networks.values())
.filter((network) => network.chainId === currentChainId)
.filter((network) => network.chainId === chainConfig.id)
.map((network) => {
return (
<WalletOption
+16 -16
View File
@@ -13,26 +13,26 @@ export default function Wallet() {
const address = useStore((s) => s.address)
useEffect(() => {
if (!currentWallet) return
if (currentWallet.account.address === address) return
if (address) return
useStore.setState({
address: undefined,
userDomain: undefined,
client: undefined,
focusComponent: {
component: <WalletConnecting autoConnect />,
onClose: () => {
disconnectWallet(currentWallet)
useStore.setState({
client: undefined,
address: undefined,
userDomain: undefined,
accounts: null,
balances: [],
focusComponent: null,
})
},
},
focusComponent: currentWallet
? {
component: <WalletConnecting />,
onClose: () => {
disconnectWallet(currentWallet)
useStore.setState({
client: undefined,
address: undefined,
userDomain: undefined,
balances: [],
focusComponent: null,
})
},
}
: null,
})
}, [currentWallet, address, disconnectWallet])
@@ -1,4 +1,4 @@
import AssetRate from 'components/Asset/AssetRate'
import AssetRate from 'components/common/assets/AssetRate'
import { byDenom } from 'utils/array'
export const APY_META = { accessorKey: 'apy', header: 'APY', meta: { className: 'w-30' } }
@@ -7,7 +7,7 @@ interface Props {
apy: number
markets: Market[]
denom: string
type: 'deposits' | 'borrowing' | 'lending' | 'vault'
type: PositionType
}
export default function Apr(props: Props) {
@@ -20,7 +20,7 @@ export default function Apr(props: Props) {
<AssetRate
className='justify-end text-xs'
rate={apy}
isEnabled={type !== 'lending' || isEnabled}
isEnabled={type !== 'lend' || isEnabled}
type='apy'
orientation='ltr'
/>
@@ -0,0 +1,19 @@
import Text from 'components/common/Text'
export const ASSET_META = { accessorKey: 'symbol', header: 'Asset', id: 'symbol' }
interface Props {
symbol: string
type: PositionType
}
export default function Asset(props: Props) {
const { symbol, type } = props
return (
<Text size='xs'>
{symbol}
{type === 'borrow' && <span className='ml-1 text-loss'>(debt)</span>}
{type === 'lend' && <span className='ml-1 text-profit'>(lent)</span>}
{type === 'vault' && <span className='ml-1 text-profit'>(farm)</span>}
</Text>
)
}
@@ -1,9 +1,9 @@
import { useEffect, useMemo, useState } from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import { InfoCircle } from 'components/Icons'
import Text from 'components/Text'
import { Tooltip } from 'components/Tooltip'
import DisplayCurrency from 'components/common/DisplayCurrency'
import { InfoCircle } from 'components/common/Icons'
import Text from 'components/common/Text'
import { Tooltip } from 'components/common/Tooltip'
import useLiquidationPrice from 'hooks/useLiquidationPrice'
import { BNCoin } from 'types/classes/BNCoin'
import { LiquidationPriceKind } from 'utils/health_computer'
@@ -20,7 +20,7 @@ interface Props {
amount: number
computeLiquidationPrice: (denom: string, kind: LiquidationPriceKind) => number | null
denom: string
type: 'deposits' | 'borrowing' | 'lending' | 'vault'
type: PositionType
account: Account
}
@@ -31,7 +31,7 @@ export default function LiqPrice(props: Props) {
const liqPrice = useMemo(() => {
if (type === 'vault' || amount === 0) return 0
return computeLiquidationPrice(denom, type === 'borrowing' ? 'debt' : 'asset')
return computeLiquidationPrice(denom, type === 'borrow' ? 'debt' : 'asset')
}, [amount, computeLiquidationPrice, denom, type])
const { liquidationPrice } = useLiquidationPrice(liqPrice)
@@ -1,4 +1,4 @@
import DisplayCurrency from 'components/DisplayCurrency'
import DisplayCurrency from 'components/common/DisplayCurrency'
import usePrice from 'hooks/usePrice'
import { BNCoin } from 'types/classes/BNCoin'
import { BN } from 'utils/helpers'
@@ -8,7 +8,7 @@ export const PRICE_META = { id: 'price', header: 'Price', meta: { className: 'w-
interface Props {
amount: number
denom: string
type: 'deposits' | 'borrowing' | 'lending' | 'vault'
type: PositionType
}
export default function Price(props: Props) {
@@ -1,8 +1,8 @@
import { Row } from '@tanstack/react-table'
import classNames from 'classnames'
import { getAmountChangeColor } from 'components/Account/AccountBalancesTable/functions'
import { FormattedNumber } from 'components/FormattedNumber'
import { getAmountChangeColor } from 'components/account/AccountBalancesTable/functions'
import { FormattedNumber } from 'components/common/FormattedNumber'
import { MAX_AMOUNT_DECIMALS, MIN_AMOUNT } from 'constants/math'
import { formatAmountToPrecision } from 'utils/formatters'
@@ -12,7 +12,7 @@ interface Props {
size: number
amountChange: BigNumber
denom: string
type: 'deposits' | 'borrowing' | 'lending' | 'vault'
type: PositionType
}
export const sizeSortingFn = (a: Row<AccountBalanceRow>, b: Row<AccountBalanceRow>): number => {
@@ -1,8 +1,8 @@
import { Row } from '@tanstack/react-table'
import classNames from 'classnames'
import { getAmountChangeColor } from 'components/Account/AccountBalancesTable/functions'
import DisplayCurrency from 'components/DisplayCurrency'
import { getAmountChangeColor } from 'components/account/AccountBalancesTable/functions'
import DisplayCurrency from 'components/common/DisplayCurrency'
import { ORACLE_DENOM } from 'constants/oracle'
import { BNCoin } from 'types/classes/BNCoin'
import { BN } from 'utils/helpers'
@@ -12,10 +12,19 @@ export const VALUE_META = { accessorKey: 'value', header: 'Value' }
interface Props {
amountChange: BigNumber
value: string
type: 'deposits' | 'borrowing' | 'lending' | 'vault'
type: PositionType
}
export const valueSortingFn = (a: Row<AccountBalanceRow>, b: Row<AccountBalanceRow>): number => {
export const valueBalancesSortingFn = (
a: Row<AccountBalanceRow>,
b: Row<AccountBalanceRow>,
): number => {
const valueA = BN(a.original.value)
const valueB = BN(b.original.value)
return valueA.minus(valueB).toNumber()
}
export const valuePerpSortingFn = (a: Row<AccountPerpRow>, b: Row<AccountPerpRow>): number => {
const valueA = BN(a.original.value)
const valueB = BN(b.original.value)
return valueA.minus(valueB).toNumber()
@@ -24,17 +33,12 @@ export const valueSortingFn = (a: Row<AccountBalanceRow>, b: Row<AccountBalanceR
export default function Value(props: Props) {
const { amountChange, type, value } = props
const color = getAmountChangeColor(type, amountChange)
const allowZero = !amountChange.isZero()
const coin = new BNCoin({
denom: ORACLE_DENOM,
amount: value,
})
return (
<DisplayCurrency
coin={coin}
className={classNames('text-xs text-right', color)}
showZero={!allowZero}
/>
<DisplayCurrency coin={coin} className={classNames('text-xs text-right', color)} showZero />
)
}
@@ -1,20 +1,20 @@
import { ColumnDef, Row } from '@tanstack/react-table'
import { useMemo } from 'react'
import Apy, { APY_META } from 'components/Account/AccountBalancesTable/Columns/Apy'
import Asset, { ASSET_META } from 'components/Account/AccountBalancesTable/Columns/Asset'
import LiqPrice, { LIQ_META } from 'components/Account/AccountBalancesTable/Columns/LiqPrice'
import Price, { PRICE_META } from 'components/Account/AccountBalancesTable/Columns/Price'
import Apy, { APY_META } from 'components/account/AccountBalancesTable/Columns/Apy'
import Asset, { ASSET_META } from 'components/account/AccountBalancesTable/Columns/Asset'
import LiqPrice, { LIQ_META } from 'components/account/AccountBalancesTable/Columns/LiqPrice'
import Price, { PRICE_META } from 'components/account/AccountBalancesTable/Columns/Price'
import Size, {
SIZE_META,
sizeSortingFn,
} from 'components/Account/AccountBalancesTable/Columns/Size'
} from 'components/account/AccountBalancesTable/Columns/Size'
import Value, {
VALUE_META,
valueSortingFn,
} from 'components/Account/AccountBalancesTable/Columns/Value'
valueBalancesSortingFn,
} from 'components/account/AccountBalancesTable/Columns/Value'
import useMarketAssets from 'hooks/markets/useMarketAssets'
import useHealthComputer from 'hooks/useHealthComputer'
import useMarketAssets from 'hooks/useMarketAssets'
import useStore from 'store'
export default function useAccountBalancesColumns(
@@ -41,8 +41,7 @@ export default function useAccountBalancesColumns(
type={row.original.type}
/>
),
sortingFn: valueSortingFn,
sortingFn: valueBalancesSortingFn,
},
{
...SIZE_META,
@@ -3,9 +3,10 @@ import { BNCoin } from 'types/classes/BNCoin'
import { demagnify, getCoinValue } from 'utils/formatters'
export function getAssetAccountBalanceRow(
type: 'deposits' | 'borrowing' | 'lending',
type: 'deposit' | 'borrow' | 'lend',
asset: Asset,
prices: BNCoin[],
assets: Asset[],
position: BNCoin,
apy: number,
prev?: BNCoin,
@@ -17,7 +18,7 @@ export function getAssetAccountBalanceRow(
type,
symbol: asset.symbol,
size: demagnify(amount, asset),
value: getCoinValue(BNCoin.fromDenomAndBigNumber(denom, amount), prices).toString(),
value: getCoinValue(BNCoin.fromDenomAndBigNumber(denom, amount), prices, assets).toString(),
denom,
amount,
apy,
@@ -56,11 +57,9 @@ export function getVaultAccountBalanceRow(
}
}
export function getAmountChangeColor(
type: 'deposits' | 'borrowing' | 'lending' | 'vault',
amount: BigNumber,
) {
if (type === 'borrowing') {
export function getAmountChangeColor(type: PositionType, amount: BigNumber) {
if (type === 'perp') return ''
if (type === 'borrow') {
if (amount.isGreaterThan(0)) return 'text-loss'
if (amount.isLessThan(0)) return 'text-profit'
}
@@ -1,14 +1,14 @@
import classNames from 'classnames'
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
import useAccountBalancesColumns from 'components/Account/AccountBalancesTable/Columns/useAccountBalancesColumns'
import useAccountBalanceData from 'components/Account/AccountBalancesTable/useAccountBalanceData'
import AccountFundFullPage from 'components/Account/AccountFund/AccountFundFullPage'
import ActionButton from 'components/Button/ActionButton'
import Card from 'components/Card'
import Table from 'components/Table'
import useAccountBalancesColumns from 'components/account/AccountBalancesTable/Columns/useAccountBalancesColumns'
import useAccountBalanceData from 'components/account/AccountBalancesTable/useAccountBalanceData'
import AccountFundFullPage from 'components/account/AccountFund/AccountFundFullPage'
import ActionButton from 'components/common/Button/ActionButton'
import Card from 'components/common/Card'
import Table from 'components/common/Table'
import ConditionalWrapper from 'hocs/ConditionalWrapper'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useStore from 'store'
import { getPage, getRoute } from 'utils/route'
@@ -89,7 +89,7 @@ export default function AccountBalancesTable(props: Props) {
initialSorting={[]}
spacingClassName='p-2'
hideCard={hideCard}
isBalancesTable
type='balances'
/>
)
}
@@ -3,8 +3,8 @@ import { useMemo } from 'react'
import {
getAssetAccountBalanceRow,
getVaultAccountBalanceRow,
} from 'components/Account/AccountBalancesTable/functions'
import { ASSETS } from 'constants/assets'
} from 'components/account/AccountBalancesTable/functions'
import useAllAssets from 'hooks/assets/useAllAssets'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import usePrices from 'hooks/usePrices'
import { byDenom } from 'utils/array'
@@ -19,10 +19,9 @@ interface Props {
export default function useAccountBalanceData(props: Props) {
const { account, updatedAccount, lendingData, borrowingData } = props
const { data: hlsStrategies } = useHLSStakingAssets()
const { data: prices } = usePrices()
const assets = useAllAssets()
return useMemo<AccountBalanceRow[]>(() => {
const usedAccount = updatedAccount ?? account
const accountDeposits = usedAccount?.deposits ?? []
@@ -32,24 +31,26 @@ export default function useAccountBalanceData(props: Props) {
const deposits: AccountBalanceRow[] = []
accountDeposits.forEach((deposit) => {
const asset = ASSETS.find(byDenom(deposit.denom))
const asset = assets.find(byDenom(deposit.denom))
if (!asset) return
const apy = props.isHls
? hlsStrategies.find((strategy) => strategy.denoms.deposit === asset.denom)?.apy ?? 0
: 0
const prevDeposit = updatedAccount ? account?.deposits.find(byDenom(deposit.denom)) : deposit
deposits.push(getAssetAccountBalanceRow('deposits', asset, prices, deposit, apy, prevDeposit))
deposits.push(
getAssetAccountBalanceRow('deposit', asset, prices, assets, deposit, apy, prevDeposit),
)
})
const lends = accountLends.map((lending) => {
const asset = ASSETS.find(byDenom(lending.denom)) ?? ASSETS[0]
const asset = assets.find(byDenom(lending.denom)) ?? assets[0]
const apy =
lendingData.find((market) => market.asset.denom === lending.denom)?.apy.deposit ?? 0
const prevLending = updatedAccount
? account?.lends.find((position) => position.denom === lending.denom)
: lending
return getAssetAccountBalanceRow('lending', asset, prices, lending, apy, prevLending)
return getAssetAccountBalanceRow('lend', asset, prices, assets, lending, apy, prevLending)
})
const vaults = accountVaults.map((vault) => {
@@ -61,13 +62,22 @@ export default function useAccountBalanceData(props: Props) {
})
const debts = accountDebts.map((debt) => {
const asset = ASSETS.find(byDenom(debt.denom)) ?? ASSETS[0]
const asset = assets.find(byDenom(debt.denom)) ?? assets[0]
const apy = borrowingData.find((market) => market.asset.denom === debt.denom)?.apy.borrow ?? 0
const prevDebt = updatedAccount
? account?.debts.find((position) => position.denom === debt.denom)
: debt
return getAssetAccountBalanceRow('borrowing', asset, prices, debt, apy, prevDebt)
return getAssetAccountBalanceRow('borrow', asset, prices, assets, debt, apy, prevDebt)
})
return [...deposits, ...lends, ...vaults, ...debts]
}, [updatedAccount, account, props.isHls, hlsStrategies, prices, lendingData, borrowingData])
}, [
updatedAccount,
account,
props.isHls,
hlsStrategies,
prices,
assets,
lendingData,
borrowingData,
])
}
@@ -2,12 +2,13 @@ import BigNumber from 'bignumber.js'
import classNames from 'classnames'
import { useMemo } from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import { ArrowRight } from 'components/Icons'
import Text from 'components/Text'
import DisplayCurrency from 'components/common/DisplayCurrency'
import { FormattedNumber } from 'components/common/FormattedNumber'
import { ArrowRight } from 'components/common/Icons'
import Text from 'components/common/Text'
import { BN_ZERO, MAX_AMOUNT_DECIMALS } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle'
import useAllAssets from 'hooks/assets/useAllAssets'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
@@ -40,7 +41,8 @@ export default function AccountComposition(props: Props) {
const hasChanged = !!updatedAccount
const { data: prices } = usePrices()
const { data: hlsStrategies } = useHLSStakingAssets()
const { data } = useBorrowMarketAssetsTableData(false)
const assets = useAllAssets()
const data = useBorrowMarketAssetsTableData()
const borrowAssetsData = useMemo(() => data?.allAssets || [], [data])
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
useLendingMarketAssetsTableData()
@@ -50,15 +52,15 @@ export default function AccountComposition(props: Props) {
)
const [depositsBalance, lendsBalance, debtsBalance, vaultsBalance] = useMemo(
() => getAccountPositionValues(account, prices),
[account, prices],
() => getAccountPositionValues(account, prices, assets),
[account, assets, prices],
)
const totalBalance = depositsBalance.plus(lendsBalance).plus(vaultsBalance)
const [updatedPositionValue, updatedDebtsBalance] = useMemo(() => {
const [updatedDepositsBalance, updatedLendsBalance, updatedDebtsBalance, updatedVaultsBalance] =
updatedAccount
? getAccountPositionValues(updatedAccount, prices)
? getAccountPositionValues(updatedAccount, prices, assets)
: [BN_ZERO, BN_ZERO, BN_ZERO]
const updatedPositionValue = updatedDepositsBalance
@@ -66,12 +68,15 @@ export default function AccountComposition(props: Props) {
.plus(updatedVaultsBalance)
return [updatedPositionValue, updatedDebtsBalance]
}, [updatedAccount, prices])
}, [updatedAccount, prices, assets])
const netWorth = useMemo(() => calculateAccountBalanceValue(account, prices), [account, prices])
const netWorth = useMemo(
() => calculateAccountBalanceValue(account, prices, assets),
[account, assets, prices],
)
const updatedTotalBalance = useMemo(
() => (updatedAccount ? calculateAccountBalanceValue(updatedAccount, prices) : BN_ZERO),
[updatedAccount, prices],
() => (updatedAccount ? calculateAccountBalanceValue(updatedAccount, prices, assets) : BN_ZERO),
[updatedAccount, prices, assets],
)
const apr = useMemo(
@@ -82,9 +87,10 @@ export default function AccountComposition(props: Props) {
lendingAssetsData,
prices,
hlsStrategies,
assets,
props.isHls,
),
[account, borrowAssetsData, hlsStrategies, lendingAssetsData, prices, props.isHls],
[account, assets, borrowAssetsData, hlsStrategies, lendingAssetsData, prices, props.isHls],
)
const updatedApr = useMemo(
() =>
@@ -95,10 +101,19 @@ export default function AccountComposition(props: Props) {
lendingAssetsData,
prices,
hlsStrategies,
assets,
props.isHls,
)
: BN_ZERO,
[updatedAccount, borrowAssetsData, lendingAssetsData, prices, hlsStrategies, props.isHls],
[
updatedAccount,
borrowAssetsData,
lendingAssetsData,
prices,
hlsStrategies,
assets,
props.isHls,
],
)
return (
@@ -1,8 +1,8 @@
import { useCallback, useEffect } from 'react'
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
import AccountFundFullPage from 'components/Account/AccountFund/AccountFundFullPage'
import FullOverlayContent from 'components/FullOverlayContent'
import AccountFundFullPage from 'components/account/AccountFund/AccountFundFullPage'
import FullOverlayContent from 'components/common/FullOverlayContent'
import WalletSelect from 'components/Wallet/WalletSelect'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
@@ -1,7 +1,7 @@
import classNames from 'classnames'
import { FormattedNumber } from 'components/FormattedNumber'
import { ArrowRight } from 'components/Icons'
import { FormattedNumber } from 'components/common/FormattedNumber'
import { ArrowRight } from 'components/common/Icons'
interface Props {
leverage: number
@@ -1,6 +1,6 @@
import { HealthGauge } from 'components/Account/Health/HealthGauge'
import Loading from 'components/Loading'
import Text from 'components/Text'
import { HealthGauge } from 'components/account/Health/HealthGauge'
import Loading from 'components/common/Loading'
import Text from 'components/common/Text'
export default function Skeleton() {
return (
@@ -2,30 +2,32 @@ import classNames from 'classnames'
import { useCallback, useMemo } from 'react'
import { useLocation } from 'react-router-dom'
import AccountBalancesTable from 'components/Account/AccountBalancesTable'
import AccountComposition from 'components/Account/AccountComposition'
import AccountDetailsLeverage from 'components/Account/AccountDetails/AccountDetailsLeverage'
import Skeleton from 'components/Account/AccountDetails/Skeleton'
import { HealthGauge } from 'components/Account/Health/HealthGauge'
import EscButton from 'components/Button/EscButton'
import { glowElement } from 'components/Button/utils'
import Card from 'components/Card'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import { ThreeDots } from 'components/Icons'
import Text from 'components/Text'
import AccountBalancesTable from 'components/account/AccountBalancesTable'
import AccountComposition from 'components/account/AccountComposition'
import AccountDetailsLeverage from 'components/account/AccountDetails/AccountDetailsLeverage'
import Skeleton from 'components/account/AccountDetails/Skeleton'
import AccountPerpPositionTable from 'components/account/AccountPerpPositionTable'
import { HealthGauge } from 'components/account/Health/HealthGauge'
import EscButton from 'components/common/Button/EscButton'
import { glowElement } from 'components/common/Button/utils'
import Card from 'components/common/Card'
import DisplayCurrency from 'components/common/DisplayCurrency'
import { FormattedNumber } from 'components/common/FormattedNumber'
import { ThreeDots } from 'components/common/Icons'
import Text from 'components/common/Text'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import { ORACLE_DENOM } from 'constants/oracle'
import useAccountIds from 'hooks/accounts/useAccountIds'
import useAccounts from 'hooks/accounts/useAccounts'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useAllAssets from 'hooks/assets/useAllAssets'
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useAccountId from 'hooks/useAccountId'
import useAccountIds from 'hooks/useAccountIds'
import useAccounts from 'hooks/useAccounts'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useHealthComputer from 'hooks/useHealthComputer'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import useHealthComputer from 'hooks/useHealthComputer'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
@@ -72,21 +74,25 @@ function AccountDetails(props: Props) {
updatedAccount || account,
)
const { data: prices } = usePrices()
const assets = useAllAssets()
const accountBalanceValue = useMemo(
() => calculateAccountBalanceValue(updatedAccount ?? account, prices),
[updatedAccount, account, prices],
() => calculateAccountBalanceValue(updatedAccount ?? account, prices, assets),
[updatedAccount, account, prices, assets],
)
const coin = BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, accountBalanceValue)
const leverage = useMemo(() => calculateAccountLeverage(account, prices), [account, prices])
const leverage = useMemo(
() => calculateAccountLeverage(account, prices, assets),
[account, assets, prices],
)
const updatedLeverage = useMemo(() => {
if (!updatedAccount) return null
const updatedLeverage = calculateAccountLeverage(updatedAccount, prices)
const updatedLeverage = calculateAccountLeverage(updatedAccount, prices, assets)
if (updatedLeverage.eq(leverage)) return null
return updatedLeverage
}, [updatedAccount, prices, leverage])
}, [updatedAccount, prices, leverage, assets])
const { data } = useBorrowMarketAssetsTableData(false)
const data = useBorrowMarketAssetsTableData()
const borrowAssetsData = useMemo(() => data?.allAssets || [], [data])
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
@@ -104,11 +110,15 @@ function AccountDetails(props: Props) {
lendingAssetsData,
prices,
hlsStrategies,
assets,
account.kind === 'high_levered_strategy',
),
[account, borrowAssetsData, hlsStrategies, lendingAssetsData, prices, updatedAccount],
[account, assets, borrowAssetsData, hlsStrategies, lendingAssetsData, prices, updatedAccount],
)
const isFullWidth = location.pathname.includes('trade') || location.pathname === '/'
const isFullWidth =
location.pathname.includes('trade') ||
location.pathname === '/' ||
location.pathname.includes('perps')
function AccountDetailsHeader() {
const onClose = useCallback(() => useStore.setState({ accountDetailsExpanded: false }), [])
@@ -209,6 +219,12 @@ function AccountDetails(props: Props) {
lendingData={lendingAssetsData}
hideCard
/>
{account.perps.length > 0 && (
<>
<Text className='w-full px-4 py-2 text-white bg-white/10'>Perp Positions</Text>
<AccountPerpPositionTable account={account} hideCard />
</>
)}
</Card>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More