Hls staking manage actions (#622)
* ✨Add basic modal for HLS staking * ✨UI components for Manage * ✨All Manage actions (except change lev) * 🐛hls intro icons + checkbox, hide repay when no debt, clickable dropdown * fix build
This commit is contained in:
parent
4ec95c885c
commit
7439bea0d8
@ -13,12 +13,10 @@ export default async function getHLSStakingAccounts(
|
||||
const hlsAccountsWithStrategy: HLSAccountWithStrategy[] = []
|
||||
|
||||
activeAccounts.forEach((account) => {
|
||||
if (account.deposits.length === 0 || account.debts.length === 0) return
|
||||
if (account.deposits.length === 0) return
|
||||
|
||||
const strategy = hlsStrategies.find(
|
||||
(strategy) =>
|
||||
strategy.denoms.deposit === account.deposits.at(0).denom &&
|
||||
strategy.denoms.borrow === account.debts.at(0).denom,
|
||||
(strategy) => strategy.denoms.deposit === account.deposits.at(0).denom,
|
||||
)
|
||||
|
||||
if (!strategy) return
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import React, { ReactElement, useMemo } from 'react'
|
||||
|
||||
import { CircularProgress } from 'components/CircularProgress'
|
||||
import Text from 'components/Text'
|
||||
@ -8,7 +8,7 @@ import { BN } from 'utils/helpers'
|
||||
interface Props {
|
||||
health: number
|
||||
healthFactor: number
|
||||
children: React.ReactNode
|
||||
children: ReactElement
|
||||
}
|
||||
|
||||
function HealthTooltipContent({ health, healthFactor }: { health: number; healthFactor: number }) {
|
||||
|
@ -2,6 +2,7 @@ import Button from 'components/Button'
|
||||
import { ChevronDown } from 'components/Icons'
|
||||
import Text from 'components/Text'
|
||||
import { Tooltip } from 'components/Tooltip'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
|
||||
interface Props extends ButtonProps {
|
||||
items: DropDownItem[]
|
||||
@ -9,6 +10,7 @@ interface Props extends ButtonProps {
|
||||
}
|
||||
|
||||
export default function DropDownButton(props: Props) {
|
||||
const [isOpen, toggleIsOpen] = useToggle(false)
|
||||
return (
|
||||
<Tooltip
|
||||
content={<DropDown {...props} />}
|
||||
@ -17,8 +19,15 @@ export default function DropDownButton(props: Props) {
|
||||
contentClassName='!bg-white/10 border border-white/20 backdrop-blur-xl !p-0'
|
||||
interactive
|
||||
hideArrow
|
||||
visible={isOpen}
|
||||
onClickOutside={() => toggleIsOpen(false)}
|
||||
>
|
||||
<Button rightIcon={<ChevronDown />} iconClassName='w-3 h-3' {...props} />
|
||||
<Button
|
||||
onClick={() => toggleIsOpen()}
|
||||
rightIcon={<ChevronDown />}
|
||||
iconClassName='w-3 h-3'
|
||||
{...props}
|
||||
/>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
@ -13,13 +13,9 @@ interface Props {
|
||||
export default function Checkbox(props: Props) {
|
||||
return (
|
||||
<>
|
||||
<label
|
||||
className='flex items-center gap-2 border-white cursor-pointer'
|
||||
htmlFor={`${props.name}-id}`}
|
||||
>
|
||||
<label className='flex items-center gap-2 border-white cursor-pointer'>
|
||||
<input
|
||||
onChange={() => props.onChange(props.checked)}
|
||||
id={`${props.name}-id`}
|
||||
name={props.name}
|
||||
checked={props.checked}
|
||||
type='checkbox'
|
||||
|
@ -2,7 +2,7 @@ import classNames from 'classnames'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import ActionButton from 'components/Button/ActionButton'
|
||||
import { Enter } from 'components/Icons'
|
||||
import { Circle, Enter, TrashBin, Wallet } from 'components/Icons'
|
||||
import Loading from 'components/Loading'
|
||||
import Text from 'components/Text'
|
||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||
@ -91,18 +91,18 @@ export default function Deposit(props: Props) {
|
||||
|
||||
const INFO_ITEMS = [
|
||||
{
|
||||
icon: <Enter width={16} height={16} />,
|
||||
icon: <Circle />,
|
||||
title: 'One account, one position',
|
||||
description:
|
||||
'A minted HLS account can only have a single position tied to it, in order to limit risk.',
|
||||
},
|
||||
{
|
||||
icon: <Enter />,
|
||||
icon: <Wallet />,
|
||||
title: 'Funded from your wallet',
|
||||
description: 'To fund your HLS position, funds will have to come directly from your wallet.',
|
||||
},
|
||||
{
|
||||
icon: <Enter />,
|
||||
icon: <TrashBin />,
|
||||
title: 'Accounts are reusable',
|
||||
description:
|
||||
'If you exited a position from a minted account, this account can be reused for a new position.',
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
|
||||
import DropDownButton from 'components/Button/DropDownButton'
|
||||
import { ArrowDownLine, HandCoins, Plus } from 'components/Icons'
|
||||
import { ArrowDownLine, Cross, HandCoins, Plus, Scale } from 'components/Icons'
|
||||
import useCloseHlsStakingPosition from 'hooks/HLS/useClosePositionActions'
|
||||
import useStore from 'store'
|
||||
|
||||
export const MANAGE_META = { id: 'manage' }
|
||||
@ -12,32 +13,55 @@ interface Props {
|
||||
|
||||
export default function Manage(props: Props) {
|
||||
const openModal = useCallback(
|
||||
(action: 'deposit' | 'withdraw' | 'repay') =>
|
||||
(action: HlsStakingManageAction) =>
|
||||
useStore.setState({
|
||||
hlsManageModal: { staking: { strategy: props.account.strategy, action } },
|
||||
hlsManageModal: {
|
||||
accountId: props.account.id,
|
||||
staking: { strategy: props.account.strategy, action },
|
||||
},
|
||||
}),
|
||||
[props.account.strategy],
|
||||
[props.account.id, props.account.strategy],
|
||||
)
|
||||
|
||||
const actions = useCloseHlsStakingPosition({ account: props.account })
|
||||
|
||||
const closeHlsStakingPosition = useStore((s) => s.closeHlsStakingPosition)
|
||||
|
||||
const hasNoDebt = useMemo(() => props.account.debts.length === 0, [props.account.debts.length])
|
||||
|
||||
const ITEMS: DropDownItem[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
icon: <Scale width={16} />,
|
||||
text: 'Change leverage',
|
||||
onClick: () => openModal('leverage'),
|
||||
},
|
||||
{
|
||||
icon: <Plus width={16} />,
|
||||
text: 'Deposit more',
|
||||
onClick: () => openModal('deposit'),
|
||||
},
|
||||
...(hasNoDebt
|
||||
? []
|
||||
: [
|
||||
{
|
||||
icon: <HandCoins width={16} />,
|
||||
text: 'Repay',
|
||||
onClick: () => openModal('repay'),
|
||||
},
|
||||
]),
|
||||
{
|
||||
icon: <HandCoins />,
|
||||
text: 'Repay',
|
||||
onClick: () => openModal('repay'),
|
||||
},
|
||||
{
|
||||
icon: <ArrowDownLine />,
|
||||
icon: <ArrowDownLine width={16} />,
|
||||
text: 'Withdraw',
|
||||
onClick: () => openModal('withdraw'),
|
||||
},
|
||||
{
|
||||
icon: <Cross width={14} />,
|
||||
text: 'Close Position',
|
||||
onClick: () => closeHlsStakingPosition({ accountId: props.account.id, actions }),
|
||||
},
|
||||
],
|
||||
[openModal],
|
||||
[actions, closeHlsStakingPosition, openModal, props.account.id],
|
||||
)
|
||||
|
||||
return <DropDownButton items={ITEMS} text='Manage' color='tertiary' />
|
||||
|
10
src/components/Icons/Circle.svg
Normal file
10
src/components/Icons/Circle.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_5007_284)">
|
||||
<path d="M8.00016 14.6654C11.6821 14.6654 14.6668 11.6806 14.6668 7.9987C14.6668 4.3168 11.6821 1.33203 8.00016 1.33203C4.31826 1.33203 1.3335 4.3168 1.3335 7.9987C1.3335 11.6806 4.31826 14.6654 8.00016 14.6654Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_5007_284">
|
||||
<rect width="16" height="16" fill="currentColor"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 567 B |
5
src/components/Icons/Scale.svg
Normal file
5
src/components/Icons/Scale.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="scales-02">
|
||||
<path id="Icon" d="M2.50047 13H8.50047M15.5005 13H21.5005M12.0005 7V21M12.0005 7C13.3812 7 14.5005 5.88071 14.5005 4.5M12.0005 7C10.6198 7 9.50047 5.88071 9.50047 4.5M4.00047 21L20.0005 21M4.00047 4.50001L9.50047 4.5M9.50047 4.5C9.50047 3.11929 10.6198 2 12.0005 2C13.3812 2 14.5005 3.11929 14.5005 4.5M14.5005 4.5L20.0005 4.5M8.88091 14.3364C8.48022 15.8706 7.11858 17 5.50047 17C3.88237 17 2.52073 15.8706 2.12004 14.3364C2.0873 14.211 2.07093 14.1483 2.06935 13.8979C2.06838 13.7443 2.12544 13.3904 2.17459 13.2449C2.25478 13.0076 2.34158 12.8737 2.51519 12.6059L5.50047 8L8.48576 12.6059C8.65937 12.8737 8.74617 13.0076 8.82636 13.2449C8.87551 13.3904 8.93257 13.7443 8.9316 13.8979C8.93002 14.1483 8.91365 14.211 8.88091 14.3364ZM21.8809 14.3364C21.4802 15.8706 20.1186 17 18.5005 17C16.8824 17 15.5207 15.8706 15.12 14.3364C15.0873 14.211 15.0709 14.1483 15.0693 13.8979C15.0684 13.7443 15.1254 13.3904 15.1746 13.2449C15.2548 13.0076 15.3416 12.8737 15.5152 12.6059L18.5005 8L21.4858 12.6059C21.6594 12.8737 21.7462 13.0076 21.8264 13.2449C21.8755 13.3904 21.9326 13.7443 21.9316 13.8979C21.93 14.1483 21.9137 14.211 21.8809 14.3364Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@ -14,6 +14,7 @@ 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 Compass } from 'components/Icons/Compass.svg'
|
||||
export { default as Copy } from 'components/Icons/Copy.svg'
|
||||
export { default as Cross } from 'components/Icons/Cross.svg'
|
||||
@ -43,6 +44,7 @@ export { default as PlusCircled } from 'components/Icons/PlusCircled.svg'
|
||||
export { default as PlusSquared } from 'components/Icons/PlusSquared.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'
|
||||
|
@ -4,7 +4,7 @@ import { ReactNode, useEffect, useRef } from 'react'
|
||||
import EscButton from 'components/Button/EscButton'
|
||||
import Card from 'components/Card'
|
||||
|
||||
interface Props {
|
||||
export interface ModalProps {
|
||||
header: string | ReactNode
|
||||
headerClassName?: string
|
||||
hideCloseBtn?: boolean
|
||||
@ -18,7 +18,7 @@ interface Props {
|
||||
dialogId?: string
|
||||
}
|
||||
|
||||
export default function Modal(props: Props) {
|
||||
export default function Modal(props: ModalProps) {
|
||||
const ref: React.RefObject<HTMLDialogElement> = useRef(null)
|
||||
const modalClassName = props.modalClassName ?? 'max-w-modal'
|
||||
|
||||
|
@ -75,7 +75,7 @@ function AlertDialog(props: Props) {
|
||||
)}
|
||||
{checkbox && (
|
||||
<Checkbox
|
||||
name='aleart-toggle'
|
||||
name='hls-info-toggle'
|
||||
checked={toggle}
|
||||
onChange={handleCheckboxClick}
|
||||
text={checkbox.text}
|
||||
|
@ -2,7 +2,7 @@ import React from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
import LeverageSummary from 'components/Modals/HLS/LeverageSummary'
|
||||
import LeverageSummary from 'components/Modals/HLS/Deposit/LeverageSummary'
|
||||
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
|
||||
|
||||
interface Props {
|
@ -1,7 +1,6 @@
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import Text from 'components/Text'
|
||||
import SummaryItems from 'components/SummaryItems'
|
||||
import useBorrowAsset from 'hooks/useBorrowAsset'
|
||||
|
||||
interface Props {
|
||||
@ -12,7 +11,7 @@ interface Props {
|
||||
export default function LeverageSummary(props: Props) {
|
||||
const borrowAsset = useBorrowAsset(props.asset.denom)
|
||||
|
||||
const items: { title: string; amount: number; options: FormatOptions }[] = useMemo(() => {
|
||||
const items: SummaryItem[] = useMemo(() => {
|
||||
return [
|
||||
// TODO: Get APY numbers
|
||||
{
|
||||
@ -33,18 +32,5 @@ export default function LeverageSummary(props: Props) {
|
||||
]
|
||||
}, [borrowAsset?.borrowRate, props.asset.symbol, props.positionValue])
|
||||
|
||||
return (
|
||||
<div className='grid grid-cols-2 gap-2'>
|
||||
{items.map((item) => (
|
||||
<React.Fragment key={item.title}>
|
||||
<Text className='text-white/60 text-xs'>{item.title}</Text>
|
||||
<FormattedNumber
|
||||
className='place-self-end text-xs'
|
||||
amount={item.amount}
|
||||
options={item.options}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
return <SummaryItems items={items} />
|
||||
}
|
@ -3,7 +3,7 @@ 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 Container from 'components/Modals/HLS/Deposit/Summary/Container'
|
||||
import Text from 'components/Text'
|
||||
|
||||
interface Props {
|
@ -3,8 +3,8 @@ 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 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 { BNCoin } from 'types/classes/BNCoin'
|
||||
@ -63,8 +63,10 @@ export default function YourPosition(props: Props) {
|
||||
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' />
|
||||
<>
|
||||
<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
|
@ -2,8 +2,8 @@ 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 AssetSummary from 'components/Modals/HLS/Deposit/Summary/AssetSummary'
|
||||
import YourPosition from 'components/Modals/HLS/Deposit/Summary/YourPosition'
|
||||
import useBorrowAsset from 'hooks/useBorrowAsset'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React, { useMemo, useState } from 'react'
|
||||
|
||||
import Accordion from 'components/Accordion'
|
||||
import useStakingController from 'components/Modals/HLS/Content//useStakingController'
|
||||
import useVaultController from 'components/Modals/HLS/Content//useVaultController'
|
||||
import useAccordionItems from 'components/Modals/HLS/Content/useAccordionItems'
|
||||
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 useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
|
@ -1,11 +1,15 @@
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
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 CreateAccount from 'components/Modals/HLS/Deposit/CreateAccount'
|
||||
import Leverage from 'components/Modals/HLS/Deposit/Leverage'
|
||||
import ProvideCollateral from 'components/Modals/HLS/Deposit/ProvideCollateral'
|
||||
import SelectAccount from 'components/Modals/HLS/Deposit/SelectAccount'
|
||||
import {
|
||||
CollateralSubTitle,
|
||||
LeverageSubTitle,
|
||||
SubTitle,
|
||||
} from 'components/Modals/HLS/Deposit/SubTitles'
|
||||
import Summary from 'components/Modals/HLS/Deposit/Summary'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
interface Props {
|
@ -1,14 +1,10 @@
|
||||
import { useCallback, useMemo } from 'react'
|
||||
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||
import useDepositHlsVault from 'hooks/useDepositHlsVault'
|
||||
import useHealthComputer from 'hooks/useHealthComputer'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
|
||||
import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||
|
||||
interface Props {
|
||||
borrowAsset: Asset
|
||||
@ -16,9 +12,8 @@ interface Props {
|
||||
selectedAccount: Account
|
||||
}
|
||||
|
||||
export default function useVaultController(props: Props) {
|
||||
export default function useStakingController(props: Props) {
|
||||
const { collateralAsset, borrowAsset, selectedAccount } = props
|
||||
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
|
||||
const addToStakingStrategy = useStore((s) => s.addToStakingStrategy)
|
||||
|
||||
const {
|
||||
@ -28,40 +23,14 @@ export default function useVaultController(props: Props) {
|
||||
setBorrowAmount,
|
||||
borrowAmount,
|
||||
positionValue,
|
||||
borrowCoin,
|
||||
depositCoin,
|
||||
actions,
|
||||
} = useDepositHlsVault({
|
||||
collateralDenom: collateralAsset.denom,
|
||||
borrowDenom: borrowAsset.denom,
|
||||
})
|
||||
|
||||
const depositCoin = useMemo(
|
||||
() => BNCoin.fromDenomAndBigNumber(collateralAsset.denom, depositAmount),
|
||||
[collateralAsset.denom, depositAmount],
|
||||
)
|
||||
|
||||
const borrowCoin = useMemo(
|
||||
() => BNCoin.fromDenomAndBigNumber(borrowAsset.denom, borrowAmount),
|
||||
[borrowAsset.denom, borrowAmount],
|
||||
)
|
||||
|
||||
const actions: Action[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
deposit: depositCoin.toCoin(),
|
||||
},
|
||||
{
|
||||
borrow: borrowCoin.toCoin(),
|
||||
},
|
||||
{
|
||||
swap_exact_in: {
|
||||
denom_out: collateralAsset.denom,
|
||||
slippage: slippage.toString(),
|
||||
coin_in: BNCoin.fromDenomAndBigNumber(borrowAsset.denom, borrowAmount).toActionCoin(),
|
||||
},
|
||||
},
|
||||
],
|
||||
[borrowAmount, borrowAsset.denom, borrowCoin, collateralAsset.denom, depositCoin, slippage],
|
||||
)
|
||||
|
||||
const { updatedAccount, addDeposits } = useUpdatedAccount(selectedAccount)
|
||||
const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount)
|
||||
|
@ -7,16 +7,18 @@ import Text from 'components/Text'
|
||||
interface Props {
|
||||
primaryAsset: Asset
|
||||
secondaryAsset: Asset
|
||||
action?: HlsStakingManageAction
|
||||
}
|
||||
|
||||
export default function Header(props: Props) {
|
||||
return (
|
||||
<div className='flex items-center gap-2'>
|
||||
<DoubleLogo
|
||||
primaryDenom={props.secondaryAsset.denom}
|
||||
secondaryDenom={props.primaryAsset.denom}
|
||||
primaryDenom={props.primaryAsset.denom}
|
||||
secondaryDenom={props.secondaryAsset.denom}
|
||||
/>
|
||||
<Text>{`${props.secondaryAsset.symbol} - ${props.primaryAsset.symbol}`}</Text>
|
||||
<Text>{`${props.primaryAsset.symbol}/${props.secondaryAsset.symbol}`}</Text>
|
||||
{props.action && <Text className='capitalize'> - {props.action}</Text>}
|
||||
<HLSTag />
|
||||
</div>
|
||||
)
|
||||
|
10
src/components/Modals/HLS/Manage/ChangeLeverage.tsx
Normal file
10
src/components/Modals/HLS/Manage/ChangeLeverage.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
interface Props {
|
||||
account: Account
|
||||
action: HlsStakingManageAction
|
||||
borrowAsset: Asset
|
||||
collateralAsset: Asset
|
||||
}
|
||||
|
||||
export default function Repay(props: Props) {
|
||||
return <></>
|
||||
}
|
204
src/components/Modals/HLS/Manage/Deposit.tsx
Normal file
204
src/components/Modals/HLS/Manage/Deposit.tsx
Normal file
@ -0,0 +1,204 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import React, { useCallback, useEffect, 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 { BN_ZERO } from 'constants/math'
|
||||
import useDepositActions from 'hooks/HLS/useDepositActions'
|
||||
import useBorrowAsset from 'hooks/useBorrowAsset'
|
||||
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
|
||||
import useHealthComputer from 'hooks/useHealthComputer'
|
||||
import usePrices from 'hooks/usePrices'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
|
||||
import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { calculateAccountLeverage } from 'utils/accounts'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { getCoinAmount, getCoinValue } from 'utils/formatters'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
interface Props {
|
||||
account: Account
|
||||
action: HlsStakingManageAction
|
||||
borrowAsset: Asset
|
||||
collateralAsset: Asset
|
||||
}
|
||||
|
||||
export default function Deposit(props: Props) {
|
||||
const { addedDeposits, addedDebts, updatedAccount, simulateHlsStakingDeposit } =
|
||||
useUpdatedAccount(props.account)
|
||||
const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount)
|
||||
const { data: prices } = usePrices()
|
||||
const [keepLeverage, toggleKeepLeverage] = useToggle(true)
|
||||
const collateralAssetAmountInWallet = BN(
|
||||
useCurrentWalletBalance(props.collateralAsset.denom)?.amount || '0',
|
||||
)
|
||||
const addToStakingStrategy = useStore((s) => s.addToStakingStrategy)
|
||||
const borrowRate = useBorrowAsset(props.borrowAsset.denom)?.borrowRate || 0
|
||||
|
||||
const currentLeverage = useMemo(
|
||||
() => calculateAccountLeverage(props.account, prices).toNumber(),
|
||||
[prices, props.account],
|
||||
)
|
||||
|
||||
const depositCoin = useMemo(
|
||||
() =>
|
||||
BNCoin.fromDenomAndBigNumber(
|
||||
props.collateralAsset.denom,
|
||||
addedDeposits.find(byDenom(props.collateralAsset.denom))?.amount || BN_ZERO,
|
||||
),
|
||||
[addedDeposits, props.collateralAsset.denom],
|
||||
)
|
||||
|
||||
const borrowCoin = useMemo(
|
||||
() =>
|
||||
BNCoin.fromDenomAndBigNumber(
|
||||
props.borrowAsset.denom,
|
||||
addedDebts.find(byDenom(props.borrowAsset.denom))?.amount || BN_ZERO,
|
||||
),
|
||||
[addedDebts, props.borrowAsset.denom],
|
||||
)
|
||||
|
||||
const maxBorrowAmount = useMemo(
|
||||
() => computeMaxBorrowAmount(props.collateralAsset.denom, 'deposit'),
|
||||
[computeMaxBorrowAmount, props.collateralAsset.denom],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (borrowCoin.amount.isGreaterThan(maxBorrowAmount)) {
|
||||
simulateHlsStakingDeposit(
|
||||
BNCoin.fromDenomAndBigNumber(props.collateralAsset.denom, depositCoin.amount),
|
||||
BNCoin.fromDenomAndBigNumber(props.borrowAsset.denom, maxBorrowAmount),
|
||||
)
|
||||
}
|
||||
}, [
|
||||
borrowCoin.amount,
|
||||
depositCoin.amount,
|
||||
maxBorrowAmount,
|
||||
props.borrowAsset.denom,
|
||||
props.collateralAsset.denom,
|
||||
simulateHlsStakingDeposit,
|
||||
])
|
||||
|
||||
const actions = useDepositActions({ depositCoin, borrowCoin })
|
||||
|
||||
const currentDebt: BigNumber = useMemo(
|
||||
() => props.account.debts.find(byDenom(props.borrowAsset.denom)).amount || BN_ZERO,
|
||||
[props.account.debts, props.borrowAsset.denom],
|
||||
)
|
||||
|
||||
const handleDeposit = useCallback(() => {
|
||||
useStore.setState({ hlsManageModal: null })
|
||||
addToStakingStrategy({
|
||||
accountId: props.account.id,
|
||||
actions,
|
||||
depositCoin,
|
||||
borrowCoin,
|
||||
})
|
||||
}, [actions, addToStakingStrategy, borrowCoin, depositCoin, props.account.id])
|
||||
|
||||
const handleOnChange = useCallback(
|
||||
(amount: BigNumber) => {
|
||||
let additionalDebt = BN_ZERO
|
||||
|
||||
if (currentLeverage > 1 && keepLeverage) {
|
||||
const depositValue = getCoinValue(
|
||||
BNCoin.fromDenomAndBigNumber(props.collateralAsset.denom, amount),
|
||||
prices,
|
||||
)
|
||||
const borrowValue = BN(currentLeverage - 1).times(depositValue)
|
||||
additionalDebt = getCoinAmount(props.borrowAsset.denom, borrowValue, prices)
|
||||
}
|
||||
|
||||
simulateHlsStakingDeposit(
|
||||
BNCoin.fromDenomAndBigNumber(props.collateralAsset.denom, amount),
|
||||
BNCoin.fromDenomAndBigNumber(props.borrowAsset.denom, additionalDebt),
|
||||
)
|
||||
},
|
||||
[
|
||||
currentLeverage,
|
||||
keepLeverage,
|
||||
prices,
|
||||
props.borrowAsset.denom,
|
||||
props.collateralAsset.denom,
|
||||
simulateHlsStakingDeposit,
|
||||
],
|
||||
)
|
||||
|
||||
const items: SummaryItem[] = useMemo(
|
||||
() => [
|
||||
...(keepLeverage
|
||||
? [
|
||||
{
|
||||
title: 'Borrow rate',
|
||||
amount: borrowRate,
|
||||
options: {
|
||||
suffix: `%`,
|
||||
minDecimals: 2,
|
||||
maxDecimals: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Additional Borrow Amount',
|
||||
amount: borrowCoin.amount.toNumber(),
|
||||
options: {
|
||||
suffix: ` ${props.borrowAsset.symbol}`,
|
||||
abbreviated: true,
|
||||
decimals: props.borrowAsset.decimals,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'New Debt Amount',
|
||||
amount: currentDebt.plus(borrowCoin.amount).toNumber(),
|
||||
options: {
|
||||
suffix: ` ${props.borrowAsset.symbol}`,
|
||||
abbreviated: true,
|
||||
decimals: props.borrowAsset.decimals,
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
[
|
||||
borrowCoin.amount,
|
||||
borrowRate,
|
||||
currentDebt,
|
||||
keepLeverage,
|
||||
props.borrowAsset.decimals,
|
||||
props.borrowAsset.symbol,
|
||||
],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<TokenInputWithSlider
|
||||
amount={depositCoin.amount}
|
||||
asset={props.collateralAsset}
|
||||
max={collateralAssetAmountInWallet}
|
||||
onChange={handleOnChange}
|
||||
maxText='In Wallet'
|
||||
/>
|
||||
<Divider className='my-6' />
|
||||
<div className='flex flex-wrap flex-1 items-center justify-between'>
|
||||
<div>
|
||||
<Text className='w-full mb-1'>Keep leverage</Text>
|
||||
<Text size='xs' className='text-white/50'>
|
||||
Automatically borrow more funds to keep leverage
|
||||
</Text>
|
||||
</div>
|
||||
<Switch name='keep-leverage' checked={keepLeverage} onChange={toggleKeepLeverage} />
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<SummaryItems items={items} />
|
||||
<Button onClick={handleDeposit} text='Deposit' disabled={depositCoin.amount.isZero()} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
98
src/components/Modals/HLS/Manage/Repay.tsx
Normal file
98
src/components/Modals/HLS/Manage/Repay.tsx
Normal file
@ -0,0 +1,98 @@
|
||||
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 { BN_ZERO } from 'constants/math'
|
||||
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
|
||||
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
|
||||
import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
interface Props {
|
||||
account: Account
|
||||
action: HlsStakingManageAction
|
||||
borrowAsset: Asset
|
||||
collateralAsset: Asset
|
||||
}
|
||||
|
||||
export default function Repay(props: Props) {
|
||||
const { removeDebts, removedDebts } = useUpdatedAccount(props.account)
|
||||
const borrowAssetAmountInWallet = BN(
|
||||
useCurrentWalletBalance(props.borrowAsset.denom)?.amount || '0',
|
||||
)
|
||||
const repay = useStore((s) => s.repay)
|
||||
|
||||
const currentDebt: BigNumber = useMemo(
|
||||
() => props.account.debts.find(byDenom(props.borrowAsset.denom)).amount || BN_ZERO,
|
||||
[props.account.debts, props.borrowAsset.denom],
|
||||
)
|
||||
|
||||
const repayAmount: BigNumber = useMemo(
|
||||
() => removedDebts.find(byDenom(props.borrowAsset.denom))?.amount || BN_ZERO,
|
||||
[removedDebts, props.borrowAsset.denom],
|
||||
)
|
||||
|
||||
const maxRepayAmount = useMemo(
|
||||
() => BigNumber.min(borrowAssetAmountInWallet.toNumber(), currentDebt),
|
||||
[borrowAssetAmountInWallet, currentDebt],
|
||||
)
|
||||
|
||||
const items: SummaryItem[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: 'Total Debt Repayable',
|
||||
amount: currentDebt.toNumber(),
|
||||
options: {
|
||||
suffix: ` ${props.borrowAsset.symbol}`,
|
||||
abbreviated: true,
|
||||
decimals: props.borrowAsset.decimals,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'New Debt Amount',
|
||||
amount: currentDebt.minus(repayAmount).toNumber(),
|
||||
options: {
|
||||
suffix: ` ${props.borrowAsset.symbol}`,
|
||||
abbreviated: true,
|
||||
decimals: props.borrowAsset.decimals,
|
||||
},
|
||||
},
|
||||
],
|
||||
[currentDebt, props.borrowAsset.decimals, props.borrowAsset.symbol, repayAmount],
|
||||
)
|
||||
|
||||
const handleRepay = useCallback(() => {
|
||||
useStore.setState({ hlsManageModal: null })
|
||||
repay({
|
||||
accountId: props.account.id,
|
||||
coin: BNCoin.fromDenomAndBigNumber(props.borrowAsset.denom, repayAmount),
|
||||
fromWallet: true,
|
||||
})
|
||||
}, [props.account.id, props.borrowAsset.denom, repay, repayAmount])
|
||||
|
||||
const handleOnChange = useCallback(
|
||||
(amount: BigNumber) =>
|
||||
removeDebts([BNCoin.fromDenomAndBigNumber(props.borrowAsset.denom, amount)]),
|
||||
[props.borrowAsset.denom, removeDebts],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<TokenInputWithSlider
|
||||
amount={removedDebts.find(byDenom(props.borrowAsset.denom))?.amount || BN_ZERO}
|
||||
asset={props.borrowAsset}
|
||||
max={maxRepayAmount}
|
||||
onChange={handleOnChange}
|
||||
maxText='In Wallet'
|
||||
/>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<SummaryItems items={items} />
|
||||
<Button onClick={handleRepay} text='Repay' />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
62
src/components/Modals/HLS/Manage/Withdraw.tsx
Normal file
62
src/components/Modals/HLS/Manage/Withdraw.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useHealthComputer from 'hooks/useHealthComputer'
|
||||
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
|
||||
import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { byDenom } from 'utils/array'
|
||||
|
||||
interface Props {
|
||||
account: Account
|
||||
action: HlsStakingManageAction
|
||||
borrowAsset: Asset
|
||||
collateralAsset: Asset
|
||||
}
|
||||
|
||||
export default function Withdraw(props: Props) {
|
||||
const { removedDeposits, removeDeposits, updatedAccount } = useUpdatedAccount(props.account)
|
||||
const { computeMaxWithdrawAmount } = useHealthComputer(updatedAccount)
|
||||
const withdraw = useStore((s) => s.withdraw)
|
||||
const handleChange = useCallback(
|
||||
(amount: BigNumber) =>
|
||||
removeDeposits([BNCoin.fromDenomAndBigNumber(props.collateralAsset.denom, amount)]),
|
||||
[removeDeposits, props.collateralAsset.denom],
|
||||
)
|
||||
|
||||
const removedDeposit = useMemo(
|
||||
() => removedDeposits.find(byDenom(props.collateralAsset.denom)),
|
||||
[props.collateralAsset.denom, removedDeposits],
|
||||
)
|
||||
|
||||
const maxWithdrawAmount = useMemo(() => {
|
||||
const currentWithdrawAmount = removedDeposit?.amount || BN_ZERO
|
||||
const extraWithdrawAmount = computeMaxWithdrawAmount(props.collateralAsset.denom)
|
||||
return currentWithdrawAmount.plus(extraWithdrawAmount)
|
||||
}, [computeMaxWithdrawAmount, props.collateralAsset.denom, removedDeposit?.amount])
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
useStore.setState({ hlsManageModal: null })
|
||||
withdraw({
|
||||
accountId: props.account.id,
|
||||
coins: [{ coin: removedDeposit }],
|
||||
borrow: [],
|
||||
reclaims: [],
|
||||
})
|
||||
}, [props.account.id, removedDeposit, withdraw])
|
||||
|
||||
return (
|
||||
<>
|
||||
<TokenInputWithSlider
|
||||
amount={removedDeposit?.amount || BN_ZERO}
|
||||
asset={props.collateralAsset}
|
||||
max={maxWithdrawAmount}
|
||||
onChange={handleChange}
|
||||
maxText='Available'
|
||||
/>
|
||||
<Button onClick={onClick} text='Withdraw' disabled={removedDeposit?.amount?.isZero()} />
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,40 +1,74 @@
|
||||
import React from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import Modal from 'components/Modal'
|
||||
import Header from 'components/Modals/HLS/Header'
|
||||
import ChangeLeverage from 'components/Modals/HLS/Manage/ChangeLeverage'
|
||||
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 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 borrowAsset = getAssetByDenom(modal?.staking.strategy.denoms.borrow || '')
|
||||
|
||||
if (!modal || !collateralAsset || !borrowAsset) return null
|
||||
if (!modal || !collateralAsset || !borrowAsset || !account) return null
|
||||
|
||||
return <HlsModal collateralAsset={collateralAsset} borrowAsset={borrowAsset} />
|
||||
return (
|
||||
<HlsModal
|
||||
account={account}
|
||||
action={modal.staking.action}
|
||||
collateralAsset={collateralAsset}
|
||||
borrowAsset={borrowAsset}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
interface Props {
|
||||
account: Account
|
||||
action: HlsStakingManageAction
|
||||
borrowAsset: Asset
|
||||
collateralAsset: Asset
|
||||
}
|
||||
|
||||
function HlsModal(props: Props) {
|
||||
const updatedAccount = useStore((s) => s.updatedAccount)
|
||||
function handleClose() {
|
||||
useStore.setState({ hlsManageModal: null })
|
||||
}
|
||||
|
||||
const ContentComponent = useCallback(() => {
|
||||
switch (props.action) {
|
||||
case 'deposit':
|
||||
return <Deposit {...props} />
|
||||
case 'withdraw':
|
||||
return <Withdraw {...props} />
|
||||
case 'repay':
|
||||
return <Repay {...props} />
|
||||
case 'leverage':
|
||||
return <ChangeLeverage {...props} />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}, [props])
|
||||
|
||||
return (
|
||||
<Modal
|
||||
header={<Header primaryAsset={props.collateralAsset} secondaryAsset={props.borrowAsset} />}
|
||||
headerClassName='gradient-header pl-2 pr-2.5 py-3 border-b-white/5 border-b'
|
||||
contentClassName='flex flex-col p-6'
|
||||
modalClassName='max-w-modal-md'
|
||||
<ModalContentWithSummary
|
||||
account={props.account}
|
||||
header={
|
||||
<Header
|
||||
action={props.action}
|
||||
primaryAsset={props.collateralAsset}
|
||||
secondaryAsset={props.borrowAsset}
|
||||
/>
|
||||
}
|
||||
onClose={handleClose}
|
||||
>
|
||||
Some kind of text here
|
||||
</Modal>
|
||||
content={<ContentComponent />}
|
||||
isContentCard
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
|
||||
import Modal from 'components/Modal'
|
||||
import Content from 'components/Modals/HLS/Content'
|
||||
import Content from 'components/Modals/HLS/Deposit'
|
||||
import Header from 'components/Modals/HLS/Header'
|
||||
import useStore from 'store'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
|
38
src/components/Modals/ModalContentWithSummary.tsx
Normal file
38
src/components/Modals/ModalContentWithSummary.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
|
||||
import AccountSummary from 'components/Account/AccountSummary'
|
||||
import Card from 'components/Card'
|
||||
import Modal, { ModalProps } from 'components/Modal'
|
||||
import useStore from 'store'
|
||||
|
||||
interface Props extends ModalProps {
|
||||
account: Account
|
||||
isContentCard?: boolean
|
||||
}
|
||||
|
||||
export default function ModalContentWithSummary(props: Props) {
|
||||
const updatedAccount = useStore((s) => s.updatedAccount)
|
||||
return (
|
||||
<Modal
|
||||
{...props}
|
||||
headerClassName={classNames(
|
||||
'gradient-header pl-2 pr-2.5 py-3 border-b-white/5 border-b',
|
||||
props.headerClassName,
|
||||
)}
|
||||
contentClassName={classNames('flex items-start flex-1 gap-6 p-6', props.contentClassName)}
|
||||
>
|
||||
{props.isContentCard ? (
|
||||
<Card
|
||||
className='flex flex-1 p-4 bg-white/5'
|
||||
contentClassName='gap-6 flex flex-col justify-between h-full min-h-[380px]'
|
||||
>
|
||||
{props.content}
|
||||
</Card>
|
||||
) : (
|
||||
props.content
|
||||
)}
|
||||
<AccountSummary account={updatedAccount || props.account} />
|
||||
</Modal>
|
||||
)
|
||||
}
|
25
src/components/SummaryItems.tsx
Normal file
25
src/components/SummaryItems.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import React from 'react'
|
||||
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import Text from 'components/Text'
|
||||
|
||||
interface Props {
|
||||
items: SummaryItem[]
|
||||
}
|
||||
|
||||
export default function SummaryItems(props: Props) {
|
||||
return (
|
||||
<div className='grid grid-cols-2 gap-2'>
|
||||
{props.items.map((item) => (
|
||||
<React.Fragment key={item.title}>
|
||||
<Text className='text-white/60 text-sm'>{item.title}</Text>
|
||||
<FormattedNumber
|
||||
className='place-self-end text-sm'
|
||||
amount={item.amount}
|
||||
options={item.options}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import Tippy from '@tippyjs/react'
|
||||
import Tippy, { TippyProps } from '@tippyjs/react'
|
||||
import classNames from 'classnames'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
@ -8,10 +8,9 @@ import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
|
||||
interface Props {
|
||||
interface Props extends TippyProps {
|
||||
content: ReactNode | string
|
||||
type: TooltipType
|
||||
children?: ReactNode | string
|
||||
className?: string
|
||||
delay?: number
|
||||
interactive?: boolean
|
||||
@ -45,6 +44,7 @@ export const Tooltip = (props: Props) => {
|
||||
className={props.contentClassName}
|
||||
/>
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{props.children ? (
|
||||
<span
|
||||
|
59
src/hooks/HLS/useClosePositionActions.ts
Normal file
59
src/hooks/HLS/useClosePositionActions.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import usePrices from 'hooks/usePrices'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||
import { getCoinAmount, getCoinValue } from 'utils/formatters'
|
||||
|
||||
interface Props {
|
||||
account: HLSAccountWithStrategy
|
||||
}
|
||||
|
||||
export default function UseClosePositionActions(props: Props): Action[] {
|
||||
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
|
||||
const { data: prices } = usePrices()
|
||||
|
||||
const collateralDenom = props.account.strategy.denoms.deposit
|
||||
const borrowDenom = props.account.strategy.denoms.borrow
|
||||
|
||||
const debtAmount: BigNumber = useMemo(
|
||||
() =>
|
||||
props.account.debts.find((debt) => debt.denom === props.account.strategy.denoms.borrow)
|
||||
?.amount || BN_ZERO,
|
||||
[props.account.debts, props.account.strategy.denoms.borrow],
|
||||
)
|
||||
|
||||
const swapInAmount = useMemo(() => {
|
||||
const targetValue = getCoinValue(BNCoin.fromDenomAndBigNumber(borrowDenom, debtAmount), prices)
|
||||
return getCoinAmount(collateralDenom, targetValue, prices)
|
||||
.times(1 + slippage)
|
||||
.integerValue()
|
||||
}, [slippage, borrowDenom, debtAmount, prices, collateralDenom])
|
||||
|
||||
return useMemo<Action[]>(
|
||||
() => [
|
||||
...(debtAmount.isZero()
|
||||
? []
|
||||
: [
|
||||
{
|
||||
swap_exact_in: {
|
||||
coin_in: BNCoin.fromDenomAndBigNumber(collateralDenom, swapInAmount).toActionCoin(),
|
||||
denom_out: borrowDenom,
|
||||
slippage: slippage.toString(),
|
||||
},
|
||||
},
|
||||
{
|
||||
repay: {
|
||||
coin: BNCoin.fromDenomAndBigNumber(borrowDenom, debtAmount).toActionCoin(),
|
||||
},
|
||||
},
|
||||
]),
|
||||
{ refund_all_coin_balances: {} },
|
||||
],
|
||||
[borrowDenom, collateralDenom, debtAmount, slippage, swapInAmount],
|
||||
)
|
||||
}
|
38
src/hooks/HLS/useDepositActions.ts
Normal file
38
src/hooks/HLS/useDepositActions.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
|
||||
interface Props {
|
||||
borrowCoin: BNCoin
|
||||
depositCoin: BNCoin
|
||||
}
|
||||
|
||||
export default function useDepositActions(props: Props) {
|
||||
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
|
||||
|
||||
return useMemo(
|
||||
() => [
|
||||
{
|
||||
deposit: props.depositCoin.toCoin(),
|
||||
},
|
||||
...(props.borrowCoin.amount.isZero()
|
||||
? []
|
||||
: [
|
||||
{
|
||||
borrow: props.borrowCoin.toCoin(),
|
||||
},
|
||||
{
|
||||
swap_exact_in: {
|
||||
denom_out: props.depositCoin.denom,
|
||||
slippage: slippage.toString(),
|
||||
coin_in: props.borrowCoin.toActionCoin(),
|
||||
},
|
||||
},
|
||||
]),
|
||||
],
|
||||
[props.borrowCoin, props.depositCoin, slippage],
|
||||
)
|
||||
}
|
@ -1,8 +1,12 @@
|
||||
import { useMemo, useState } from 'react'
|
||||
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import usePrices from 'hooks/usePrices'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||
import { getCoinValue } from 'utils/formatters'
|
||||
|
||||
interface Props {
|
||||
@ -11,10 +15,21 @@ interface Props {
|
||||
}
|
||||
export default function useDepositHlsVault(props: Props) {
|
||||
const { data: prices } = usePrices()
|
||||
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
|
||||
|
||||
const [depositAmount, setDepositAmount] = useState<BigNumber>(BN_ZERO)
|
||||
const [borrowAmount, setBorrowAmount] = useState<BigNumber>(BN_ZERO)
|
||||
|
||||
const depositCoin = useMemo(
|
||||
() => BNCoin.fromDenomAndBigNumber(props.collateralDenom, depositAmount),
|
||||
[depositAmount, props.collateralDenom],
|
||||
)
|
||||
|
||||
const borrowCoin = useMemo(
|
||||
() => BNCoin.fromDenomAndBigNumber(props.borrowDenom, borrowAmount),
|
||||
[borrowAmount, props.borrowDenom],
|
||||
)
|
||||
|
||||
const { positionValue, leverage } = useMemo(() => {
|
||||
const collateralValue = getCoinValue(
|
||||
BNCoin.fromDenomAndBigNumber(props.collateralDenom, depositAmount),
|
||||
@ -31,6 +46,32 @@ export default function useDepositHlsVault(props: Props) {
|
||||
}
|
||||
}, [borrowAmount, depositAmount, prices, props.collateralDenom, props.borrowDenom])
|
||||
|
||||
const actions: Action[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
deposit: depositCoin.toCoin(),
|
||||
},
|
||||
...(borrowAmount.isZero()
|
||||
? []
|
||||
: [
|
||||
{
|
||||
borrow: borrowCoin.toCoin(),
|
||||
},
|
||||
{
|
||||
swap_exact_in: {
|
||||
denom_out: props.collateralDenom,
|
||||
slippage: slippage.toString(),
|
||||
coin_in: BNCoin.fromDenomAndBigNumber(
|
||||
props.borrowDenom,
|
||||
borrowAmount,
|
||||
).toActionCoin(),
|
||||
},
|
||||
},
|
||||
]),
|
||||
],
|
||||
[borrowAmount, borrowCoin, depositCoin, props.borrowDenom, props.collateralDenom, slippage],
|
||||
)
|
||||
|
||||
return {
|
||||
setDepositAmount,
|
||||
depositAmount,
|
||||
@ -38,5 +79,8 @@ export default function useDepositHlsVault(props: Props) {
|
||||
borrowAmount,
|
||||
positionValue,
|
||||
leverage,
|
||||
depositCoin,
|
||||
borrowCoin,
|
||||
actions,
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { cloneAccount } from 'utils/accounts'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { getCoinAmount, getCoinValue } from 'utils/formatters'
|
||||
import { getValueFromBNCoins } from 'utils/helpers'
|
||||
|
||||
export interface VaultValue {
|
||||
@ -37,6 +38,7 @@ export function useUpdatedAccount(account?: Account) {
|
||||
const [addedVaultValues, addVaultValues] = useState<VaultValue[]>([])
|
||||
const [addedLends, addLends] = useState<BNCoin[]>([])
|
||||
const [removedLends, removeLends] = useState<BNCoin[]>([])
|
||||
const [addedTrades, addTrades] = useState<BNCoin[]>([])
|
||||
|
||||
const removeDepositAndLendsByDenom = useCallback(
|
||||
(denom: string) => {
|
||||
@ -151,6 +153,18 @@ export function useUpdatedAccount(account?: Account) {
|
||||
[account, addDebts, addDeposits, addLends, removeDeposits, removeLends],
|
||||
)
|
||||
|
||||
const simulateHlsStakingDeposit = useCallback(
|
||||
(depositCoin: BNCoin, borrowCoin: BNCoin) => {
|
||||
addDeposits([depositCoin])
|
||||
addDebts([borrowCoin])
|
||||
const additionalDebtValue = getCoinValue(borrowCoin, prices)
|
||||
|
||||
const tradeOutputAmount = getCoinAmount(depositCoin.denom, additionalDebtValue, prices)
|
||||
addTrades([BNCoin.fromDenomAndBigNumber(depositCoin.denom, tradeOutputAmount)])
|
||||
},
|
||||
[prices],
|
||||
)
|
||||
|
||||
const simulateVaultDeposit = useCallback(
|
||||
(address: string, coins: BNCoin[], borrowCoins: BNCoin[]) => {
|
||||
if (!account) return
|
||||
@ -179,7 +193,7 @@ export function useUpdatedAccount(account?: Account) {
|
||||
if (!account) return
|
||||
|
||||
const accountCopy = cloneAccount(account)
|
||||
accountCopy.deposits = addCoins(addedDeposits, [...accountCopy.deposits])
|
||||
accountCopy.deposits = addCoins([...addedDeposits, ...addedTrades], [...accountCopy.deposits])
|
||||
accountCopy.debts = addCoins(addedDebts, [...accountCopy.debts])
|
||||
accountCopy.vaults = addValueToVaults(
|
||||
addedVaultValues,
|
||||
@ -205,6 +219,7 @@ export function useUpdatedAccount(account?: Account) {
|
||||
removedLends,
|
||||
availableVaults,
|
||||
prices,
|
||||
addedTrades,
|
||||
])
|
||||
|
||||
return {
|
||||
@ -225,6 +240,7 @@ export function useUpdatedAccount(account?: Account) {
|
||||
removedLends,
|
||||
simulateBorrow,
|
||||
simulateDeposits,
|
||||
simulateHlsStakingDeposit,
|
||||
simulateLending,
|
||||
simulateRepay,
|
||||
simulateTrade,
|
||||
|
@ -232,6 +232,32 @@ export default function createBroadcastSlice(
|
||||
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
closeHlsStakingPosition: async (options: { accountId: string; actions: Action[] }) => {
|
||||
const msg: CreditManagerExecuteMsg = {
|
||||
update_credit_account: {
|
||||
account_id: options.accountId,
|
||||
actions: options.actions,
|
||||
},
|
||||
}
|
||||
|
||||
const response = get().executeMsg({
|
||||
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
||||
})
|
||||
|
||||
get().setToast({
|
||||
response,
|
||||
options: {
|
||||
action: 'deposit',
|
||||
message: `Exited HLS strategy`,
|
||||
},
|
||||
})
|
||||
|
||||
const response_1 = await response
|
||||
return response_1.result
|
||||
? getSingleValueFromBroadcastResult(response_1.result, 'wasm', 'token_id')
|
||||
: null
|
||||
},
|
||||
|
||||
createAccount: async (accountKind: AccountKind) => {
|
||||
const msg: CreditManagerExecuteMsg = {
|
||||
create_credit_account: accountKind,
|
||||
@ -538,8 +564,10 @@ export default function createBroadcastSlice(
|
||||
coin: BNCoin
|
||||
accountBalance?: boolean
|
||||
lend?: BNCoin
|
||||
fromWallet?: boolean
|
||||
}) => {
|
||||
const actions: Action[] = [
|
||||
...(options.fromWallet ? [{ deposit: options.coin.toCoin() }] : []),
|
||||
{
|
||||
repay: {
|
||||
coin: options.coin.toActionCoin(options.accountBalance),
|
||||
@ -558,7 +586,14 @@ export default function createBroadcastSlice(
|
||||
}
|
||||
|
||||
const response = get().executeMsg({
|
||||
messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, [])],
|
||||
messages: [
|
||||
generateExecutionMessage(
|
||||
get().address,
|
||||
ENV.ADDRESS_CREDIT_MANAGER,
|
||||
msg,
|
||||
options.fromWallet ? [options.coin.toCoin()] : [],
|
||||
),
|
||||
],
|
||||
})
|
||||
|
||||
get().setToast({
|
||||
|
5
src/types/interfaces/components/SummaryItems.d.ts
vendored
Normal file
5
src/types/interfaces/components/SummaryItems.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
interface SummaryItem {
|
||||
amount: number
|
||||
options: FormatOptions
|
||||
title: string
|
||||
}
|
5
src/types/interfaces/store/broadcast.d.ts
vendored
5
src/types/interfaces/store/broadcast.d.ts
vendored
@ -92,6 +92,10 @@ interface BroadcastSlice {
|
||||
borrowToWallet: boolean
|
||||
}) => Promise<boolean>
|
||||
claimRewards: (options: { accountId: string }) => ExecutableTx
|
||||
closeHlsStakingPosition: (options: {
|
||||
accountId: string
|
||||
actions: Action[]
|
||||
}) => Promise<string | null>
|
||||
createAccount: (
|
||||
accountKind: import('types/generated/mars-rover-health-types/MarsRoverHealthTypes.types').AccountKind,
|
||||
) => Promise<string | null>
|
||||
@ -113,6 +117,7 @@ interface BroadcastSlice {
|
||||
coin: BNCoin
|
||||
accountBalance?: boolean
|
||||
lend?: BNCoin
|
||||
fromWallet?: boolean
|
||||
}) => Promise<boolean>
|
||||
setToast: (toast: ToastObject) => void
|
||||
swap: (options: {
|
||||
|
5
src/types/interfaces/store/modals.d.ts
vendored
5
src/types/interfaces/store/modals.d.ts
vendored
@ -76,8 +76,11 @@ interface HlsModal {
|
||||
}
|
||||
|
||||
interface HlsManageModal {
|
||||
accountId: string
|
||||
staking: {
|
||||
strategy: HLSStrategy
|
||||
action: 'deposit' | 'withdraw' | 'repay'
|
||||
action: HlsStakingManageAction
|
||||
}
|
||||
}
|
||||
|
||||
type HlsStakingManageAction = 'deposit' | 'withdraw' | 'repay' | 'leverage'
|
||||
|
Loading…
Reference in New Issue
Block a user