Hls leverage (#628)
* ✨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 * ✨finish all actiosn for HLS staking * 🐛clean up tooltip props
This commit is contained in:
parent
cc3b0eee20
commit
1ecd80dac2
@ -26,6 +26,7 @@ export default async function getHLSStakingAssets() {
|
||||
used: BN(depositCap.amount),
|
||||
max: BN(depositCap.cap),
|
||||
},
|
||||
apy: 18, // TODO: Actually implement the APY here!
|
||||
} as HLSStrategy
|
||||
})
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
|
||||
import DropDownButton from 'components/Button/DropDownButton'
|
||||
import { ArrowDownLine, Cross, HandCoins, Plus, Scale } from 'components/Icons'
|
||||
@ -61,7 +61,7 @@ export default function Manage(props: Props) {
|
||||
onClick: () => closeHlsStakingPosition({ accountId: props.account.id, actions }),
|
||||
},
|
||||
],
|
||||
[actions, closeHlsStakingPosition, openModal, props.account.id, hasNoDebt],
|
||||
[actions, closeHlsStakingPosition, hasNoDebt, openModal, props.account.id],
|
||||
)
|
||||
|
||||
return <DropDownButton items={ITEMS} text='Manage' color='tertiary' />
|
||||
|
@ -17,7 +17,7 @@ export const menuTree: { pages: Page[]; label: string }[] = [
|
||||
{ pages: ['lend', 'farm'], label: 'Earn' },
|
||||
{ pages: ['borrow'], label: 'Borrow' },
|
||||
{ pages: ['portfolio'], label: 'Portfolio' },
|
||||
...(ENABLE_HLS ? [{ pages: ['hls-farm', 'hls-staking'] as Page[], label: 'High Leverage' }] : []),
|
||||
...(ENABLE_HLS ? [{ pages: ['hls-staking'] as Page[], label: 'High Leverage' }] : []),
|
||||
]
|
||||
|
||||
export default function DesktopHeader() {
|
||||
|
@ -12,20 +12,28 @@ interface Props {
|
||||
onChangeAmount: (amount: BigNumber) => void
|
||||
onClickBtn: () => void
|
||||
positionValue: BigNumber
|
||||
leverage: number
|
||||
maxLeverage: number
|
||||
}
|
||||
|
||||
export default function Leverage(props: Props) {
|
||||
return (
|
||||
<div className='p-4 flex-col gap-6 flex'>
|
||||
<div className='flex-col gap-6 flex justify-between h-full p-4'>
|
||||
<TokenInputWithSlider
|
||||
amount={props.amount}
|
||||
asset={props.asset}
|
||||
max={props.max}
|
||||
onChange={props.onChangeAmount}
|
||||
maxText='Max borrow'
|
||||
leverage={{
|
||||
current: props.leverage,
|
||||
max: props.maxLeverage,
|
||||
}}
|
||||
/>
|
||||
<LeverageSummary asset={props.asset} positionValue={props.positionValue} />
|
||||
<Button onClick={props.onClickBtn} text='Continue' rightIcon={<ArrowRight />} />
|
||||
<div className='flex flex-col gap-6'>
|
||||
<LeverageSummary asset={props.asset} positionValue={props.positionValue} />
|
||||
<Button onClick={props.onClickBtn} text='Continue' rightIcon={<ArrowRight />} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ interface Props {
|
||||
borrowAsset: Asset
|
||||
collateralAsset: Asset
|
||||
vaultAddress: string | null
|
||||
strategy?: HLSStrategy
|
||||
}
|
||||
|
||||
export default function Controller(props: Props) {
|
||||
@ -46,19 +47,24 @@ export default function Controller(props: Props) {
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<StakingContent
|
||||
walletCollateralAsset={walletCollateralAsset}
|
||||
collateralAsset={props.collateralAsset}
|
||||
borrowAsset={props.borrowAsset}
|
||||
emptyHlsAccounts={emptyHlsAccounts}
|
||||
hlsAccounts={hlsAccounts}
|
||||
isOpen={isOpen}
|
||||
selectedAccount={selectedAccount}
|
||||
setSelectedAccount={setSelectedAccount}
|
||||
toggleIsOpen={toggleIsOpen}
|
||||
/>
|
||||
)
|
||||
if (props.strategy) {
|
||||
return (
|
||||
<StakingContent
|
||||
walletCollateralAsset={walletCollateralAsset}
|
||||
collateralAsset={props.collateralAsset}
|
||||
borrowAsset={props.borrowAsset}
|
||||
emptyHlsAccounts={emptyHlsAccounts}
|
||||
hlsAccounts={hlsAccounts}
|
||||
isOpen={isOpen}
|
||||
selectedAccount={selectedAccount}
|
||||
setSelectedAccount={setSelectedAccount}
|
||||
toggleIsOpen={toggleIsOpen}
|
||||
strategy={props.strategy}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
interface ContentProps {
|
||||
@ -120,7 +126,11 @@ function Vault(props: VaultContentProps) {
|
||||
return <Accordion className='h-[546px] overflow-y-scroll scrollbar-hide' items={items} />
|
||||
}
|
||||
|
||||
function StakingContent(props: ContentProps) {
|
||||
interface StakingContentProps extends ContentProps {
|
||||
strategy: HLSStrategy
|
||||
}
|
||||
|
||||
function StakingContent(props: StakingContentProps) {
|
||||
const {
|
||||
depositAmount,
|
||||
onChangeCollateral,
|
||||
@ -152,10 +162,11 @@ function StakingContent(props: ContentProps) {
|
||||
positionValue,
|
||||
selectedAccount: props.selectedAccount,
|
||||
setSelectedAccount: props.setSelectedAccount,
|
||||
strategy: props.strategy,
|
||||
toggleIsOpen: props.toggleIsOpen,
|
||||
updatedAccount,
|
||||
maxBorrowAmount,
|
||||
apy: 0, // TODO: Implement APY
|
||||
apy: props.strategy.apy || 0, // TODO: Implement APY
|
||||
walletCollateralAsset: props.walletCollateralAsset,
|
||||
})
|
||||
|
||||
|
@ -29,6 +29,7 @@ interface Props {
|
||||
positionValue: BigNumber
|
||||
selectedAccount: Account | null
|
||||
setSelectedAccount: (account: Account) => void
|
||||
strategy?: HLSStrategy
|
||||
toggleIsOpen: (index: number) => void
|
||||
updatedAccount: Account | undefined
|
||||
walletCollateralAsset: Coin | undefined
|
||||
@ -64,12 +65,14 @@ export default function useAccordionItems(props: Props) {
|
||||
title: 'Leverage',
|
||||
renderContent: () => (
|
||||
<Leverage
|
||||
leverage={props.leverage}
|
||||
amount={props.borrowAmount}
|
||||
asset={props.borrowAsset}
|
||||
onChangeAmount={props.onChangeDebt}
|
||||
onClickBtn={() => props.toggleIsOpen(2)}
|
||||
max={props.maxBorrowAmount}
|
||||
positionValue={props.positionValue}
|
||||
maxLeverage={props.strategy?.maxLeverage || 1}
|
||||
/>
|
||||
),
|
||||
renderSubTitle: () => (
|
||||
|
@ -23,8 +23,6 @@ export default function useStakingController(props: Props) {
|
||||
setBorrowAmount,
|
||||
borrowAmount,
|
||||
positionValue,
|
||||
borrowCoin,
|
||||
depositCoin,
|
||||
actions,
|
||||
} = useDepositHlsVault({
|
||||
collateralDenom: collateralAsset.denom,
|
||||
@ -40,6 +38,7 @@ export default function useStakingController(props: Props) {
|
||||
}, [computeMaxBorrowAmount, props.borrowAsset.denom])
|
||||
|
||||
const execute = useCallback(() => {
|
||||
useStore.setState({ hlsModal: null })
|
||||
addToStakingStrategy({
|
||||
actions,
|
||||
accountId: selectedAccount.id,
|
||||
|
@ -1,10 +1,120 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import LeverageSummary from 'components/Modals/HLS/Deposit/LeverageSummary'
|
||||
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useHealthComputer from 'hooks/useHealthComputer'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import usePrices from 'hooks/usePrices'
|
||||
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
|
||||
import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { getAccountPositionValues } from 'utils/accounts'
|
||||
import { getHlsStakingChangeLevActions } from 'utils/actions'
|
||||
import { byDenom } from 'utils/array'
|
||||
|
||||
interface Props {
|
||||
account: Account
|
||||
account: HLSAccountWithStrategy
|
||||
action: HlsStakingManageAction
|
||||
borrowAsset: Asset
|
||||
collateralAsset: Asset
|
||||
}
|
||||
|
||||
export default function Repay(props: Props) {
|
||||
return <></>
|
||||
export default function ChangeLeverage(props: Props) {
|
||||
const { data: prices } = usePrices()
|
||||
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
|
||||
const { updatedAccount, simulateHlsStakingDeposit, simulateHlsStakingWithdraw, leverage } =
|
||||
useUpdatedAccount(props.account)
|
||||
|
||||
const changeHlsStakingLeverage = useStore((s) => s.changeHlsStakingLeverage)
|
||||
const { computeMaxBorrowAmount } = useHealthComputer(props.account)
|
||||
const previousDebt: BigNumber = useMemo(
|
||||
() => props.account.debts.find(byDenom(props.borrowAsset.denom))?.amount || BN_ZERO,
|
||||
[props.account.debts, props.borrowAsset.denom],
|
||||
)
|
||||
|
||||
const [currentDebt, setAmount] = useState(previousDebt)
|
||||
const maxBorrowAmount = useMemo(() => {
|
||||
return computeMaxBorrowAmount(props.borrowAsset.denom, 'deposit').plus(previousDebt)
|
||||
}, [computeMaxBorrowAmount, previousDebt, props.borrowAsset.denom])
|
||||
|
||||
const onChangeAmount = useCallback(
|
||||
(currentDebt: BigNumber) => {
|
||||
setAmount(currentDebt)
|
||||
if (currentDebt.isLessThan(previousDebt)) {
|
||||
simulateHlsStakingWithdraw(
|
||||
props.collateralAsset.denom,
|
||||
props.borrowAsset.denom,
|
||||
previousDebt.minus(currentDebt),
|
||||
)
|
||||
} else {
|
||||
simulateHlsStakingDeposit(
|
||||
BNCoin.fromDenomAndBigNumber(props.collateralAsset.denom, BN_ZERO),
|
||||
BNCoin.fromDenomAndBigNumber(props.borrowAsset.denom, currentDebt.minus(previousDebt)),
|
||||
)
|
||||
}
|
||||
},
|
||||
[
|
||||
previousDebt,
|
||||
props.borrowAsset.denom,
|
||||
props.collateralAsset.denom,
|
||||
simulateHlsStakingDeposit,
|
||||
simulateHlsStakingWithdraw,
|
||||
],
|
||||
)
|
||||
|
||||
const positionValue = useMemo(() => {
|
||||
const [deposits, lends, debts, vaults] = getAccountPositionValues(
|
||||
updatedAccount || props.account,
|
||||
prices,
|
||||
)
|
||||
|
||||
return deposits.plus(lends).plus(debts).plus(vaults)
|
||||
}, [prices, props.account, updatedAccount])
|
||||
|
||||
const handleOnClick = useCallback(() => {
|
||||
useStore.setState({ hlsManageModal: null })
|
||||
if (currentDebt.isEqualTo(previousDebt)) return
|
||||
const actions = getHlsStakingChangeLevActions(
|
||||
previousDebt,
|
||||
currentDebt,
|
||||
props.collateralAsset.denom,
|
||||
props.borrowAsset.denom,
|
||||
slippage,
|
||||
prices,
|
||||
)
|
||||
changeHlsStakingLeverage({ accountId: props.account.id, actions })
|
||||
}, [
|
||||
currentDebt,
|
||||
changeHlsStakingLeverage,
|
||||
previousDebt,
|
||||
prices,
|
||||
props.account.id,
|
||||
props.borrowAsset.denom,
|
||||
props.collateralAsset.denom,
|
||||
slippage,
|
||||
])
|
||||
|
||||
return (
|
||||
<>
|
||||
<TokenInputWithSlider
|
||||
amount={currentDebt}
|
||||
asset={props.borrowAsset}
|
||||
max={maxBorrowAmount}
|
||||
onChange={onChangeAmount}
|
||||
maxText='Max borrow'
|
||||
leverage={{
|
||||
current: leverage,
|
||||
max: props.account.strategy.maxLeverage,
|
||||
}}
|
||||
/>
|
||||
<div className='flex flex-col gap-6'>
|
||||
<LeverageSummary asset={props.borrowAsset} positionValue={positionValue} />
|
||||
<Button onClick={handleOnClick} text='Confirm' />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ export default function Deposit(props: Props) {
|
||||
const actions = useDepositActions({ depositCoin, borrowCoin })
|
||||
|
||||
const currentDebt: BigNumber = useMemo(
|
||||
() => props.account.debts.find(byDenom(props.borrowAsset.denom)).amount || BN_ZERO,
|
||||
() => props.account.debts.find(byDenom(props.borrowAsset.denom))?.amount || BN_ZERO,
|
||||
[props.account.debts, props.borrowAsset.denom],
|
||||
)
|
||||
|
||||
|
@ -20,7 +20,7 @@ export default function HlsManageModalController() {
|
||||
|
||||
return (
|
||||
<HlsModal
|
||||
account={account}
|
||||
account={{ ...account, strategy: modal.staking.strategy } as HLSAccountWithStrategy}
|
||||
action={modal.staking.action}
|
||||
collateralAsset={collateralAsset}
|
||||
borrowAsset={borrowAsset}
|
||||
@ -29,7 +29,7 @@ export default function HlsManageModalController() {
|
||||
}
|
||||
|
||||
interface Props {
|
||||
account: Account
|
||||
account: HLSAccountWithStrategy
|
||||
action: HlsStakingManageAction
|
||||
borrowAsset: Asset
|
||||
collateralAsset: Asset
|
||||
|
@ -28,7 +28,12 @@ export default function HlsModalController() {
|
||||
)
|
||||
if (modal?.strategy)
|
||||
return (
|
||||
<HlsModal primaryAsset={primaryAsset} secondaryAsset={secondaryAsset} vaultAddress={null} />
|
||||
<HlsModal
|
||||
primaryAsset={primaryAsset}
|
||||
secondaryAsset={secondaryAsset}
|
||||
strategy={modal.strategy}
|
||||
vaultAddress={null}
|
||||
/>
|
||||
)
|
||||
|
||||
return null
|
||||
@ -37,6 +42,7 @@ export default function HlsModalController() {
|
||||
interface Props {
|
||||
primaryAsset: Asset
|
||||
secondaryAsset: Asset
|
||||
strategy?: HLSStrategy
|
||||
vaultAddress: string | null
|
||||
}
|
||||
|
||||
@ -57,6 +63,7 @@ function HlsModal(props: Props) {
|
||||
collateralAsset={props.primaryAsset}
|
||||
borrowAsset={props.secondaryAsset}
|
||||
vaultAddress={props.vaultAddress}
|
||||
strategy={props.strategy}
|
||||
/>
|
||||
</Modal>
|
||||
)
|
||||
|
@ -6,7 +6,7 @@ import DepositCapMessage from 'components/DepositCapMessage'
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import Divider from 'components/Divider'
|
||||
import { ArrowRight, ExclamationMarkCircled } from 'components/Icons'
|
||||
import Slider from 'components/Slider'
|
||||
import Index from 'components/Slider'
|
||||
import Text from 'components/Text'
|
||||
import TokenInput from 'components/TokenInput'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
@ -192,7 +192,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{props.borrowings.length === 1 && <Slider onChange={onChangeSlider} value={percentage} />}
|
||||
{props.borrowings.length === 1 && <Index onChange={onChangeSlider} value={percentage} />}
|
||||
{props.borrowings.length === 0 && (
|
||||
<div className='flex items-center gap-4 py-2'>
|
||||
<div className='w-4'>
|
||||
|
@ -3,7 +3,6 @@ import { Navigate, Outlet, Route, Routes as RoutesWrapper } from 'react-router-d
|
||||
import Layout from 'pages/_layout'
|
||||
import BorrowPage from 'pages/BorrowPage'
|
||||
import FarmPage from 'pages/FarmPage'
|
||||
import HLSFarmPage from 'pages/HLSFarmPage'
|
||||
import HLSStakingPage from 'pages/HLSStakingPage'
|
||||
import LendPage from 'pages/LendPage'
|
||||
import MobilePage from 'pages/MobilePage'
|
||||
@ -28,7 +27,7 @@ export default function Routes() {
|
||||
<Route path='/portfolio' element={<PortfolioPage />} />
|
||||
<Route path='/mobile' element={<MobilePage />} />
|
||||
<Route path='/hls-staking' element={<HLSStakingPage />} />
|
||||
<Route path='/hls-farm' element={<HLSFarmPage />} />
|
||||
{/*<Route path='/hls-farm' element={<HLSFarmPage />} />*/}
|
||||
<Route path='/' element={<TradePage />} />
|
||||
<Route path='/wallets/:address'>
|
||||
<Route path='trade' element={<TradePage />} />
|
||||
@ -36,11 +35,11 @@ export default function Routes() {
|
||||
<Route path='lend' element={<LendPage />} />
|
||||
<Route path='borrow' element={<BorrowPage />} />
|
||||
<Route path='portfolio' element={<PortfolioPage />} />
|
||||
<Route path='hls-staking' element={<HLSStakingPage />} />
|
||||
<Route path='portfolio/:accountId'>
|
||||
<Route path='' element={<PortfolioAccountPage />} />
|
||||
</Route>
|
||||
<Route path='hls-staking' element={<HLSStakingPage />} />
|
||||
<Route path='hls-farm' element={<HLSFarmPage />} />
|
||||
{/*<Route path='hls-farm' element={<HLSFarmPage />} />*/}
|
||||
<Route path='' element={<TradePage />} />
|
||||
</Route>
|
||||
<Route path='*' element={<Navigate to='/' />} />
|
||||
|
@ -1,205 +0,0 @@
|
||||
import classNames from 'classnames'
|
||||
import { ChangeEvent, useRef, useState } from 'react'
|
||||
import Draggable from 'react-draggable'
|
||||
|
||||
import { OverlayMark } from 'components/Icons/index'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
|
||||
type Props = {
|
||||
value: number
|
||||
onChange: (value: number) => void
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export default function Slider(props: Props) {
|
||||
const [showTooltip, setShowTooltip] = useToggle()
|
||||
const [sliderRect, setSliderRect] = useState({ width: 0, left: 0, right: 0 })
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const nodeRef = useRef(null)
|
||||
const [isDragging, setIsDragging] = useToggle()
|
||||
|
||||
function handleSliderRect() {
|
||||
const leftCap = ref.current?.getBoundingClientRect().left ?? 0
|
||||
const rightCap = ref.current?.getBoundingClientRect().right ?? 0
|
||||
const newSliderWidth = ref.current?.getBoundingClientRect().width ?? 0
|
||||
|
||||
if (
|
||||
sliderRect.width !== newSliderWidth ||
|
||||
leftCap !== sliderRect.left ||
|
||||
rightCap !== sliderRect.right
|
||||
) {
|
||||
setSliderRect({
|
||||
left: leftCap,
|
||||
right: rightCap,
|
||||
width: newSliderWidth,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function handleDrag(e: any) {
|
||||
if (!isDragging) {
|
||||
setIsDragging(true)
|
||||
}
|
||||
|
||||
const current: number = e.clientX
|
||||
|
||||
if (current < sliderRect.left) {
|
||||
props.onChange(0)
|
||||
return
|
||||
}
|
||||
|
||||
if (current > sliderRect.right) {
|
||||
props.onChange(100)
|
||||
return
|
||||
}
|
||||
|
||||
const value = Math.round(((current - sliderRect.left) / sliderRect.width) * 100)
|
||||
|
||||
if (value !== props.value) {
|
||||
props.onChange(value)
|
||||
}
|
||||
}
|
||||
|
||||
function handleSliderClick(e: ChangeEvent<HTMLInputElement>) {
|
||||
props.onChange(Number(e.target.value))
|
||||
}
|
||||
|
||||
function handleShowTooltip() {
|
||||
setShowTooltip(true)
|
||||
}
|
||||
|
||||
function handleHideTooltip() {
|
||||
setShowTooltip(false)
|
||||
}
|
||||
|
||||
const DraggableElement: any = Draggable
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={classNames(
|
||||
'relative min-h-3 w-full transition-opacity',
|
||||
props.className,
|
||||
props.disabled && 'pointer-events-none',
|
||||
)}
|
||||
onMouseEnter={handleSliderRect}
|
||||
>
|
||||
<input
|
||||
type='range'
|
||||
value={props.value}
|
||||
onChange={handleSliderClick}
|
||||
onMouseDown={handleShowTooltip}
|
||||
className='absolute z-2 w-full hover:cursor-pointer appearance-none bg-transparent [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:appearance-none'
|
||||
/>
|
||||
<div className='absolute flex items-center w-full gap-1'>
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={0}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
<Track maxValue={23} sliderValue={props.value} />
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={25}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
<Track maxValue={48} sliderValue={props.value} />
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={50}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
<Track maxValue={73} sliderValue={props.value} />
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={75}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
<Track maxValue={98} sliderValue={props.value} />
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={100}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
</div>
|
||||
{!props.disabled && (
|
||||
<div onMouseEnter={handleShowTooltip} onMouseLeave={handleHideTooltip}>
|
||||
<DraggableElement
|
||||
nodeRef={nodeRef}
|
||||
axis='x'
|
||||
grid={[sliderRect.width / 100, 0]}
|
||||
bounds={{ left: 0, right: sliderRect.width }}
|
||||
positionOffset={{ x: (props.value / 100) * -12, y: 0 }}
|
||||
onDrag={handleDrag}
|
||||
onStop={() => setIsDragging(false)}
|
||||
position={{ x: (sliderRect.width / 100) * props.value, y: 0 }}
|
||||
>
|
||||
<div ref={nodeRef} className='absolute z-20 leading-3'>
|
||||
<div
|
||||
className={
|
||||
'z-20 h-3 w-3 rotate-45 hover:cursor-pointer rounded-xs border-[2px] border-white bg-martian-red'
|
||||
}
|
||||
/>
|
||||
{(showTooltip || isDragging) && (
|
||||
<div className='absolute -top-8 left-1/2 -translate-x-1/2 rounded-xs bg-martian-red px-2 py-[2px] text-xs'>
|
||||
<OverlayMark className='absolute h-2 -translate-x-1/2 -bottom-2 left-1/2 -z-1 text-martian-red' />
|
||||
{props.value.toFixed(0)}%
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DraggableElement>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface MarkProps {
|
||||
value: number
|
||||
sliderValue: number
|
||||
onClick: (value: number) => void
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
function Mark(props: MarkProps) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => props.onClick(props.value)}
|
||||
className={`z-20 h-3 w-3 rotate-45 rounded-xs border-[1px] border-white/20 hover:border-[2px] hover:border-white ${
|
||||
props.sliderValue >= props.value && !props.disabled
|
||||
? 'bg-martian-red hover:border-white'
|
||||
: 'bg-grey-medium'
|
||||
}`}
|
||||
disabled={props.disabled}
|
||||
></button>
|
||||
)
|
||||
}
|
||||
|
||||
interface TrackProps {
|
||||
maxValue: number
|
||||
sliderValue: number
|
||||
}
|
||||
|
||||
function Track(props: TrackProps) {
|
||||
const minValue = props.maxValue - 21
|
||||
let percentage = 0
|
||||
|
||||
if (props.sliderValue >= props.maxValue) percentage = 100
|
||||
|
||||
if (props.sliderValue > minValue && props.sliderValue < props.maxValue) {
|
||||
percentage = ((props.sliderValue - minValue) / (props.maxValue - minValue)) * 100
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='relative flex-1 h-1 overflow-hidden bg-transparent rounded-sm'>
|
||||
<div className='absolute h-3 z-1 bg-martian-red ' style={{ width: `${percentage}%` }} />
|
||||
<div className='absolute w-full h-3 bg-white/20' />
|
||||
</div>
|
||||
)
|
||||
}
|
26
src/components/Slider/LeverageLabel.tsx
Normal file
26
src/components/Slider/LeverageLabel.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Text from 'components/Text'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
decimals: number
|
||||
leverage: number
|
||||
style?: {}
|
||||
}
|
||||
|
||||
export default function LeverageLabel(props: Props) {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'flex flex-col gap-1 items-center',
|
||||
'transition-opacity duration-100',
|
||||
props.className,
|
||||
)}
|
||||
style={props.style}
|
||||
>
|
||||
<div className={classNames('h-2.5 w-[1px] border-[0.5px] border-white/20')} />
|
||||
<Text className='text-xs text-white/50'>{props.leverage.toFixed(props.decimals)}x</Text>
|
||||
</div>
|
||||
)
|
||||
}
|
23
src/components/Slider/Mark.tsx
Normal file
23
src/components/Slider/Mark.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean
|
||||
onClick: (value: number) => void
|
||||
sliderValue: number
|
||||
style?: {}
|
||||
value: number
|
||||
}
|
||||
|
||||
export default function Mark(props: Props) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => props.onClick(props.value)}
|
||||
className={classNames(
|
||||
'z-20 h-2 w-2 rotate-45 rounded-xs border-[1px] border-white/20 hover:border-[1px] hover:border-white',
|
||||
(props.sliderValue < props.value || props.disabled) && '!bg-grey-medium',
|
||||
)}
|
||||
style={props.style}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
)
|
||||
}
|
32
src/components/Slider/Track.tsx
Normal file
32
src/components/Slider/Track.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
interface Props {
|
||||
bg: string
|
||||
maxValue: number
|
||||
sliderValue: number
|
||||
}
|
||||
|
||||
export default function Track(props: Props) {
|
||||
const minValue = props.maxValue - 21
|
||||
let percentage = 0
|
||||
|
||||
if (props.sliderValue >= props.maxValue) percentage = 100
|
||||
|
||||
if (props.sliderValue > minValue && props.sliderValue < props.maxValue) {
|
||||
percentage = ((props.sliderValue - minValue) / (props.maxValue - minValue)) * 100
|
||||
}
|
||||
return (
|
||||
<div className='relative flex-1 h-1 bg-white/20 rounded-sm w-1'>
|
||||
<div
|
||||
className={classNames(
|
||||
'h-1 z-1 rounded-sm w-1',
|
||||
'before:absolute',
|
||||
'before:top-0 before:bottom-0 before:right-0 before:left-0',
|
||||
percentage > 0 && props.bg,
|
||||
percentage > 0 && 'slider-mask',
|
||||
)}
|
||||
style={{ width: `${percentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
222
src/components/Slider/index.tsx
Normal file
222
src/components/Slider/index.tsx
Normal file
@ -0,0 +1,222 @@
|
||||
import classNames from 'classnames'
|
||||
import { ChangeEvent, useRef, useState } from 'react'
|
||||
import Draggable from 'react-draggable'
|
||||
|
||||
import { OverlayMark } from 'components/Icons'
|
||||
import LeverageLabel from 'components/Slider/LeverageLabel'
|
||||
import Mark from 'components/Slider/Mark'
|
||||
import Track from 'components/Slider/Track'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
|
||||
const colors = {
|
||||
'1': '#897E83',
|
||||
'2': '#BD8898',
|
||||
'3': '#DB83A5',
|
||||
'4': '#B5469B',
|
||||
'5': '#920D92',
|
||||
}
|
||||
|
||||
type Props = {
|
||||
value: number
|
||||
onChange: (value: number) => void
|
||||
leverage?: {
|
||||
current: number
|
||||
max: number
|
||||
}
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export default function Slider(props: Props) {
|
||||
const [showTooltip, setShowTooltip] = useToggle()
|
||||
const [sliderRect, setSliderRect] = useState({ width: 0, left: 0, right: 0 })
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const nodeRef = useRef(null)
|
||||
const [isDragging, setIsDragging] = useToggle()
|
||||
|
||||
function handleSliderRect() {
|
||||
const leftCap = ref.current?.getBoundingClientRect().left ?? 0
|
||||
const rightCap = ref.current?.getBoundingClientRect().right ?? 0
|
||||
const newSliderWidth = ref.current?.getBoundingClientRect().width ?? 0
|
||||
|
||||
if (
|
||||
sliderRect.width !== newSliderWidth ||
|
||||
leftCap !== sliderRect.left ||
|
||||
rightCap !== sliderRect.right
|
||||
) {
|
||||
setSliderRect({
|
||||
left: leftCap,
|
||||
right: rightCap,
|
||||
width: newSliderWidth,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function handleDrag(e: any) {
|
||||
if (!isDragging) {
|
||||
setIsDragging(true)
|
||||
}
|
||||
|
||||
const current: number = e.clientX
|
||||
|
||||
if (current < sliderRect.left) {
|
||||
props.onChange(0)
|
||||
return
|
||||
}
|
||||
|
||||
if (current > sliderRect.right) {
|
||||
props.onChange(100)
|
||||
return
|
||||
}
|
||||
|
||||
const value = Math.round(((current - sliderRect.left) / sliderRect.width) * 100)
|
||||
|
||||
if (value !== props.value) {
|
||||
props.onChange(value)
|
||||
}
|
||||
}
|
||||
|
||||
function handleSliderClick(e: ChangeEvent<HTMLInputElement>) {
|
||||
props.onChange(Number(e.target.value))
|
||||
}
|
||||
|
||||
function handleShowTooltip() {
|
||||
setShowTooltip(true)
|
||||
}
|
||||
|
||||
function handleHideTooltip() {
|
||||
setShowTooltip(false)
|
||||
}
|
||||
|
||||
function getActiveIndex() {
|
||||
if (props.value >= 100) return '5'
|
||||
if (props.value >= 75) return '4'
|
||||
if (props.value >= 50) return '3'
|
||||
if (props.value >= 25) return '2'
|
||||
return '1'
|
||||
}
|
||||
|
||||
const DraggableElement: any = Draggable
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
ref={ref}
|
||||
className={classNames(
|
||||
'relative min-h-3 w-full transition-opacity',
|
||||
props.className,
|
||||
props.disabled && 'pointer-events-none',
|
||||
)}
|
||||
onMouseEnter={handleSliderRect}
|
||||
>
|
||||
<input
|
||||
type='range'
|
||||
value={props.value}
|
||||
onChange={handleSliderClick}
|
||||
onMouseDown={handleShowTooltip}
|
||||
className='absolute z-2 w-full hover:cursor-pointer appearance-none bg-transparent [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:appearance-none'
|
||||
/>
|
||||
<div className='absolute flex items-center w-full gap-1.5'>
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={0}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
style={{ backgroundColor: colors['1'] }}
|
||||
/>
|
||||
<Track maxValue={23} sliderValue={props.value} bg='before:gradient-slider-1' />
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={25}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
style={{ backgroundColor: colors['2'] }}
|
||||
/>
|
||||
<Track maxValue={48} sliderValue={props.value} bg='before:gradient-slider-2' />
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={50}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
style={{ backgroundColor: colors['3'] }}
|
||||
/>
|
||||
<Track maxValue={73} sliderValue={props.value} bg='before:gradient-slider-3' />
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={75}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
style={{ backgroundColor: colors['4'] }}
|
||||
/>
|
||||
<Track maxValue={98} sliderValue={props.value} bg='before:gradient-slider-4' />
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={100}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
style={{ backgroundColor: colors['5'] }}
|
||||
/>
|
||||
</div>
|
||||
{!props.disabled && (
|
||||
<div onMouseEnter={handleShowTooltip} onMouseLeave={handleHideTooltip}>
|
||||
<DraggableElement
|
||||
nodeRef={nodeRef}
|
||||
axis='x'
|
||||
grid={[sliderRect.width / 100, 0]}
|
||||
bounds={{ left: 0, right: sliderRect.width }}
|
||||
positionOffset={{ x: (props.value / 100) * -12, y: 0 }}
|
||||
onDrag={handleDrag}
|
||||
onStop={() => setIsDragging(false)}
|
||||
position={{ x: (sliderRect.width / 100) * props.value, y: -2 }}
|
||||
>
|
||||
<div ref={nodeRef} className='absolute z-20 leading-3'>
|
||||
<div
|
||||
className={classNames(
|
||||
'z-20 h-3 w-3 rotate-45 hover:cursor-pointer rounded-xs border-[2px] border-white',
|
||||
)}
|
||||
style={{ background: colors[getActiveIndex()] }}
|
||||
/>
|
||||
{props.leverage ? (
|
||||
<div className='pt-2.5'>
|
||||
<LeverageLabel
|
||||
leverage={props.leverage.current}
|
||||
decimals={1}
|
||||
className={props.leverage.current >= 10 ? '-translate-x-2' : '-translate-x-1'}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
(showTooltip || isDragging) && (
|
||||
<div className='absolute -top-8 left-1/2 -translate-x-1/2 rounded-xs bg-fuchsia px-2 py-[2px] text-xs'>
|
||||
<OverlayMark
|
||||
className={classNames(
|
||||
'absolute h-2 -translate-x-1/2 -bottom-2 left-1/2 -z-1 text-fuchsia',
|
||||
)}
|
||||
/>
|
||||
{props.value.toFixed(0)}%
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</DraggableElement>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{props.leverage && (
|
||||
<div className='pt-2 flex justify-between'>
|
||||
<LeverageLabel
|
||||
leverage={1}
|
||||
decimals={0}
|
||||
className='-translate-x-0.5'
|
||||
style={{ opacity: props.value < 5 ? 0 : 1 }}
|
||||
/>
|
||||
<LeverageLabel
|
||||
leverage={props.leverage.max || 1}
|
||||
decimals={0}
|
||||
className='translate-x-1.5'
|
||||
style={{ opacity: props.value > 95 ? 0 : 1 }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
@ -19,6 +19,10 @@ interface Props {
|
||||
hasSelect?: boolean
|
||||
maxText?: string
|
||||
onChangeAsset?: (asset: Asset) => void
|
||||
leverage?: {
|
||||
current: number
|
||||
max: number
|
||||
}
|
||||
}
|
||||
|
||||
export default function TokenInputWithSlider(props: Props) {
|
||||
@ -71,6 +75,7 @@ export default function TokenInputWithSlider(props: Props) {
|
||||
value={percentage || 0}
|
||||
onChange={(value) => onChangeSlider(value)}
|
||||
disabled={props.disabled}
|
||||
leverage={props.leverage}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
@ -44,7 +44,8 @@ export const Tooltip = (props: Props) => {
|
||||
className={props.contentClassName}
|
||||
/>
|
||||
)}
|
||||
{...props}
|
||||
onClickOutside={props.onClickOutside}
|
||||
visible={props.visible}
|
||||
>
|
||||
{props.children ? (
|
||||
<span
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
import useVaults from 'hooks/useVaults'
|
||||
import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { cloneAccount } from 'utils/accounts'
|
||||
import { calculateAccountLeverage, cloneAccount } from 'utils/accounts'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { getCoinAmount, getCoinValue } from 'utils/formatters'
|
||||
import { getValueFromBNCoins } from 'utils/helpers'
|
||||
@ -30,6 +30,7 @@ export function useUpdatedAccount(account?: Account) {
|
||||
const [updatedAccount, setUpdatedAccount] = useState<Account | undefined>(
|
||||
account ? cloneAccount(account) : undefined,
|
||||
)
|
||||
|
||||
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
|
||||
const [addedDeposits, addDeposits] = useState<BNCoin[]>([])
|
||||
const [removedDeposits, removeDeposits] = useState<BNCoin[]>([])
|
||||
@ -39,7 +40,7 @@ export function useUpdatedAccount(account?: Account) {
|
||||
const [addedLends, addLends] = useState<BNCoin[]>([])
|
||||
const [removedLends, removeLends] = useState<BNCoin[]>([])
|
||||
const [addedTrades, addTrades] = useState<BNCoin[]>([])
|
||||
|
||||
const [leverage, setLeverage] = useState<number>(0)
|
||||
const removeDepositAndLendsByDenom = useCallback(
|
||||
(denom: string) => {
|
||||
if (!account) return
|
||||
@ -165,6 +166,16 @@ export function useUpdatedAccount(account?: Account) {
|
||||
[prices],
|
||||
)
|
||||
|
||||
const simulateHlsStakingWithdraw = useCallback(
|
||||
(collateralDenom: string, debtDenom: string, repayAmount: BigNumber) => {
|
||||
const repayValue = getCoinValue(BNCoin.fromDenomAndBigNumber(debtDenom, repayAmount), prices)
|
||||
const removeDepositAmount = getCoinAmount(collateralDenom, repayValue, prices)
|
||||
removeDeposits([BNCoin.fromDenomAndBigNumber(collateralDenom, removeDepositAmount)])
|
||||
removeDebts([BNCoin.fromDenomAndBigNumber(debtDenom, repayAmount)])
|
||||
},
|
||||
[prices],
|
||||
)
|
||||
|
||||
const simulateVaultDeposit = useCallback(
|
||||
(address: string, coins: BNCoin[], borrowCoins: BNCoin[]) => {
|
||||
if (!account) return
|
||||
@ -205,6 +216,7 @@ export function useUpdatedAccount(account?: Account) {
|
||||
accountCopy.lends = addCoins(addedLends, [...accountCopy.lends])
|
||||
accountCopy.lends = removeCoins(removedLends, [...accountCopy.lends])
|
||||
setUpdatedAccount(accountCopy)
|
||||
setLeverage(calculateAccountLeverage(accountCopy, prices).toNumber())
|
||||
useStore.setState({ updatedAccount: accountCopy })
|
||||
|
||||
return () => useStore.setState({ updatedAccount: undefined })
|
||||
@ -235,12 +247,14 @@ export function useUpdatedAccount(account?: Account) {
|
||||
addedDeposits,
|
||||
addedDebts,
|
||||
addedLends,
|
||||
leverage,
|
||||
removedDeposits,
|
||||
removedDebts,
|
||||
removedLends,
|
||||
simulateBorrow,
|
||||
simulateDeposits,
|
||||
simulateHlsStakingDeposit,
|
||||
simulateHlsStakingWithdraw,
|
||||
simulateLending,
|
||||
simulateRepay,
|
||||
simulateTrade,
|
||||
|
@ -1,15 +1,13 @@
|
||||
import Tab from 'components/Earn/Tab'
|
||||
import ActiveStakingAccounts from 'components/HLS/Staking/ActiveStakingAccounts'
|
||||
import AvailableHlsStakingAssets from 'components/HLS/Staking/AvailableHLSStakingAssets'
|
||||
import HLSStakingIntro from 'components/HLS/Staking/HLSStakingIntro'
|
||||
import MigrationBanner from 'components/MigrationBanner'
|
||||
import { HLS_TABS } from 'constants/pages'
|
||||
|
||||
export default function HLSStakingPage() {
|
||||
return (
|
||||
<div className='flex flex-wrap w-full gap-6'>
|
||||
<MigrationBanner />
|
||||
<Tab tabs={HLS_TABS} activeTabIdx={1} />
|
||||
{/*<Tab tabs={HLS_TABS} activeTabIdx={1} />*/}
|
||||
<HLSStakingIntro />
|
||||
<AvailableHlsStakingAssets />
|
||||
<ActiveStakingAccounts />
|
||||
|
@ -232,6 +232,28 @@ export default function createBroadcastSlice(
|
||||
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
changeHlsStakingLeverage: 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: `Changed Leverage`,
|
||||
},
|
||||
})
|
||||
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
closeHlsStakingPosition: async (options: { accountId: string; actions: Action[] }) => {
|
||||
const msg: CreditManagerExecuteMsg = {
|
||||
update_credit_account: {
|
||||
@ -252,10 +274,7 @@ export default function createBroadcastSlice(
|
||||
},
|
||||
})
|
||||
|
||||
const response_1 = await response
|
||||
return response_1.result
|
||||
? getSingleValueFromBroadcastResult(response_1.result, 'wasm', 'token_id')
|
||||
: null
|
||||
return response.then((response) => !!response.result)
|
||||
},
|
||||
|
||||
createAccount: async (accountKind: AccountKind) => {
|
||||
|
6
src/types/interfaces/store/broadcast.d.ts
vendored
6
src/types/interfaces/store/broadcast.d.ts
vendored
@ -91,11 +91,9 @@ interface BroadcastSlice {
|
||||
coin: BNCoin
|
||||
borrowToWallet: boolean
|
||||
}) => Promise<boolean>
|
||||
changeHlsStakingLeverage: (options: { accountId: string; actions: Action[] }) => Promise<boolean>
|
||||
claimRewards: (options: { accountId: string }) => ExecutableTx
|
||||
closeHlsStakingPosition: (options: {
|
||||
accountId: string
|
||||
actions: Action[]
|
||||
}) => Promise<string | null>
|
||||
closeHlsStakingPosition: (options: { accountId: string; actions: Action[] }) => Promise<boolean>
|
||||
createAccount: (
|
||||
accountKind: import('types/generated/mars-rover-health-types/MarsRoverHealthTypes.types').AccountKind,
|
||||
) => Promise<string | null>
|
||||
|
68
src/utils/actions.ts
Normal file
68
src/utils/actions.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||
import { getCoinAmount, getCoinValue } from 'utils/formatters'
|
||||
|
||||
export function getHlsStakingChangeLevActions(
|
||||
previousAmount: BigNumber,
|
||||
currentAmount: BigNumber,
|
||||
collateralDenom: string,
|
||||
borrowDenom: string,
|
||||
slippage: number,
|
||||
prices: BNCoin[],
|
||||
): Action[] {
|
||||
let actions: Action[] = []
|
||||
|
||||
if (currentAmount.isLessThan(previousAmount)) {
|
||||
const debtValue = getCoinValue(
|
||||
BNCoin.fromDenomAndBigNumber(borrowDenom, previousAmount.minus(currentAmount)),
|
||||
prices,
|
||||
)
|
||||
const collateralAmount = getCoinAmount(collateralDenom, debtValue, prices)
|
||||
|
||||
actions = [
|
||||
{
|
||||
swap_exact_in: {
|
||||
coin_in: BNCoin.fromDenomAndBigNumber(collateralDenom, collateralAmount).toActionCoin(),
|
||||
denom_out: borrowDenom,
|
||||
slippage: slippage.toString(),
|
||||
},
|
||||
},
|
||||
{
|
||||
repay: {
|
||||
coin: BNCoin.fromDenomAndBigNumber(
|
||||
borrowDenom,
|
||||
previousAmount
|
||||
.minus(currentAmount)
|
||||
.times(1 - slippage)
|
||||
.integerValue(),
|
||||
).toActionCoin(),
|
||||
},
|
||||
},
|
||||
{
|
||||
withdraw: BNCoin.fromDenomAndBigNumber(borrowDenom, BN_ZERO).toActionCoin(true),
|
||||
},
|
||||
]
|
||||
} else {
|
||||
actions = [
|
||||
{
|
||||
borrow: BNCoin.fromDenomAndBigNumber(
|
||||
borrowDenom,
|
||||
currentAmount.minus(previousAmount),
|
||||
).toCoin(),
|
||||
},
|
||||
{
|
||||
swap_exact_in: {
|
||||
denom_out: collateralDenom,
|
||||
coin_in: BNCoin.fromDenomAndBigNumber(
|
||||
borrowDenom,
|
||||
currentAmount.minus(previousAmount),
|
||||
).toActionCoin(true),
|
||||
slippage: slippage.toString(),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
@ -88,6 +88,7 @@ module.exports = {
|
||||
chart: '#220e1d',
|
||||
error: '#F04438',
|
||||
'error-bg': '#FDA29B',
|
||||
fuchsia: '#B7439F',
|
||||
green: '#039855',
|
||||
grey: '#908e91',
|
||||
'grey-dark': '#1a1c25',
|
||||
@ -352,6 +353,18 @@ module.exports = {
|
||||
'.gradient-secondary-to-primary': {
|
||||
background: 'linear-gradient(180deg, #926AC8 100%, #7F78E8 0%)',
|
||||
},
|
||||
'.gradient-slider-1': {
|
||||
background: 'linear-gradient(to right, #8D7F85, #B78796)',
|
||||
},
|
||||
'.gradient-slider-2': {
|
||||
background: 'linear-gradient(to right, #C08899, #E08AA6)',
|
||||
},
|
||||
'.gradient-slider-3': {
|
||||
background: 'linear-gradient(to right, #D97FA4, #B84A9C)',
|
||||
},
|
||||
'.gradient-slider-4': {
|
||||
background: 'linear-gradient(to right, #961293, #B3419B)',
|
||||
},
|
||||
'.gradient-tooltip': {
|
||||
background:
|
||||
'linear-gradient(77.47deg, rgba(20, 24, 57, 0.9) 11.58%, rgba(34, 16, 57, 0.9) 93.89%)',
|
||||
@ -364,6 +377,9 @@ module.exports = {
|
||||
whiteSpace: 'nowrap',
|
||||
fontFeatureSettings: '"tnum" on',
|
||||
},
|
||||
'.slider-mask': {
|
||||
mask: 'linear-gradient(#fff 0 0)',
|
||||
},
|
||||
'.text-3xs': { fontSize: '9px', lineHeight: '12px' },
|
||||
'.text-3xs-caps': {
|
||||
fontSize: '9px',
|
||||
|
Loading…
Reference in New Issue
Block a user