v2.2.0 (#746)
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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 |
@@ -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 (
|
||||
|
||||
@@ -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],
|
||||
)
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -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],
|
||||
)
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import AccountDetailsCard from 'components/Trade/AccountDetailsCard'
|
||||
|
||||
export function PerpsPositions() {
|
||||
return <AccountDetailsCard />
|
||||
}
|
||||
@@ -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)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
+3
-3
@@ -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>
|
||||
)
|
||||
}
|
||||
+6
-6
@@ -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)
|
||||
+2
-2
@@ -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) {
|
||||
+3
-3
@@ -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 => {
|
||||
+14
-10
@@ -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 />
|
||||
)
|
||||
}
|
||||
+9
-10
@@ -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,
|
||||
+6
-7
@@ -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'
|
||||
}
|
||||
+8
-8
@@ -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'
|
||||
/>
|
||||
)
|
||||
}
|
||||
+21
-11
@@ -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,
|
||||
])
|
||||
}
|
||||
+29
-14
@@ -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 (
|
||||
+2
-2
@@ -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'
|
||||
+2
-2
@@ -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
|
||||
+3
-3
@@ -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 (
|
||||
+41
-25
@@ -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
Reference in New Issue
Block a user