🌟 Add HLS Vault Modal (#595)
* 🌟 Add HLS Vault Modal * 🛠️ Fix failing build * fix: keep the selected accountId if its present int the url (#588) * Link changelog (#589) * env: update RPC endpoint * feat: added changelog link to the footer version * Refactor balances table (#590) * env: update env.example after last sync * tidy: refactored AccountBalancesTable * fix: updated isCard to hideCard * fix: do update the health on sliding the margin back to 0 (#593) * fix: disable highlighting on non-expandable rows (#592) * Healthfactor adjustments (#594) * fix: do update the health on sliding the margin back to 0 * MP-3531: first updates on the health bars * fix: added exponential function for health percentage * fix: build fix * tidy: refactor * tidy: cleanup * feat: added new curve * fix: base set to 3.5 * env: version update * 🌟 Add HLS Vault Modal * Use `DisplayCurrency` in subtitle header * 🔥Remove redundant component --------- Co-authored-by: Linkie Link <linkielink.dev@gmail.com>
This commit is contained in:
parent
1b8ff00e91
commit
65ee49a3cd
@ -14,9 +14,9 @@ jest.mock('hooks/useHealthComputer', () =>
|
|||||||
jest.mock('components/Account/AccountBalancesTable', () => jest.fn(() => null))
|
jest.mock('components/Account/AccountBalancesTable', () => jest.fn(() => null))
|
||||||
|
|
||||||
const mockedUseCurrentAccount = useCurrentAccount as jest.Mock
|
const mockedUseCurrentAccount = useCurrentAccount as jest.Mock
|
||||||
const mockedAccounts = [
|
const mockedAccounts: Account[] = [
|
||||||
{ id: '1', deposits: [], lends: [], debts: [], vaults: [] },
|
{ id: '1', deposits: [], lends: [], debts: [], vaults: [], kind: 'default' },
|
||||||
{ id: '2', deposits: [], lends: [], debts: [], vaults: [] },
|
{ id: '2', deposits: [], lends: [], debts: [], vaults: [], kind: 'default' },
|
||||||
]
|
]
|
||||||
jest.mock('hooks/useAccountId', () => jest.fn(() => '1'))
|
jest.mock('hooks/useAccountId', () => jest.fn(() => '1'))
|
||||||
jest.mock('hooks/useAccounts', () =>
|
jest.mock('hooks/useAccounts', () =>
|
||||||
|
@ -4,16 +4,16 @@ import getDepositedVaults from 'api/vaults/getDepositedVaults'
|
|||||||
import { BNCoin } from 'types/classes/BNCoin'
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
import { Positions } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
import { Positions } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||||
|
|
||||||
export default async function getAccount(accountId: string): Promise<Account> {
|
export default async function getAccount(accountIdAndKind: AccountIdAndKind): Promise<Account> {
|
||||||
const creditManagerQueryClient = await getCreditManagerQueryClient()
|
const creditManagerQueryClient = await getCreditManagerQueryClient()
|
||||||
|
|
||||||
const accountPosition: Positions = await cacheFn(
|
const accountPosition: Positions = await cacheFn(
|
||||||
() => creditManagerQueryClient.positions({ accountId }),
|
() => creditManagerQueryClient.positions({ accountId: accountIdAndKind.id }),
|
||||||
positionsCache,
|
positionsCache,
|
||||||
`account/${accountId}`,
|
`account/${accountIdAndKind.id}`,
|
||||||
)
|
)
|
||||||
|
|
||||||
const depositedVaults = await getDepositedVaults(accountId, accountPosition)
|
const depositedVaults = await getDepositedVaults(accountIdAndKind.id, accountPosition)
|
||||||
|
|
||||||
if (accountPosition) {
|
if (accountPosition) {
|
||||||
return {
|
return {
|
||||||
@ -22,6 +22,7 @@ export default async function getAccount(accountId: string): Promise<Account> {
|
|||||||
lends: accountPosition.lends.map((lend) => new BNCoin(lend)),
|
lends: accountPosition.lends.map((lend) => new BNCoin(lend)),
|
||||||
deposits: accountPosition.deposits.map((deposit) => new BNCoin(deposit)),
|
deposits: accountPosition.deposits.map((deposit) => new BNCoin(deposit)),
|
||||||
vaults: depositedVaults,
|
vaults: depositedVaults,
|
||||||
|
kind: accountIdAndKind.kind,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,25 +1,27 @@
|
|||||||
import { getAccountNftQueryClient } from 'api/cosmwasm-client'
|
import { getCreditManagerQueryClient } from 'api/cosmwasm-client'
|
||||||
import { ITEM_LIMIT_PER_QUERY } from 'constants/query'
|
import { ITEM_LIMIT_PER_QUERY } from 'constants/query'
|
||||||
|
|
||||||
export default async function getAccountIds(
|
export default async function getAccountIds(
|
||||||
address?: string,
|
address?: string,
|
||||||
previousResults?: string[],
|
previousResults?: AccountIdAndKind[],
|
||||||
): Promise<string[]> {
|
): Promise<AccountIdAndKind[]> {
|
||||||
if (!address) return []
|
if (!address) return []
|
||||||
try {
|
try {
|
||||||
const accountNftQueryClient = await getAccountNftQueryClient()
|
const client = await getCreditManagerQueryClient()
|
||||||
|
|
||||||
const lastItem = previousResults && previousResults.at(-1)
|
const lastItem = previousResults && previousResults.at(-1)
|
||||||
const results = await accountNftQueryClient.tokens({
|
const accounts = (
|
||||||
limit: ITEM_LIMIT_PER_QUERY,
|
await client.accounts({
|
||||||
startAfter: lastItem,
|
limit: ITEM_LIMIT_PER_QUERY,
|
||||||
owner: address,
|
startAfter: lastItem?.id,
|
||||||
})
|
owner: address,
|
||||||
|
})
|
||||||
|
).map((account) => ({ id: account.id, kind: account.kind }) as AccountIdAndKind)
|
||||||
|
|
||||||
const accumulated = (previousResults ?? []).concat(results.tokens)
|
const accumulated = (previousResults ?? []).concat(accounts)
|
||||||
|
|
||||||
if (results.tokens.length < ITEM_LIMIT_PER_QUERY) {
|
if (accounts.length < ITEM_LIMIT_PER_QUERY) {
|
||||||
return accumulated.sort((a, b) => parseInt(a) - parseInt(b))
|
return accumulated.sort((a, b) => parseInt(a.id) - parseInt(b.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
return await getAccountIds(address, accumulated)
|
return await getAccountIds(address, accumulated)
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import getAccount from 'api/accounts/getAccount'
|
import getAccount from 'api/accounts/getAccount'
|
||||||
import getWalletAccountIds from 'api/wallets/getAccountIds'
|
import getWalletAccountIds from 'api/wallets/getAccountIds'
|
||||||
|
import { AccountKind } from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types'
|
||||||
|
|
||||||
export default async function getAccounts(address?: string): Promise<Account[]> {
|
export default async function getAccounts(kind: AccountKind, address?: string): Promise<Account[]> {
|
||||||
if (!address) return []
|
if (!address) return new Promise((_, reject) => reject('No address'))
|
||||||
const accountIds: string[] = await getWalletAccountIds(address)
|
const accountIdsAndKinds = await getWalletAccountIds(address)
|
||||||
|
|
||||||
const $accounts = accountIds.map((accountId) => getAccount(accountId))
|
const $accounts = accountIdsAndKinds
|
||||||
|
.filter((a) => a.kind === kind)
|
||||||
|
.map((account) => getAccount(account))
|
||||||
|
|
||||||
const accounts = await Promise.all($accounts).then((accounts) => accounts)
|
const accounts = await Promise.all($accounts).then((accounts) => accounts)
|
||||||
|
|
||||||
|
@ -20,7 +20,10 @@ export default function AccordionContent(props: Props) {
|
|||||||
const { title, renderContent, isOpen, renderSubTitle, toggleOpen } = props.item
|
const { title, renderContent, isOpen, renderSubTitle, toggleOpen } = props.item
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={title} className='border-b border-collapse group border-white/20 last:border-b-0'>
|
<div
|
||||||
|
key={title}
|
||||||
|
className='border-b border-collapse group/accordion border-white/20 last:border-b-0'
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
onClick={() => toggleOpen(props.index)}
|
onClick={() => toggleOpen(props.index)}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
@ -33,7 +36,7 @@ export default function AccordionContent(props: Props) {
|
|||||||
<Text>{title}</Text>
|
<Text>{title}</Text>
|
||||||
{renderSubTitle()}
|
{renderSubTitle()}
|
||||||
</div>
|
</div>
|
||||||
<div className='block pr-1 group-[[open]]:hidden'>
|
<div className='block pr-1 group-[[open]]/accordion:hidden'>
|
||||||
{isOpen ? <ChevronDown className='h-1.5' /> : <ChevronRight className='w-1.5' />}
|
{isOpen ? <ChevronDown className='h-1.5' /> : <ChevronRight className='w-1.5' />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,7 +21,7 @@ export default function AccountCreateFirst() {
|
|||||||
|
|
||||||
const handleClick = useCallback(async () => {
|
const handleClick = useCallback(async () => {
|
||||||
setIsCreating(true)
|
setIsCreating(true)
|
||||||
const accountId = await createAccount()
|
const accountId = await createAccount('default')
|
||||||
setIsCreating(false)
|
setIsCreating(false)
|
||||||
if (accountId) {
|
if (accountId) {
|
||||||
navigate(getRoute(getPage(pathname), address, accountId))
|
navigate(getRoute(getPage(pathname), address, accountId))
|
||||||
|
@ -5,7 +5,7 @@ import Text from 'components/Text'
|
|||||||
export default function Skeleton() {
|
export default function Skeleton() {
|
||||||
return (
|
return (
|
||||||
<div className='absolute flex items-start w-16 gap-4 right-4 top-6 opacity-90'>
|
<div className='absolute flex items-start w-16 gap-4 right-4 top-6 opacity-90'>
|
||||||
<div className='relative flex flex-wrap w-16 border min-w-16 group rounded-base border-white/20'>
|
<div className='relative flex flex-wrap w-16 border min-w-16 rounded-base border-white/20'>
|
||||||
<div className='flex flex-wrap justify-center w-full py-4'>
|
<div className='flex flex-wrap justify-center w-full py-4'>
|
||||||
<HealthGauge health={0} healthFactor={0} />
|
<HealthGauge health={0} healthFactor={0} />
|
||||||
<Text size='2xs' className='mb-0.5 mt-1 w-full text-center text-white/50'>
|
<Text size='2xs' className='mb-0.5 mt-1 w-full text-center text-white/50'>
|
||||||
|
@ -35,7 +35,7 @@ import {
|
|||||||
|
|
||||||
export default function AccountDetailsController() {
|
export default function AccountDetailsController() {
|
||||||
const address = useStore((s) => s.address)
|
const address = useStore((s) => s.address)
|
||||||
const { data: accounts, isLoading } = useAccounts(address)
|
const { data: accounts, isLoading } = useAccounts('default', address)
|
||||||
const { data: accountIds } = useAccountIds(address, false)
|
const { data: accountIds } = useAccountIds(address, false)
|
||||||
const accountId = useAccountId()
|
const accountId = useAccountId()
|
||||||
const account = useCurrentAccount()
|
const account = useCurrentAccount()
|
||||||
@ -121,7 +121,7 @@ function AccountDetails(props: Props) {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'flex flex-wrap min-w-16 w-16 group relative',
|
'flex flex-wrap min-w-16 w-16 group/accountdetail relative',
|
||||||
'border rounded-base border-white/20',
|
'border rounded-base border-white/20',
|
||||||
'bg-white/5 backdrop-blur-sticky',
|
'bg-white/5 backdrop-blur-sticky',
|
||||||
!reduceMotion && 'transition-colors duration-300',
|
!reduceMotion && 'transition-colors duration-300',
|
||||||
@ -171,7 +171,7 @@ function AccountDetails(props: Props) {
|
|||||||
'flex justify-center items-center w-full h-6 opacity-50',
|
'flex justify-center items-center w-full h-6 opacity-50',
|
||||||
!reduceMotion && 'transition-[opacity] duration-300',
|
!reduceMotion && 'transition-[opacity] duration-300',
|
||||||
'absolute -bottom-6',
|
'absolute -bottom-6',
|
||||||
'group-hover:opacity-100',
|
'group-hover/accountdetail:opacity-100',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{!accountDetailsExpanded && <ThreeDots className='h-1' />}
|
{!accountDetailsExpanded && <ThreeDots className='h-1' />}
|
||||||
|
@ -13,7 +13,7 @@ export default function AccountFundFullPage() {
|
|||||||
const address = useStore((s) => s.address)
|
const address = useStore((s) => s.address)
|
||||||
const accountId = useAccountId()
|
const accountId = useAccountId()
|
||||||
|
|
||||||
const { data: accounts, isLoading } = useAccounts(address)
|
const { data: accounts, isLoading } = useAccounts('default', address)
|
||||||
const currentAccount = useCurrentAccount()
|
const currentAccount = useCurrentAccount()
|
||||||
const [selectedAccountId, setSelectedAccountId] = useState<null | string>(null)
|
const [selectedAccountId, setSelectedAccountId] = useState<null | string>(null)
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ interface Props {
|
|||||||
|
|
||||||
export default function AccountStats(props: Props) {
|
export default function AccountStats(props: Props) {
|
||||||
const { accountId, isActive, setShowMenu } = props
|
const { accountId, isActive, setShowMenu } = props
|
||||||
const { data: account } = useAccount(accountId)
|
const { data: account } = useAccount('default', accountId)
|
||||||
const { data: prices } = usePrices()
|
const { data: prices } = usePrices()
|
||||||
const positionBalance = useMemo(
|
const positionBalance = useMemo(
|
||||||
() => (!account ? null : calculateAccountBalanceValue(account, prices)),
|
() => (!account ? null : calculateAccountBalanceValue(account, prices)),
|
||||||
|
@ -59,7 +59,7 @@ export default function AccountMenuContent() {
|
|||||||
const performCreateAccount = useCallback(async () => {
|
const performCreateAccount = useCallback(async () => {
|
||||||
setShowMenu(false)
|
setShowMenu(false)
|
||||||
setIsCreating(true)
|
setIsCreating(true)
|
||||||
const accountId = await createAccount()
|
const accountId = await createAccount('default')
|
||||||
setIsCreating(false)
|
setIsCreating(false)
|
||||||
|
|
||||||
if (accountId) {
|
if (accountId) {
|
||||||
|
@ -7,6 +7,7 @@ import { demagnify } from 'utils/formatters'
|
|||||||
interface Props {
|
interface Props {
|
||||||
asset: Asset
|
asset: Asset
|
||||||
amount: BigNumber
|
amount: BigNumber
|
||||||
|
isApproximation?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AmountAndValue(props: Props) {
|
export default function AmountAndValue(props: Props) {
|
||||||
@ -15,7 +16,7 @@ export default function AmountAndValue(props: Props) {
|
|||||||
const isBelowMinAmount = amount < MIN_AMOUNT
|
const isBelowMinAmount = amount < MIN_AMOUNT
|
||||||
const displayAmount = isBelowMinAmount ? MIN_AMOUNT : amount
|
const displayAmount = isBelowMinAmount ? MIN_AMOUNT : amount
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-[0.5] text-xs'>
|
<div className='flex flex-col gap-[0.5] text-xs text-right'>
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
amount={isZero ? 0 : displayAmount}
|
amount={isZero ? 0 : displayAmount}
|
||||||
smallerThanThreshold={!isZero && isBelowMinAmount}
|
smallerThanThreshold={!isZero && isBelowMinAmount}
|
||||||
@ -25,6 +26,7 @@ export default function AmountAndValue(props: Props) {
|
|||||||
<DisplayCurrency
|
<DisplayCurrency
|
||||||
className='justify-end text-xs text-white/50'
|
className='justify-end text-xs text-white/50'
|
||||||
coin={BNCoin.fromDenomAndBigNumber(props.asset.denom, props.amount)}
|
coin={BNCoin.fromDenomAndBigNumber(props.asset.denom, props.amount)}
|
||||||
|
isApproximation={props.isApproximation}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -2,7 +2,6 @@ import { useCallback, useEffect } from 'react'
|
|||||||
|
|
||||||
import Button from 'components/Button'
|
import Button from 'components/Button'
|
||||||
import { Cross } from 'components/Icons'
|
import { Cross } from 'components/Icons'
|
||||||
import Text from 'components/Text'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
enableKeyPress?: boolean
|
enableKeyPress?: boolean
|
||||||
@ -33,13 +32,11 @@ export default function EscButton(props: Props) {
|
|||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
onClick={props.onClick}
|
onClick={props.onClick}
|
||||||
leftIcon={<Cross />}
|
leftIcon={<Cross size={16} />}
|
||||||
iconClassName='w-3'
|
iconClassName='w-3'
|
||||||
color='tertiary'
|
color='tertiary'
|
||||||
className={props.className ? props.className : 'h-3 w-13'}
|
className={props.className ? props.className : 'h-8 w-8'}
|
||||||
size='xs'
|
size='xs'
|
||||||
>
|
/>
|
||||||
{!props.hideText && <Text size='2xs'>ESC</Text>}
|
|
||||||
</Button>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import classNames from 'classnames'
|
||||||
|
|
||||||
import { ExternalLink } from 'components/Icons'
|
import { ExternalLink } from 'components/Icons'
|
||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
import { TextLink } from 'components/TextLink'
|
import { TextLink } from 'components/TextLink'
|
||||||
@ -5,6 +7,7 @@ import { DocURL } from 'types/enums/docURL'
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
type: DocLinkType
|
type: DocLinkType
|
||||||
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function getData(type: string) {
|
function getData(type: string) {
|
||||||
@ -18,12 +21,15 @@ export default function DocsLink(props: Props) {
|
|||||||
const [intro, linkText, url] = getData(props.type)
|
const [intro, linkText, url] = getData(props.type)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text size='sm' className='w-full pt-3 text-center text-white/60'>
|
<Text
|
||||||
|
size='sm'
|
||||||
|
className={classNames('w-full pt-3 text-center text-white/60', props.className)}
|
||||||
|
>
|
||||||
{`${intro} `}
|
{`${intro} `}
|
||||||
<TextLink
|
<TextLink
|
||||||
href={url}
|
href={url}
|
||||||
target='_blank'
|
target='_blank'
|
||||||
className='ml-1 leading-4 text-white hover:underline'
|
className={classNames('ml-1 leading-4 text-white hover:underline', props.className)}
|
||||||
title={linkText}
|
title={linkText}
|
||||||
>
|
>
|
||||||
{linkText}
|
{linkText}
|
||||||
|
@ -2,12 +2,13 @@ import AssetImage from 'components/Asset/AssetImage'
|
|||||||
import { getAssetByDenom } from 'utils/assets'
|
import { getAssetByDenom } from 'utils/assets'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
vault: VaultMetaData
|
primaryDenom: string
|
||||||
|
secondaryDenom: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function VaultLogo(props: Props) {
|
export default function DoubleLogo(props: Props) {
|
||||||
const primaryAsset = getAssetByDenom(props.vault.denoms.primary)
|
const primaryAsset = getAssetByDenom(props.primaryDenom)
|
||||||
const secondaryAsset = getAssetByDenom(props.vault.denoms.secondary)
|
const secondaryAsset = getAssetByDenom(props.secondaryDenom)
|
||||||
|
|
||||||
if (!primaryAsset || !secondaryAsset) return null
|
if (!primaryAsset || !secondaryAsset) return null
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import VaultLogo from 'components/Earn/Farm/VaultLogo'
|
import DoubleLogo from 'components/DoubleLogo'
|
||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||||
import { VaultStatus } from 'types/enums/vault'
|
import { VaultStatus } from 'types/enums/vault'
|
||||||
@ -28,7 +28,7 @@ export default function Name(props: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex'>
|
<div className='flex'>
|
||||||
<VaultLogo vault={vault} />
|
<DoubleLogo primaryDenom={vault.denoms.primary} secondaryDenom={vault.denoms.secondary} />
|
||||||
<TitleAndSubCell
|
<TitleAndSubCell
|
||||||
className='ml-2 mr-2 text-left'
|
className='ml-2 mr-2 text-left'
|
||||||
title={`${vault.name}${unlockDuration}`}
|
title={`${vault.name}${unlockDuration}`}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import ActionButton from 'components/Button/ActionButton'
|
import ActionButton from 'components/Button/ActionButton'
|
||||||
import VaultLogo from 'components/Earn/Farm/VaultLogo'
|
import DoubleLogo from 'components/DoubleLogo'
|
||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||||
import useCurrentAccount from 'hooks/useCurrentAccount'
|
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||||
@ -44,7 +44,10 @@ export default function VaultCard(props: Props) {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<VaultLogo vault={props.vault} />
|
<DoubleLogo
|
||||||
|
primaryDenom={props.vault.denoms.primary}
|
||||||
|
secondaryDenom={props.vault.denoms.secondary}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-between mb-6'>
|
<div className='flex justify-between mb-6'>
|
||||||
<TitleAndSubCell
|
<TitleAndSubCell
|
||||||
|
@ -8,6 +8,7 @@ import Text from 'components/Text'
|
|||||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||||
import useAlertDialog from 'hooks/useAlertDialog'
|
import useAlertDialog from 'hooks/useAlertDialog'
|
||||||
import useLocalStorage from 'hooks/useLocalStorage'
|
import useLocalStorage from 'hooks/useLocalStorage'
|
||||||
|
import useStore from 'store'
|
||||||
|
|
||||||
export const DEPOSIT_META = { accessorKey: 'deposit', header: 'Deposit' }
|
export const DEPOSIT_META = { accessorKey: 'deposit', header: 'Deposit' }
|
||||||
|
|
||||||
@ -26,8 +27,12 @@ export default function Deposit(props: Props) {
|
|||||||
|
|
||||||
const { open: openAlertDialog, close } = useAlertDialog()
|
const { open: openAlertDialog, close } = useAlertDialog()
|
||||||
|
|
||||||
const showHlsInfoModal = useCallback(() => {
|
const enterVaultHandler = useCallback(() => {
|
||||||
if (!showHlsInfo) return
|
if (!showHlsInfo) {
|
||||||
|
openHlsModal()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
openAlertDialog({
|
openAlertDialog({
|
||||||
title: 'Understanding HLS Positions',
|
title: 'Understanding HLS Positions',
|
||||||
content: (
|
content: (
|
||||||
@ -53,7 +58,7 @@ export default function Deposit(props: Props) {
|
|||||||
positiveButton: {
|
positiveButton: {
|
||||||
text: 'Continue',
|
text: 'Continue',
|
||||||
icon: <Enter />,
|
icon: <Enter />,
|
||||||
onClick: enterVaultHandler,
|
onClick: openHlsModal,
|
||||||
},
|
},
|
||||||
negativeButton: {
|
negativeButton: {
|
||||||
text: 'Cancel',
|
text: 'Cancel',
|
||||||
@ -67,11 +72,10 @@ export default function Deposit(props: Props) {
|
|||||||
onClick: (isChecked: boolean) => setShowHlsInfo(!isChecked),
|
onClick: (isChecked: boolean) => setShowHlsInfo(!isChecked),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}, [close, enterVaultHandler, openAlertDialog, setShowHlsInfo, showHlsInfo])
|
}, [close, openAlertDialog, setShowHlsInfo, showHlsInfo])
|
||||||
|
|
||||||
function enterVaultHandler() {
|
function openHlsModal() {
|
||||||
showHlsInfoModal()
|
useStore.setState({ hlsModal: { vault } })
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.isLoading) return <Loading />
|
if (props.isLoading) return <Loading />
|
||||||
|
@ -3,7 +3,6 @@ import { ReactNode, useEffect, useRef } from 'react'
|
|||||||
|
|
||||||
import EscButton from 'components/Button/EscButton'
|
import EscButton from 'components/Button/EscButton'
|
||||||
import Card from 'components/Card'
|
import Card from 'components/Card'
|
||||||
import useStore from 'store'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
header: string | ReactNode
|
header: string | ReactNode
|
||||||
|
17
src/components/Modals/HLS/ChooseAccount.tsx
Normal file
17
src/components/Modals/HLS/ChooseAccount.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import Button from 'components/Button'
|
||||||
|
import { ArrowRight } from 'components/Icons'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
hlsAccounts: AccountIdAndKind[]
|
||||||
|
onClickBtn: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ChooseAccount(props: Props) {
|
||||||
|
return (
|
||||||
|
<div className='p-4'>
|
||||||
|
<Button onClick={props.onClickBtn} text='Continue' rightIcon={<ArrowRight />} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
142
src/components/Modals/HLS/Content.tsx
Normal file
142
src/components/Modals/HLS/Content.tsx
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import React, { useMemo, useState } from 'react'
|
||||||
|
|
||||||
|
import Accordion from 'components/Accordion'
|
||||||
|
import { Item } from 'components/AccordionContent'
|
||||||
|
import CreateAccount from 'components/Modals/HLS/CreateAccount'
|
||||||
|
import Leverage from 'components/Modals/HLS/Leverage'
|
||||||
|
import ProvideCollateral from 'components/Modals/HLS/ProvideCollateral'
|
||||||
|
import SelectAccount from 'components/Modals/HLS/SelectAccount'
|
||||||
|
import { CollateralSubTitle, LeverageSubTitle, SubTitle } from 'components/Modals/HLS/SubTitles'
|
||||||
|
import Summary from 'components/Modals/HLS/Summary'
|
||||||
|
import { BN_ZERO } from 'constants/math'
|
||||||
|
import useAccounts from 'hooks/useAccounts'
|
||||||
|
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
|
||||||
|
import useDepositHlsVault from 'hooks/useDepositHlsVault'
|
||||||
|
import useIsOpenArray from 'hooks/useIsOpenArray'
|
||||||
|
import useStore from 'store'
|
||||||
|
import { getAssetByDenom } from 'utils/assets'
|
||||||
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
vault: Vault
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Content(props: Props) {
|
||||||
|
const [selectedAccount, setSelectedAccount] = useState<Account | null>(null)
|
||||||
|
const [isOpen, toggleIsOpen] = useIsOpenArray(4, false)
|
||||||
|
const address = useStore((s) => s.address)
|
||||||
|
const { data: hlsAccounts } = useAccounts('high_levered_strategy', address)
|
||||||
|
const collateralAsset = getAssetByDenom(props.vault.denoms.primary)
|
||||||
|
const borrowAsset = getAssetByDenom(props.vault.denoms.secondary)
|
||||||
|
const walletCollateralAsset = useCurrentWalletBalance(props.vault.denoms.primary)
|
||||||
|
const { setDepositAmount, depositAmount, setBorrowAmount, borrowAmount, positionValue } =
|
||||||
|
useDepositHlsVault({
|
||||||
|
vault: props.vault,
|
||||||
|
})
|
||||||
|
|
||||||
|
const items: Item[] = useMemo(() => {
|
||||||
|
if (!collateralAsset || !borrowAsset) return []
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'Provide Collateral',
|
||||||
|
renderContent: () => (
|
||||||
|
<ProvideCollateral
|
||||||
|
amount={depositAmount}
|
||||||
|
onChangeAmount={setDepositAmount}
|
||||||
|
asset={collateralAsset}
|
||||||
|
onClickBtn={() => toggleIsOpen(1)}
|
||||||
|
max={BN(walletCollateralAsset?.amount || 0)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
renderSubTitle: () => (
|
||||||
|
<CollateralSubTitle
|
||||||
|
isOpen={isOpen[0]}
|
||||||
|
amount={depositAmount}
|
||||||
|
denom={collateralAsset.denom}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
isOpen: isOpen[0],
|
||||||
|
|
||||||
|
toggleOpen: toggleIsOpen,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Leverage',
|
||||||
|
renderContent: () => (
|
||||||
|
<Leverage
|
||||||
|
amount={depositAmount}
|
||||||
|
asset={borrowAsset}
|
||||||
|
// TODO: Get max borrow amount
|
||||||
|
max={BN_ZERO}
|
||||||
|
onChangeAmount={setDepositAmount}
|
||||||
|
onClickBtn={() => toggleIsOpen(2)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
renderSubTitle: () => (
|
||||||
|
// TODO: Add leverage
|
||||||
|
<LeverageSubTitle leverage={1.1} isOpen={isOpen[1]} positionValue={positionValue} />
|
||||||
|
),
|
||||||
|
isOpen: isOpen[1],
|
||||||
|
toggleOpen: toggleIsOpen,
|
||||||
|
},
|
||||||
|
...[
|
||||||
|
hlsAccounts.length > 2
|
||||||
|
? {
|
||||||
|
title: 'Select HLS Account',
|
||||||
|
renderContent: () => (
|
||||||
|
<SelectAccount
|
||||||
|
selectedAccount={selectedAccount}
|
||||||
|
onChangeSelected={setSelectedAccount}
|
||||||
|
hlsAccounts={hlsAccounts}
|
||||||
|
onClickBtn={() => toggleIsOpen(3)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
renderSubTitle: () =>
|
||||||
|
selectedAccount && !isOpen[2] ? (
|
||||||
|
<SubTitle text={`Account ${selectedAccount.id}`} />
|
||||||
|
) : null,
|
||||||
|
isOpen: isOpen[2],
|
||||||
|
toggleOpen: toggleIsOpen,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
title: 'Create HLS Account',
|
||||||
|
renderContent: () => <CreateAccount />,
|
||||||
|
renderSubTitle: () => null,
|
||||||
|
isOpen: isOpen[2],
|
||||||
|
toggleOpen: toggleIsOpen,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
title: 'Summary',
|
||||||
|
renderContent: () => (
|
||||||
|
<Summary
|
||||||
|
depositAmount={depositAmount}
|
||||||
|
borrowAmount={borrowAmount}
|
||||||
|
positionValue={positionValue}
|
||||||
|
vault={props.vault}
|
||||||
|
onClickBtn={() => {
|
||||||
|
// TODO: Implement tx execution
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
renderSubTitle: () => null,
|
||||||
|
isOpen: isOpen[3],
|
||||||
|
toggleOpen: toggleIsOpen,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}, [
|
||||||
|
collateralAsset,
|
||||||
|
borrowAsset,
|
||||||
|
isOpen,
|
||||||
|
toggleIsOpen,
|
||||||
|
hlsAccounts,
|
||||||
|
depositAmount,
|
||||||
|
setDepositAmount,
|
||||||
|
walletCollateralAsset?.amount,
|
||||||
|
selectedAccount,
|
||||||
|
borrowAmount,
|
||||||
|
positionValue,
|
||||||
|
props.vault,
|
||||||
|
])
|
||||||
|
|
||||||
|
return <Accordion items={items} />
|
||||||
|
}
|
43
src/components/Modals/HLS/CreateAccount.tsx
Normal file
43
src/components/Modals/HLS/CreateAccount.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
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 useStore from 'store'
|
||||||
|
|
||||||
|
export default function CreateAccount() {
|
||||||
|
const createAccount = useStore((s) => s.createAccount)
|
||||||
|
|
||||||
|
const [isTxPending, setIsTxPending] = useState(false)
|
||||||
|
|
||||||
|
async function handleBtnClick() {
|
||||||
|
setIsTxPending(true)
|
||||||
|
const response = await createAccount('high_levered_strategy')
|
||||||
|
|
||||||
|
if (response === null) {
|
||||||
|
setIsTxPending(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await mutate('accounts/high_levered_strategy')
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='p-4 flex-col flex'>
|
||||||
|
<Text size='sm' className='text-white/50 mb-4 mt-2'>
|
||||||
|
Creating a HLS position mandates the creation of a, single-use HLS account. This account is
|
||||||
|
deleted once you close your position.
|
||||||
|
</Text>
|
||||||
|
<Button
|
||||||
|
onClick={handleBtnClick}
|
||||||
|
text='Approve Transaction'
|
||||||
|
rightIcon={<ArrowRight />}
|
||||||
|
showProgressIndicator={isTxPending}
|
||||||
|
disabled={isTxPending}
|
||||||
|
className='mb-2'
|
||||||
|
/>
|
||||||
|
<DocsLink className='text-xs' type='account' />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
27
src/components/Modals/HLS/Header.tsx
Normal file
27
src/components/Modals/HLS/Header.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import DoubleLogo from 'components/DoubleLogo'
|
||||||
|
import Text from 'components/Text'
|
||||||
|
import { getAssetByDenom } from 'utils/assets'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
primaryDenom: string
|
||||||
|
secondaryDenom: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Header(props: Props) {
|
||||||
|
const primaryAsset = getAssetByDenom(props.primaryDenom)
|
||||||
|
const secondaryAsset = getAssetByDenom(props.secondaryDenom)
|
||||||
|
|
||||||
|
if (!primaryAsset || !secondaryAsset) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className='flex items-center gap-2'>
|
||||||
|
<DoubleLogo primaryDenom={props.primaryDenom} secondaryDenom={props.secondaryDenom} />
|
||||||
|
<Text>{`${primaryAsset.symbol} - ${secondaryAsset.symbol}`}</Text>
|
||||||
|
<Text className='rounded-sm gradient-hls px-2 font-bold py-0.5' size='xs'>
|
||||||
|
HLS
|
||||||
|
</Text>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
29
src/components/Modals/HLS/Leverage.tsx
Normal file
29
src/components/Modals/HLS/Leverage.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import Button from 'components/Button'
|
||||||
|
import { ArrowRight } from 'components/Icons'
|
||||||
|
import LeverageSummary from 'components/Modals/HLS/LeverageSummary'
|
||||||
|
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
amount: BigNumber
|
||||||
|
asset: Asset
|
||||||
|
max: BigNumber
|
||||||
|
onChangeAmount: (amount: BigNumber) => void
|
||||||
|
onClickBtn: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Leverage(props: Props) {
|
||||||
|
return (
|
||||||
|
<div className='p-4 flex-col gap-6 flex'>
|
||||||
|
<TokenInputWithSlider
|
||||||
|
amount={props.amount}
|
||||||
|
asset={props.asset}
|
||||||
|
max={props.max}
|
||||||
|
onChange={props.onChangeAmount}
|
||||||
|
/>
|
||||||
|
<LeverageSummary asset={props.asset} />
|
||||||
|
<Button onClick={props.onClickBtn} text='Continue' rightIcon={<ArrowRight />} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
41
src/components/Modals/HLS/LeverageSummary.tsx
Normal file
41
src/components/Modals/HLS/LeverageSummary.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import React, { useMemo } from 'react'
|
||||||
|
|
||||||
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
|
import Text from 'components/Text'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
asset: Asset
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LeverageSummary(props: Props) {
|
||||||
|
const items: { title: string; amount: number; options: FormatOptions }[] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'APY',
|
||||||
|
amount: 0,
|
||||||
|
options: { suffix: '%', minDecimals: 1, maxDecimals: 1 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: `Borrow APR ${props.asset.symbol}`,
|
||||||
|
amount: 0,
|
||||||
|
options: { suffix: '%', minDecimals: 1, maxDecimals: 1 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Total Position Size',
|
||||||
|
amount: 0,
|
||||||
|
options: { prefix: '$' },
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}, [props.asset.symbol])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='grid grid-cols-2'>
|
||||||
|
{items.map((item) => (
|
||||||
|
<React.Fragment key={item.title}>
|
||||||
|
<Text className='text-white/60'>{item.title}</Text>
|
||||||
|
<FormattedNumber className='place-self-end' amount={item.amount} options={item.options} />
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
28
src/components/Modals/HLS/ProvideCollateral.tsx
Normal file
28
src/components/Modals/HLS/ProvideCollateral.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import Button from 'components/Button'
|
||||||
|
import { ArrowRight } from 'components/Icons'
|
||||||
|
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
amount: BigNumber
|
||||||
|
asset: Asset
|
||||||
|
max: BigNumber
|
||||||
|
onChangeAmount: (amount: BigNumber) => void
|
||||||
|
onClickBtn: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ProvideCollateral(props: Props) {
|
||||||
|
return (
|
||||||
|
<div className='p-4 flex-col gap-6 flex'>
|
||||||
|
<TokenInputWithSlider
|
||||||
|
maxText='In wallet'
|
||||||
|
amount={props.amount}
|
||||||
|
asset={props.asset}
|
||||||
|
max={props.max}
|
||||||
|
onChange={props.onChangeAmount}
|
||||||
|
/>
|
||||||
|
<Button onClick={props.onClickBtn} text='Continue' rightIcon={<ArrowRight />} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
47
src/components/Modals/HLS/SelectAccount.tsx
Normal file
47
src/components/Modals/HLS/SelectAccount.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import classNames from 'classnames'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import Button from 'components/Button'
|
||||||
|
import { ArrowRight } from 'components/Icons'
|
||||||
|
import Radio from 'components/Radio'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
hlsAccounts: Account[]
|
||||||
|
onChangeSelected: (account: Account) => void
|
||||||
|
onClickBtn: () => void
|
||||||
|
selectedAccount: Account | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CreateAccount(props: Props) {
|
||||||
|
async function handleBtnClick() {
|
||||||
|
props.onClickBtn()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex-col flex'>
|
||||||
|
{props.hlsAccounts.map((account, index) => (
|
||||||
|
<div
|
||||||
|
key={account.id}
|
||||||
|
onClick={() => props.onChangeSelected(account)}
|
||||||
|
className={classNames(
|
||||||
|
`group/hls relative flex gap-2 items-center px-4 py-5 cursor-pointer`,
|
||||||
|
index !== props.hlsAccounts.length - 1 && 'border-b border-white/10',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Radio
|
||||||
|
active={account.id === props.selectedAccount?.id}
|
||||||
|
className={classNames(`group-hover/hls:opacity-100`)}
|
||||||
|
/>
|
||||||
|
{`HLS Account ${account.id}`}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
onClick={handleBtnClick}
|
||||||
|
text='Continue'
|
||||||
|
rightIcon={<ArrowRight />}
|
||||||
|
showProgressIndicator={false}
|
||||||
|
className='mb-2 mx-4 mb-5'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
56
src/components/Modals/HLS/SubTitles.tsx
Normal file
56
src/components/Modals/HLS/SubTitles.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import DisplayCurrency from 'components/DisplayCurrency'
|
||||||
|
import Text from 'components/Text'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
import { formatAmountWithSymbol } from 'utils/formatters'
|
||||||
|
|
||||||
|
interface SubTitleProps {
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SubTitle(props: SubTitleProps) {
|
||||||
|
return (
|
||||||
|
<Text className='text-white/60 mt-1' size='xs' tag='span'>
|
||||||
|
{props.text}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CollateralSubTitleProps {
|
||||||
|
amount: BigNumber
|
||||||
|
denom: string
|
||||||
|
isOpen: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CollateralSubTitle(props: CollateralSubTitleProps) {
|
||||||
|
if (props.isOpen || props.amount.isZero()) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SubTitle
|
||||||
|
text={formatAmountWithSymbol({
|
||||||
|
denom: props.denom,
|
||||||
|
amount: props.amount.toString(),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LeveragedSubTitleProps {
|
||||||
|
isOpen: boolean
|
||||||
|
leverage: number
|
||||||
|
positionValue: BigNumber
|
||||||
|
}
|
||||||
|
export function LeverageSubTitle(props: LeveragedSubTitleProps) {
|
||||||
|
if (props.isOpen || props.leverage <= 1) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SubTitle text={`${props.leverage}x • Total Position Value `} />
|
||||||
|
<DisplayCurrency
|
||||||
|
coin={BNCoin.fromDenomAndBigNumber('usd', props.positionValue)}
|
||||||
|
className='text-white/60 text-xs inline'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
25
src/components/Modals/HLS/Summary/ApyBreakdown.tsx
Normal file
25
src/components/Modals/HLS/Summary/ApyBreakdown.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
|
import Text from 'components/Text'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
items: { title: string; amount: number }[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AprBreakdown(props: Props) {
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col gap-2 w-[242px] p-3'>
|
||||||
|
{props.items.map((item) => (
|
||||||
|
<div key={item.title} className='flex justify-between'>
|
||||||
|
<Text className='text-white/60 text-sm'>{item.title}</Text>
|
||||||
|
<FormattedNumber
|
||||||
|
amount={item.amount}
|
||||||
|
className='text-sm'
|
||||||
|
options={{ suffix: '%', maxDecimals: 2, minDecimals: 0 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
37
src/components/Modals/HLS/Summary/AssetSummary.tsx
Normal file
37
src/components/Modals/HLS/Summary/AssetSummary.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import AmountAndValue from 'components/AmountAndValue'
|
||||||
|
import AssetImage from 'components/Asset/AssetImage'
|
||||||
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
|
import Container from 'components/Modals/HLS/Summary/Container'
|
||||||
|
import Text from 'components/Text'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
amount: BigNumber
|
||||||
|
asset: Asset | BorrowAsset
|
||||||
|
isBorrow?: boolean
|
||||||
|
}
|
||||||
|
export default function AssetSummary(props: Props) {
|
||||||
|
return (
|
||||||
|
<Container title={props.isBorrow ? 'Leverage' : 'Supplying'}>
|
||||||
|
<div className='flex justify-between'>
|
||||||
|
<span className='flex items-center gap-2'>
|
||||||
|
<AssetImage asset={props.asset} size={32} />
|
||||||
|
<Text size='xs' className='font-bold'>
|
||||||
|
{props.asset.symbol}
|
||||||
|
</Text>
|
||||||
|
</span>
|
||||||
|
<AmountAndValue asset={props.asset} amount={props.amount} isApproximation />
|
||||||
|
</div>
|
||||||
|
{props.isBorrow && (
|
||||||
|
<div className='rounded-sm bg-white/5 grid place-items-center py-2 mt-3'>
|
||||||
|
<FormattedNumber
|
||||||
|
amount={(props.asset as BorrowAsset).borrowRate ?? 0}
|
||||||
|
options={{ suffix: '% Borrow Rate', maxDecimals: 2, minDecimals: 0 }}
|
||||||
|
className='text-white/70 text-xs'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
18
src/components/Modals/HLS/Summary/Container.tsx
Normal file
18
src/components/Modals/HLS/Summary/Container.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import Text from 'components/Text'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children: React.ReactNode
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
export default function Container(props: Props) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Text size='xs' className='text-white/60 mb-2 uppercase'>
|
||||||
|
{props.title}
|
||||||
|
</Text>
|
||||||
|
<div className='p-3 rounded-sm bg-black/20'>{props.children}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
70
src/components/Modals/HLS/Summary/YourPosition.tsx
Normal file
70
src/components/Modals/HLS/Summary/YourPosition.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import React, { useMemo } from 'react'
|
||||||
|
|
||||||
|
import DisplayCurrency from 'components/DisplayCurrency'
|
||||||
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
|
import { InfoCircle } from 'components/Icons'
|
||||||
|
import AprBreakdown from 'components/Modals/HLS/Summary/ApyBreakdown'
|
||||||
|
import Container from 'components/Modals/HLS/Summary/Container'
|
||||||
|
import Text from 'components/Text'
|
||||||
|
import { Tooltip } from 'components/Tooltip'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
baseApy: number
|
||||||
|
borrowRate: number
|
||||||
|
leverage: number
|
||||||
|
positionValue: BNCoin
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function YourPosition(props: Props) {
|
||||||
|
const netApy = useMemo(
|
||||||
|
() => props.baseApy * props.leverage - props.borrowRate,
|
||||||
|
[props.baseApy, props.borrowRate, props.leverage],
|
||||||
|
)
|
||||||
|
const apyItems = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
title: 'Base APY',
|
||||||
|
amount: props.baseApy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Levered APY',
|
||||||
|
amount: props.baseApy * props.leverage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Borrow Rate',
|
||||||
|
amount: props.borrowRate,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[props.baseApy, props.borrowRate, props.leverage],
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container title='Your Position'>
|
||||||
|
<div className='flex justify-between mb-2'>
|
||||||
|
<Text className='text-white/60 text-xs'>Total Position Value</Text>
|
||||||
|
<DisplayCurrency
|
||||||
|
coin={props.positionValue}
|
||||||
|
className='text-white/60 place-self-end text-xs'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='flex justify-between'>
|
||||||
|
<Text className='text-xs group/apytooltip' tag='span'>
|
||||||
|
<Tooltip
|
||||||
|
content={<AprBreakdown items={apyItems} />}
|
||||||
|
type='info'
|
||||||
|
className='items-center flex gap-2 group-hover/apytooltip:text-white text-white/60 cursor-pointer'
|
||||||
|
>
|
||||||
|
<span className='mt-0.5'>Net APY</span>{' '}
|
||||||
|
<InfoCircle className='w-4 h-4 text-white/40 inline group-hover/apytooltip:text-white transition-all' />
|
||||||
|
</Tooltip>
|
||||||
|
</Text>
|
||||||
|
<FormattedNumber
|
||||||
|
className='text-white/60 place-self-end text-xs'
|
||||||
|
amount={netApy}
|
||||||
|
options={{ suffix: '%', minDecimals: 0, maxDecimals: 2 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
43
src/components/Modals/HLS/Summary/index.tsx
Normal file
43
src/components/Modals/HLS/Summary/index.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import Button from 'components/Button'
|
||||||
|
import { ArrowRight } from 'components/Icons'
|
||||||
|
import AssetSummary from 'components/Modals/HLS/Summary/AssetSummary'
|
||||||
|
import YourPosition from 'components/Modals/HLS/Summary/YourPosition'
|
||||||
|
import useBorrowAsset from 'hooks/useBorrowAsset'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
import { getAssetByDenom } from 'utils/assets'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
borrowAmount: BigNumber
|
||||||
|
depositAmount: BigNumber
|
||||||
|
onClickBtn: () => void
|
||||||
|
positionValue: BigNumber
|
||||||
|
vault: Vault
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Summary(props: Props) {
|
||||||
|
const collateralAsset = getAssetByDenom(props.vault.denoms.primary)
|
||||||
|
const borrowAsset = useBorrowAsset(props.vault.denoms.secondary)
|
||||||
|
|
||||||
|
if (!collateralAsset || !borrowAsset) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='p-4 flex flex-col gap-4'>
|
||||||
|
<AssetSummary asset={collateralAsset} amount={props.depositAmount} />
|
||||||
|
<AssetSummary asset={borrowAsset} amount={props.borrowAmount} isBorrow />
|
||||||
|
<YourPosition
|
||||||
|
positionValue={BNCoin.fromDenomAndBigNumber('usd', props.positionValue)}
|
||||||
|
baseApy={props.vault.apy || 0}
|
||||||
|
borrowRate={borrowAsset.borrowRate || 0}
|
||||||
|
leverage={3.5}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={props.onClickBtn}
|
||||||
|
text='Approve Funding Transaction'
|
||||||
|
rightIcon={<ArrowRight />}
|
||||||
|
className='mt-1'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
41
src/components/Modals/HLS/index.tsx
Normal file
41
src/components/Modals/HLS/index.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import Modal from 'components/Modal'
|
||||||
|
import Content from 'components/Modals/HLS/Content'
|
||||||
|
import Header from 'components/Modals/HLS/Header'
|
||||||
|
import useStore from 'store'
|
||||||
|
|
||||||
|
export default function HlsModalController() {
|
||||||
|
const modal = useStore((s) => s.hlsModal)
|
||||||
|
|
||||||
|
if (!modal?.vault) return null
|
||||||
|
|
||||||
|
return <HlsModal vault={modal.vault} />
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
vault: Vault
|
||||||
|
}
|
||||||
|
|
||||||
|
function HlsModal(props: Props) {
|
||||||
|
function handleClose() {
|
||||||
|
useStore.setState({ hlsModal: null })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
header={
|
||||||
|
<Header
|
||||||
|
primaryDenom={props.vault.denoms.primary}
|
||||||
|
secondaryDenom={props.vault.denoms.secondary}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
headerClassName='gradient-header pl-2 pr-2.5 py-3 border-b-white/5 border-b'
|
||||||
|
contentClassName='flex flex-col p-6 h-full overflow-y-scroll scrollbar-hide'
|
||||||
|
modalClassName='max-w-modal-md h-[min(80%,600px)]'
|
||||||
|
onClose={handleClose}
|
||||||
|
>
|
||||||
|
<Content vault={props.vault} />
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
@ -5,6 +5,7 @@ import {
|
|||||||
BorrowModal,
|
BorrowModal,
|
||||||
FundAndWithdrawModal,
|
FundAndWithdrawModal,
|
||||||
GetStartedModal,
|
GetStartedModal,
|
||||||
|
HlsModal,
|
||||||
LendAndReclaimModalController,
|
LendAndReclaimModalController,
|
||||||
SettingsModal,
|
SettingsModal,
|
||||||
UnlockModal,
|
UnlockModal,
|
||||||
@ -28,6 +29,7 @@ export default function ModalsContainer() {
|
|||||||
<WithdrawFromVaultsModal />
|
<WithdrawFromVaultsModal />
|
||||||
<WalletAssets />
|
<WalletAssets />
|
||||||
<AlertDialogController />
|
<AlertDialogController />
|
||||||
|
<HlsModal />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { useCallback, useMemo } from 'react'
|
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
import { useCallback, useMemo } from 'react'
|
||||||
|
|
||||||
import VaultLogo from 'components/Earn/Farm/VaultLogo'
|
import DoubleLogo from 'components/DoubleLogo'
|
||||||
|
import { InfoCircle } from 'components/Icons'
|
||||||
import Modal from 'components/Modal'
|
import Modal from 'components/Modal'
|
||||||
import VaultModalContent from 'components/Modals/Vault/VaultModalContent'
|
import VaultModalContent from 'components/Modals/Vault/VaultModalContent'
|
||||||
|
import VaultModalContentHeader from 'components/Modals/Vault/VaultModalContentHeader'
|
||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
|
import { Tooltip } from 'components/Tooltip'
|
||||||
import { ASSETS } from 'constants/assets'
|
import { ASSETS } from 'constants/assets'
|
||||||
import useCurrentAccount from 'hooks/useCurrentAccount'
|
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import VaultModalContentHeader from 'components/Modals/Vault/VaultModalContentHeader'
|
|
||||||
import { InfoCircle } from 'components/Icons'
|
|
||||||
import { Tooltip } from 'components/Tooltip'
|
|
||||||
|
|
||||||
export default function VaultModalController() {
|
export default function VaultModalController() {
|
||||||
const currentAccount = useCurrentAccount()
|
const currentAccount = useCurrentAccount()
|
||||||
@ -60,7 +60,7 @@ function VaultModal(props: Props) {
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
header={
|
header={
|
||||||
<span className='flex items-center pr-4 py-1'>
|
<span className='flex items-center pr-4 py-1'>
|
||||||
<VaultLogo vault={vault} />
|
<DoubleLogo primaryDenom={vault.denoms.primary} secondaryDenom={vault.denoms.secondary} />
|
||||||
<Text className='pl-3 pr-2'>{vault.name}</Text>
|
<Text className='pl-3 pr-2'>{vault.name}</Text>
|
||||||
{unlockTime && (
|
{unlockTime && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Button from 'components/Button'
|
import Button from 'components/Button'
|
||||||
import { CircularProgress } from 'components/CircularProgress'
|
import { CircularProgress } from 'components/CircularProgress'
|
||||||
import DisplayCurrency from 'components/DisplayCurrency'
|
import DisplayCurrency from 'components/DisplayCurrency'
|
||||||
import VaultLogo from 'components/Earn/Farm/VaultLogo'
|
import DoubleLogo from 'components/DoubleLogo'
|
||||||
import { FormattedNumber } from 'components/FormattedNumber'
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
import Modal from 'components/Modal'
|
import Modal from 'components/Modal'
|
||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
@ -60,7 +60,10 @@ export default function WithdrawFromVaultsModal() {
|
|||||||
if (!primaryAsset || !secondaryAsset) return null
|
if (!primaryAsset || !secondaryAsset) return null
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center gap-4' key={vault.unlockId}>
|
<div className='flex items-center gap-4' key={vault.unlockId}>
|
||||||
<VaultLogo vault={vault} />
|
<DoubleLogo
|
||||||
|
primaryDenom={vault.denoms.primary}
|
||||||
|
secondaryDenom={vault.denoms.secondary}
|
||||||
|
/>
|
||||||
<div className='flex flex-wrap flex-1'>
|
<div className='flex flex-wrap flex-1'>
|
||||||
<Text className='w-full'>{vault.name}</Text>
|
<Text className='w-full'>{vault.name}</Text>
|
||||||
<Text size='sm' className='w-full text-white/50'>
|
<Text size='sm' className='w-full text-white/50'>
|
||||||
|
@ -10,3 +10,4 @@ export { default as UnlockModal } from 'components/Modals/Unlock'
|
|||||||
export { default as VaultModal } from 'components/Modals/Vault'
|
export { default as VaultModal } from 'components/Modals/Vault'
|
||||||
export { default as WalletAssets } from 'components/Modals/WalletAssets'
|
export { default as WalletAssets } from 'components/Modals/WalletAssets'
|
||||||
export { default as WithdrawFromVaultsModal } from 'components/Modals/WithdrawFromVaultsModal'
|
export { default as WithdrawFromVaultsModal } from 'components/Modals/WithdrawFromVaultsModal'
|
||||||
|
export { default as HlsModal } from 'components/Modals/HLS'
|
||||||
|
@ -13,7 +13,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Content(props: Props) {
|
function Content(props: Props) {
|
||||||
const { data: account } = useAccount(props.accountId, true)
|
const { data: account } = useAccount('high_levered_strategy', props.accountId, true)
|
||||||
|
|
||||||
const { data } = useBorrowMarketAssetsTableData(false)
|
const { data } = useBorrowMarketAssetsTableData(false)
|
||||||
const borrowAssets = useMemo(() => data?.allAssets || [], [data])
|
const borrowAssets = useMemo(() => data?.allAssets || [], [data])
|
||||||
|
@ -17,7 +17,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Content(props: Props) {
|
function Content(props: Props) {
|
||||||
const { data: account } = useAccount(props.accountId, true)
|
const { data: account } = useAccount('default', props.accountId, true)
|
||||||
const { data: prices } = usePrices()
|
const { data: prices } = usePrices()
|
||||||
const { health, healthFactor } = useHealthComputer(account)
|
const { health, healthFactor } = useHealthComputer(account)
|
||||||
const { data } = useBorrowMarketAssetsTableData(false)
|
const { data } = useBorrowMarketAssetsTableData(false)
|
||||||
|
@ -27,7 +27,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function PortfolioCard(props: Props) {
|
export default function PortfolioCard(props: Props) {
|
||||||
const { data: account } = useAccount(props.accountId)
|
const { data: account } = useAccount('default', props.accountId)
|
||||||
const { health, healthFactor } = useHealthComputer(account)
|
const { health, healthFactor } = useHealthComputer(account)
|
||||||
const { address: urlAddress } = useParams()
|
const { address: urlAddress } = useParams()
|
||||||
const { data: prices } = usePrices()
|
const { data: prices } = usePrices()
|
||||||
|
@ -20,7 +20,7 @@ export default function PortfolioSummary() {
|
|||||||
const { data } = useBorrowMarketAssetsTableData(false)
|
const { data } = useBorrowMarketAssetsTableData(false)
|
||||||
const borrowAssets = useMemo(() => data?.allAssets || [], [data])
|
const borrowAssets = useMemo(() => data?.allAssets || [], [data])
|
||||||
const { allAssets: lendingAssets } = useLendingMarketAssetsTableData()
|
const { allAssets: lendingAssets } = useLendingMarketAssetsTableData()
|
||||||
const { data: accounts } = useAccounts(urlAddress || walletAddress)
|
const { data: accounts } = useAccounts('default', urlAddress || walletAddress)
|
||||||
|
|
||||||
const stats = useMemo(() => {
|
const stats = useMemo(() => {
|
||||||
if (!accounts?.length) return
|
if (!accounts?.length) return
|
||||||
@ -38,6 +38,7 @@ export default function PortfolioSummary() {
|
|||||||
lends: [],
|
lends: [],
|
||||||
debts: [],
|
debts: [],
|
||||||
vaults: [],
|
vaults: [],
|
||||||
|
kind: 'default',
|
||||||
} as Account,
|
} as Account,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -9,16 +9,14 @@ export default function Radio(props: Props) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'group flex h-5 w-5 items-center justify-center rounded-full border',
|
'flex h-5 w-5 items-center justify-center rounded-full border',
|
||||||
props.active && 'border-primary',
|
props.active && 'border-primary',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'h-3 w-3 rounded-full',
|
'h-3 w-3 rounded-full',
|
||||||
props.active
|
props.active ? 'bg-primary' : 'bg-white opacity-0 transition-opacity',
|
||||||
? 'bg-primary'
|
|
||||||
: 'bg-white opacity-0 transition-opacity group-hover:opacity-100',
|
|
||||||
props.className,
|
props.className,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
|
|
||||||
import getAccount from 'api/accounts/getAccount'
|
import getAccount from 'api/accounts/getAccount'
|
||||||
|
import { AccountKind } from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types'
|
||||||
|
|
||||||
export default function useAccount(accountId?: string, suspense?: boolean) {
|
export default function useAccount(kind: AccountKind, accountId?: string, suspense?: boolean) {
|
||||||
return useSWR(`account${accountId}`, () => getAccount(accountId || ''), {
|
return useSWR(`account${accountId}`, () => getAccount({ id: accountId || '', kind }), {
|
||||||
suspense: suspense,
|
suspense: suspense,
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
})
|
})
|
||||||
|
@ -2,10 +2,14 @@ import useSWR from 'swr'
|
|||||||
|
|
||||||
import getAccountIds from 'api/wallets/getAccountIds'
|
import getAccountIds from 'api/wallets/getAccountIds'
|
||||||
|
|
||||||
export default function useAccountIds(address?: string, suspense = true) {
|
export default function useAccountIdsAndKinds(address?: string, suspense = true) {
|
||||||
return useSWR(`wallets/${address}/account-ids`, () => getAccountIds(address), {
|
return useSWR(
|
||||||
suspense: suspense,
|
`wallets/${address}/account-ids`,
|
||||||
fallback: [] as string[],
|
() => getAccountIds(address).then((accountIdsAndKinds) => accountIdsAndKinds.map((a) => a.id)),
|
||||||
revalidateOnFocus: false,
|
{
|
||||||
})
|
suspense: suspense,
|
||||||
|
fallback: [] as string[],
|
||||||
|
revalidateOnFocus: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,18 @@ import useSWR from 'swr'
|
|||||||
|
|
||||||
import getAccounts from 'api/wallets/getAccounts'
|
import getAccounts from 'api/wallets/getAccounts'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
|
import { AccountKind } from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types'
|
||||||
|
|
||||||
// TODO: Remove this hook
|
export default function useAccounts(kind: AccountKind, address?: string, suspense = true) {
|
||||||
export default function useAccounts(address?: string) {
|
return useSWR(`accounts/${kind}`, () => getAccounts(kind, address), {
|
||||||
return useSWR(`accounts${address}`, () => getAccounts(address), {
|
suspense: suspense,
|
||||||
suspense: true,
|
|
||||||
fallbackData: [],
|
fallbackData: [],
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
onSuccess: (accounts) => {
|
onSuccess: (accounts) => {
|
||||||
|
if (kind === 'high_levered_strategy') {
|
||||||
|
useStore.setState({ hlsAccounts: accounts })
|
||||||
|
return
|
||||||
|
}
|
||||||
useStore.setState({ accounts: accounts })
|
useStore.setState({ accounts: accounts })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
10
src/hooks/useBorrowAsset.ts
Normal file
10
src/hooks/useBorrowAsset.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import useMarketBorrowings from 'hooks/useMarketBorrowings'
|
||||||
|
import { byDenom } from 'utils/array'
|
||||||
|
|
||||||
|
export default function useBorrowAsset(denom: string) {
|
||||||
|
const { data: borrowAssets } = useMarketBorrowings()
|
||||||
|
|
||||||
|
if (!borrowAssets.length) return null
|
||||||
|
|
||||||
|
return borrowAssets.find(byDenom(denom))
|
||||||
|
}
|
@ -7,9 +7,7 @@ export default function useCurrentWallet() {
|
|||||||
const { wallets } = useShuttle()
|
const { wallets } = useShuttle()
|
||||||
const chainId = ENV.CHAIN_ID
|
const chainId = ENV.CHAIN_ID
|
||||||
|
|
||||||
const currentWallet = useMemo(() => {
|
return useMemo(() => {
|
||||||
return wallets.find((wallet) => wallet.network.chainId === chainId)
|
return wallets.find((wallet) => wallet.network.chainId === chainId)
|
||||||
}, [wallets, chainId])
|
}, [wallets, chainId])
|
||||||
|
|
||||||
return currentWallet
|
|
||||||
}
|
}
|
||||||
|
36
src/hooks/useDepositHlsVault.ts
Normal file
36
src/hooks/useDepositHlsVault.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { useMemo, useState } from 'react'
|
||||||
|
|
||||||
|
import { BN_ZERO } from 'constants/math'
|
||||||
|
import usePrices from 'hooks/usePrices'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
import { getValueFromBNCoins } from 'utils/helpers'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
vault: Vault
|
||||||
|
}
|
||||||
|
export default function useDepositHlsVault(props: Props) {
|
||||||
|
const { data: prices } = usePrices()
|
||||||
|
|
||||||
|
const [depositAmount, setDepositAmount] = useState<BigNumber>(BN_ZERO)
|
||||||
|
const [borrowAmount, setBorrowAmount] = useState<BigNumber>(BN_ZERO)
|
||||||
|
|
||||||
|
const positionValue = useMemo(() => {
|
||||||
|
if (!prices.length) return BN_ZERO
|
||||||
|
|
||||||
|
return getValueFromBNCoins(
|
||||||
|
[
|
||||||
|
BNCoin.fromDenomAndBigNumber(props.vault.denoms.primary, depositAmount),
|
||||||
|
BNCoin.fromDenomAndBigNumber(props.vault.denoms.secondary, borrowAmount),
|
||||||
|
],
|
||||||
|
prices,
|
||||||
|
)
|
||||||
|
}, [prices, depositAmount, borrowAmount])
|
||||||
|
|
||||||
|
return {
|
||||||
|
setDepositAmount,
|
||||||
|
depositAmount,
|
||||||
|
setBorrowAmount,
|
||||||
|
borrowAmount,
|
||||||
|
positionValue,
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ import {
|
|||||||
} from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
} from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||||
import { VaultConfigBaseForString } from 'types/generated/mars-params/MarsParams.types'
|
import { VaultConfigBaseForString } from 'types/generated/mars-params/MarsParams.types'
|
||||||
import {
|
import {
|
||||||
|
AccountKind,
|
||||||
AssetParamsBaseForAddr,
|
AssetParamsBaseForAddr,
|
||||||
HealthComputer,
|
HealthComputer,
|
||||||
} from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types'
|
} from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types'
|
||||||
@ -24,7 +25,7 @@ import {
|
|||||||
max_withdraw_estimate_js,
|
max_withdraw_estimate_js,
|
||||||
SwapKind,
|
SwapKind,
|
||||||
} from 'utils/health_computer'
|
} from 'utils/health_computer'
|
||||||
import { BN } from 'utils/helpers'
|
import { BN } from 'utils/helpers' // Pyth returns prices with up to 32 decimals. Javascript only supports 18 decimals. So we need to scale by 14 t
|
||||||
|
|
||||||
// Pyth returns prices with up to 32 decimals. Javascript only supports 18 decimals. So we need to scale by 14 t
|
// Pyth returns prices with up to 32 decimals. Javascript only supports 18 decimals. So we need to scale by 14 t
|
||||||
// avoid "too many decimals" errors.
|
// avoid "too many decimals" errors.
|
||||||
@ -129,7 +130,7 @@ export default function useHealthComputer(account?: Account) {
|
|||||||
vault_values: vaultPositionValues,
|
vault_values: vaultPositionValues,
|
||||||
},
|
},
|
||||||
positions: positions,
|
positions: positions,
|
||||||
kind: 'default',
|
kind: 'default' as AccountKind,
|
||||||
}
|
}
|
||||||
}, [priceData, denomsData, vaultConfigsData, vaultPositionValues, positions])
|
}, [priceData, denomsData, vaultConfigsData, vaultPositionValues, positions])
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
Action as CreditManagerAction,
|
Action as CreditManagerAction,
|
||||||
ExecuteMsg as CreditManagerExecuteMsg,
|
ExecuteMsg as CreditManagerExecuteMsg,
|
||||||
} from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
} from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||||
|
import { AccountKind } from 'types/generated/mars-rover-health-types/MarsRoverHealthTypes.types'
|
||||||
import { getAssetByDenom, getAssetBySymbol } from 'utils/assets'
|
import { getAssetByDenom, getAssetBySymbol } from 'utils/assets'
|
||||||
import { generateErrorMessage, getSingleValueFromBroadcastResult } from 'utils/broadcast'
|
import { generateErrorMessage, getSingleValueFromBroadcastResult } from 'utils/broadcast'
|
||||||
import checkAutoLendEnabled from 'utils/checkAutoLendEnabled'
|
import checkAutoLendEnabled from 'utils/checkAutoLendEnabled'
|
||||||
@ -193,9 +194,9 @@ export default function createBroadcastSlice(
|
|||||||
|
|
||||||
return response.then((response) => !!response.result)
|
return response.then((response) => !!response.result)
|
||||||
},
|
},
|
||||||
createAccount: async () => {
|
createAccount: async (accountKind: AccountKind) => {
|
||||||
const msg: CreditManagerExecuteMsg = {
|
const msg: CreditManagerExecuteMsg = {
|
||||||
create_credit_account: 'default',
|
create_credit_account: accountKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = get().executeMsg({
|
const response = get().executeMsg({
|
||||||
|
@ -5,6 +5,7 @@ export default function createCommonSlice(set: SetState<CommonSlice>, get: GetSt
|
|||||||
accounts: null,
|
accounts: null,
|
||||||
balances: [],
|
balances: [],
|
||||||
creditAccounts: null,
|
creditAccounts: null,
|
||||||
|
hlsAccounts: null,
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
selectedAccount: null,
|
selectedAccount: null,
|
||||||
focusComponent: null,
|
focusComponent: null,
|
||||||
|
@ -6,6 +6,7 @@ export default function createModalSlice(set: SetState<ModalSlice>, get: GetStat
|
|||||||
addVaultBorrowingsModal: null,
|
addVaultBorrowingsModal: null,
|
||||||
alertDialog: null,
|
alertDialog: null,
|
||||||
assetOverlayState: 'closed' as OverlayState,
|
assetOverlayState: 'closed' as OverlayState,
|
||||||
|
hlsModal: null,
|
||||||
borrowModal: null,
|
borrowModal: null,
|
||||||
fundAndWithdrawModal: null,
|
fundAndWithdrawModal: null,
|
||||||
getStartedModal: false,
|
getStartedModal: false,
|
||||||
|
@ -215,6 +215,7 @@ export type CallbackMsg =
|
|||||||
| {
|
| {
|
||||||
claim_rewards: {
|
claim_rewards: {
|
||||||
account_id: string
|
account_id: string
|
||||||
|
recipient: Addr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
@ -321,6 +322,13 @@ export type CallbackMsg =
|
|||||||
| {
|
| {
|
||||||
remove_reentrancy_guard: {}
|
remove_reentrancy_guard: {}
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
send_rewards_to_addr: {
|
||||||
|
account_id: string
|
||||||
|
previous_balances: Coin[]
|
||||||
|
recipient: Addr
|
||||||
|
}
|
||||||
|
}
|
||||||
export type Addr = string
|
export type Addr = string
|
||||||
export type HealthState =
|
export type HealthState =
|
||||||
| 'healthy'
|
| 'healthy'
|
||||||
|
6
src/types/interfaces/account.d.ts
vendored
6
src/types/interfaces/account.d.ts
vendored
@ -4,6 +4,7 @@ interface Account extends AccountChange {
|
|||||||
debts: BNCoin[]
|
debts: BNCoin[]
|
||||||
lends: BNCoin[]
|
lends: BNCoin[]
|
||||||
vaults: DepositedVault[]
|
vaults: DepositedVault[]
|
||||||
|
kind: AccountKind
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AccountChange {
|
interface AccountChange {
|
||||||
@ -23,3 +24,8 @@ interface AccountBalanceRow {
|
|||||||
value: string
|
value: string
|
||||||
amountChange: BigNumber
|
amountChange: BigNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AccountIdAndKind {
|
||||||
|
id: string
|
||||||
|
kind: AccountKind
|
||||||
|
}
|
||||||
|
2
src/types/interfaces/components/docLink.d.ts
vendored
2
src/types/interfaces/components/docLink.d.ts
vendored
@ -1 +1 @@
|
|||||||
type DocLinkType = 'wallet' | 'account' | 'terms' | 'fund'
|
type DocLinkType = 'wallet' | 'account' | 'terms' | 'fund' | 'hls'
|
||||||
|
4
src/types/interfaces/store/broadcast.d.ts
vendored
4
src/types/interfaces/store/broadcast.d.ts
vendored
@ -84,7 +84,9 @@ interface BroadcastSlice {
|
|||||||
borrowToWallet: boolean
|
borrowToWallet: boolean
|
||||||
}) => Promise<boolean>
|
}) => Promise<boolean>
|
||||||
claimRewards: (options: { accountId: string }) => ExecutableTx
|
claimRewards: (options: { accountId: string }) => ExecutableTx
|
||||||
createAccount: () => Promise<string | null>
|
createAccount: (
|
||||||
|
accountKind: import('types/generated/mars-rover-health-types/MarsRoverHealthTypes.types').AccountKind,
|
||||||
|
) => Promise<string | null>
|
||||||
deleteAccount: (options: { accountId: string; lends: BNCoin[] }) => Promise<boolean>
|
deleteAccount: (options: { accountId: string; lends: BNCoin[] }) => Promise<boolean>
|
||||||
deposit: (options: { accountId: string; coins: BNCoin[]; lend: boolean }) => Promise<boolean>
|
deposit: (options: { accountId: string; coins: BNCoin[]; lend: boolean }) => Promise<boolean>
|
||||||
depositIntoVault: (options: {
|
depositIntoVault: (options: {
|
||||||
|
1
src/types/interfaces/store/common.d.ts
vendored
1
src/types/interfaces/store/common.d.ts
vendored
@ -2,6 +2,7 @@ interface CommonSlice {
|
|||||||
accounts: Account[] | null
|
accounts: Account[] | null
|
||||||
address?: string
|
address?: string
|
||||||
balances: Coin[]
|
balances: Coin[]
|
||||||
|
hlsAccounts: Account[] | null
|
||||||
client?: WalletClient
|
client?: WalletClient
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
selectedAccount: string | null
|
selectedAccount: string | null
|
||||||
|
5
src/types/interfaces/store/modals.d.ts
vendored
5
src/types/interfaces/store/modals.d.ts
vendored
@ -3,6 +3,7 @@ interface ModalSlice {
|
|||||||
addVaultBorrowingsModal: AddVaultBorrowingsModal | null
|
addVaultBorrowingsModal: AddVaultBorrowingsModal | null
|
||||||
alertDialog: AlertDialogConfig | null
|
alertDialog: AlertDialogConfig | null
|
||||||
assetOverlayState: OverlayState
|
assetOverlayState: OverlayState
|
||||||
|
hlsModal: HlsModal | null
|
||||||
borrowModal: BorrowModal | null
|
borrowModal: BorrowModal | null
|
||||||
fundAndWithdrawModal: 'fund' | 'withdraw' | null
|
fundAndWithdrawModal: 'fund' | 'withdraw' | null
|
||||||
getStartedModal: boolean
|
getStartedModal: boolean
|
||||||
@ -67,3 +68,7 @@ interface WalletAssetModal {
|
|||||||
selectedDenoms: string[]
|
selectedDenoms: string[]
|
||||||
isBorrow?: boolean
|
isBorrow?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface HlsModal {
|
||||||
|
vault: Vault
|
||||||
|
}
|
||||||
|
@ -171,6 +171,7 @@ export function convertAccountToPositions(account: Account): Positions {
|
|||||||
export function cloneAccount(account: Account): Account {
|
export function cloneAccount(account: Account): Account {
|
||||||
return {
|
return {
|
||||||
id: account.id,
|
id: account.id,
|
||||||
|
kind: account.kind,
|
||||||
debts: account.debts.map(
|
debts: account.debts.map(
|
||||||
(debt) =>
|
(debt) =>
|
||||||
new BNCoin({
|
new BNCoin({
|
||||||
|
@ -205,6 +205,7 @@ module.exports = {
|
|||||||
maxWidth: {
|
maxWidth: {
|
||||||
content: '1024px',
|
content: '1024px',
|
||||||
modal: '895px',
|
modal: '895px',
|
||||||
|
'modal-md': '556px',
|
||||||
'modal-sm': '526px',
|
'modal-sm': '526px',
|
||||||
'modal-xs': '442px',
|
'modal-xs': '442px',
|
||||||
},
|
},
|
||||||
@ -327,6 +328,9 @@ module.exports = {
|
|||||||
background:
|
background:
|
||||||
'linear-gradient(90deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 50%)',
|
'linear-gradient(90deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 50%)',
|
||||||
},
|
},
|
||||||
|
'.gradient-hls': {
|
||||||
|
background: 'linear-gradient(180deg, #FE625D 0%, #FB9562 50%)',
|
||||||
|
},
|
||||||
'.gradient-limit': {
|
'.gradient-limit': {
|
||||||
background:
|
background:
|
||||||
'linear-gradient(to right,#15bfa9 20.9%,#5e4bb1 49.68%,#382685 82.55%,#c83333 100%)',
|
'linear-gradient(to right,#15bfa9 20.9%,#5e4bb1 49.68%,#382685 82.55%,#c83333 100%)',
|
||||||
|
Loading…
Reference in New Issue
Block a user