mirror of
https://github.com/cerc-io/mars-interface.git
synced 2024-12-22 20:27:44 +00:00
commit
e769b42cf5
@ -26,22 +26,22 @@ const moduleExports = {
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/farm/vault/:address/edit',
|
||||
source: '/farm/vault/:address/account/:id/edit',
|
||||
destination: '/farm/',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/farm/vault/:address/unlock',
|
||||
source: '/farm/vault/:address/account/:id/unlock',
|
||||
destination: '/farm',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/farm/vault/:address/close',
|
||||
source: '/farm/vault/:address/account/:id/close',
|
||||
destination: '/farm',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/farm/vault/:address/repay',
|
||||
source: '/farm/vault/:address/account/:id/repay',
|
||||
destination: '/farm',
|
||||
permanent: true,
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mars",
|
||||
"homepage": "./",
|
||||
"version": "1.4.6",
|
||||
"version": "1.4.7",
|
||||
"license": "SEE LICENSE IN LICENSE FILE",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
|
@ -18,10 +18,10 @@ import {
|
||||
useUsdPrice,
|
||||
useUserBalance,
|
||||
useUserDebt,
|
||||
useUserDeposit,
|
||||
useUserIcns,
|
||||
} from 'hooks/queries'
|
||||
import { useSpotPrice } from 'hooks/queries/useSpotPrice'
|
||||
import { useUserCollaterals } from 'hooks/queries/useUserCollaterals'
|
||||
import { ReactNode, useEffect, useState } from 'react'
|
||||
import useStore from 'store'
|
||||
import { State } from 'types/enums'
|
||||
@ -155,8 +155,8 @@ export const CommonContainer = ({ children }: CommonContainerProps) => {
|
||||
useRedBank()
|
||||
useUserBalance()
|
||||
useUserIcns()
|
||||
useUserDeposit()
|
||||
useUserDebt()
|
||||
useUserCollaterals()
|
||||
useMarsOracle()
|
||||
useSpotPrice(MARS_SYMBOL)
|
||||
useUsdPrice()
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { useWalletManager, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
|
||||
import classNames from 'classnames'
|
||||
import { Footer, Header, MobileNav } from 'components/common'
|
||||
import { Footer, Header, MobileNav, TermsOfService } from 'components/common'
|
||||
import { FieldsNotConnected } from 'components/fields'
|
||||
import { RedbankNotConnected } from 'components/redbank'
|
||||
import { SESSION_WALLET_KEY } from 'constants/appConstants'
|
||||
import { SESSION_WALLET_KEY, TERMS_OF_SERVICE } from 'constants/appConstants'
|
||||
import { useAnimations } from 'hooks/data'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useEffect } from 'react'
|
||||
@ -14,6 +14,9 @@ type Props = {
|
||||
}
|
||||
|
||||
export const Layout = ({ children }: Props) => {
|
||||
const alreadyAcceptedTOS = localStorage.getItem(TERMS_OF_SERVICE)
|
||||
const currentlyAcceptedROS = useStore((s) => s.acceptedTermsOfService)
|
||||
|
||||
const router = useRouter()
|
||||
const { status } = useWalletManager()
|
||||
useAnimations()
|
||||
@ -35,6 +38,8 @@ export const Layout = ({ children }: Props) => {
|
||||
|
||||
return (
|
||||
<div className={classNames('app', !enableAnimations && 'no-motion')}>
|
||||
{alreadyAcceptedTOS || currentlyAcceptedROS ? null : <TermsOfService />}
|
||||
|
||||
<div className={backgroundClasses} id='bg' />
|
||||
<Header />
|
||||
<div className='appContainer'>
|
||||
|
@ -0,0 +1,44 @@
|
||||
@import 'src/styles/master';
|
||||
|
||||
.container {
|
||||
backdrop-filter: blur(rem-calc(50));
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
||||
.card {
|
||||
max-width: min(rem-calc(500), 95vw);
|
||||
@include padding(2, 4, 4, 4);
|
||||
|
||||
.subtitle {
|
||||
text-align: center;
|
||||
@include padding(4, 0, 0, 0);
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
align-items: baseline;
|
||||
@include margin(4, 0);
|
||||
}
|
||||
|
||||
.btn {
|
||||
@include margin(6, 0, 0);
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: $bpXSmallLow) {
|
||||
.container {
|
||||
.card {
|
||||
@include padding(2, 12, 12, 12);
|
||||
}
|
||||
}
|
||||
}
|
60
src/components/common/TermsOfService/TermsOfService.tsx
Normal file
60
src/components/common/TermsOfService/TermsOfService.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
import classNames from 'classnames'
|
||||
import { Button, Card, Checkbox } from 'components/common'
|
||||
import { TERMS_OF_SERVICE } from 'constants/appConstants'
|
||||
import { useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import useStore from 'store'
|
||||
import { DocURL } from 'types/enums/docURL'
|
||||
|
||||
import styles from './TermsOfService.module.scss'
|
||||
|
||||
export const TermsOfService = () => {
|
||||
const { t } = useTranslation()
|
||||
const [isFirstAccepted, setIsFirstAccepted] = useState(false)
|
||||
const [isSecondAccepted, setIsSecondAccepted] = useState(false)
|
||||
|
||||
const onTermsConfirmed = () => {
|
||||
if (!isFirstAccepted && !isSecondAccepted) return
|
||||
|
||||
localStorage.setItem(TERMS_OF_SERVICE, 'true')
|
||||
useStore.setState({ acceptedTermsOfService: true })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Card title='Disclaimer' className={styles.card}>
|
||||
<div className={classNames('xs', styles.subtitle)}>
|
||||
<Trans i18nKey='common.termsOfService.intro'>
|
||||
<span className={classNames('faded xs')}>
|
||||
Please check the boxes below to confirm your agreement to the{' '}
|
||||
</span>
|
||||
<a
|
||||
href={DocURL.TERMS_OF_SERVICE_URL}
|
||||
rel='noreferrer'
|
||||
target='_blank'
|
||||
className={classNames('xs')}
|
||||
>
|
||||
Mars Protocol Terms and Conditions
|
||||
</a>
|
||||
</Trans>
|
||||
</div>
|
||||
<Checkbox
|
||||
className={styles.checkbox}
|
||||
onChecked={(isChecked) => setIsFirstAccepted(isChecked)}
|
||||
text={t('common.termsOfService.term1')}
|
||||
/>
|
||||
<Checkbox
|
||||
className={styles.checkbox}
|
||||
onChecked={(isChecked) => setIsSecondAccepted(isChecked)}
|
||||
text={t('common.termsOfService.term2')}
|
||||
/>
|
||||
<Button
|
||||
onClick={onTermsConfirmed}
|
||||
text={t('common.confirm')}
|
||||
disabled={!isFirstAccepted || !isSecondAccepted}
|
||||
className={styles.btn}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -6,6 +6,23 @@
|
||||
@include layoutTooltip;
|
||||
min-width: rem-calc(280);
|
||||
|
||||
.border {
|
||||
@include devider20;
|
||||
}
|
||||
|
||||
.list {
|
||||
padding-inline-start: 1rem;
|
||||
font-style: italic;
|
||||
@include padding(0, 0, 1);
|
||||
@include typoXS;
|
||||
|
||||
.listItem {
|
||||
list-style-type: circle;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
@include padding(1, 0);
|
||||
display: flex;
|
||||
|
@ -5,33 +5,45 @@ import { useTranslation } from 'react-i18next'
|
||||
|
||||
import styles from './Apy.module.scss'
|
||||
|
||||
interface VaultRate {
|
||||
total: number
|
||||
borrow: number
|
||||
}
|
||||
|
||||
interface Props {
|
||||
apyData: VaultRate
|
||||
apyData: ApyBreakdown | PositionApyBreakdown
|
||||
borrowRate: number
|
||||
leverage: number
|
||||
}
|
||||
|
||||
export const Apy = ({ apyData, leverage }: Props) => {
|
||||
export const Apy = ({ apyData, leverage, borrowRate }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const totalApy = useMemo(() => apyData.total * leverage - apyData.borrow, [apyData, leverage])
|
||||
const leveragedApy = useMemo(() => apyData.total * leverage, [apyData, leverage])
|
||||
const totalApy = useMemo(
|
||||
() => (apyData.total ?? 0) * leverage - borrowRate ?? 0,
|
||||
[apyData, leverage],
|
||||
)
|
||||
const leveragedApy = useMemo(() => (apyData.total ?? 0) * leverage, [apyData, leverage])
|
||||
const performanceFee = apyData.fees && apyData.fees[0].value > 0 ? apyData.fees[0] : null
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<p className='sub2'>{t('fields.apyBreakdown')}</p>
|
||||
<div className={classNames(styles.border)}>
|
||||
<div className={classNames(styles.item, styles.total)}>
|
||||
<span className={styles.label}>{t('fields.vaultApy')}</span>
|
||||
<span className={styles.value}>
|
||||
{formatValue(apyData.total ?? 0, 2, 2, true, false, '%', true)}
|
||||
</span>
|
||||
</div>
|
||||
<ul className={styles.list}>
|
||||
{apyData.apys
|
||||
?.filter((apy) => apy.value > 0.009)
|
||||
.map((item, index) => (
|
||||
<li className={styles.listItem} key={index}>
|
||||
<span>- {item.type}</span>
|
||||
<span>{formatValue(item.value, 2, 2, true, false, '%', true)}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{leverage > 1 && (
|
||||
<>
|
||||
<div className={classNames(styles.item, styles.total, styles.border)}>
|
||||
<span className={styles.label}>{t('fields.vaultApy')}</span>
|
||||
<span className={styles.value}>
|
||||
{formatValue(apyData.total, 2, 2, true, false, '%', true)}
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.item}>
|
||||
<span className={styles.label}>
|
||||
{t('fields.leveragedApy', {
|
||||
@ -42,11 +54,19 @@ export const Apy = ({ apyData, leverage }: Props) => {
|
||||
{formatValue(leveragedApy, 2, 2, true, false, '%', true)}
|
||||
</span>
|
||||
</div>
|
||||
{apyData.borrow > 0 && (
|
||||
<div className={classNames(styles.item, styles.border)}>
|
||||
{borrowRate > 0 && (
|
||||
<div className={classNames(styles.item, !performanceFee && styles.border)}>
|
||||
<span className={styles.label}>{t('fields.borrowRateApy')}</span>
|
||||
<span className={styles.value}>
|
||||
{formatValue(apyData.borrow, 2, 2, true, '-', '%', true)}
|
||||
{formatValue(borrowRate, 2, 2, true, '-', '%', true)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{performanceFee && (
|
||||
<div className={classNames(styles.item, styles.border)}>
|
||||
<span className={styles.label}>{performanceFee.type}</span>
|
||||
<span className={styles.value}>
|
||||
{formatValue(performanceFee.value, 2, 2, true, '-', '%', true)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
@ -30,6 +30,7 @@ export { MobileNav } from './MobileNav/MobileNav'
|
||||
export { Notification } from './Notification/Notification'
|
||||
export { NumberInput } from './NumberInput/NumberInput'
|
||||
export { SVG } from './SVG/SVG'
|
||||
export { TermsOfService } from './TermsOfService/TermsOfService'
|
||||
export { TextTooltip } from './TextTooltip/TextTooltip'
|
||||
export { Title } from './Title/Title'
|
||||
export { Toggle } from './Toggle/Toggle'
|
||||
|
@ -32,6 +32,7 @@ export const EditContent = (props: Props) => {
|
||||
denom: props.vault.denoms.primary,
|
||||
amount: primaryAmount.toString(),
|
||||
}}
|
||||
maxDecimals={6}
|
||||
className={styles.marginRight}
|
||||
showSymbol
|
||||
/>
|
||||
@ -43,6 +44,7 @@ export const EditContent = (props: Props) => {
|
||||
denom: props.vault.denoms.secondary,
|
||||
amount: secondaryAmount.toString(),
|
||||
}}
|
||||
maxDecimals={6}
|
||||
className={styles.marginRight}
|
||||
showSymbol
|
||||
/>
|
||||
@ -54,6 +56,7 @@ export const EditContent = (props: Props) => {
|
||||
denom: props.position.borrowDenom || props.vault.denoms.secondary,
|
||||
amount: borrowedAmount.toString(),
|
||||
}}
|
||||
maxDecimals={6}
|
||||
className={styles.marginRight}
|
||||
showSymbol
|
||||
/>
|
||||
@ -113,6 +116,7 @@ export const EditContent = (props: Props) => {
|
||||
amount: (difference / 2).toString(),
|
||||
}).toString(),
|
||||
}}
|
||||
maxDecimals={6}
|
||||
className={styles.marginRight}
|
||||
showSymbol
|
||||
/>
|
||||
@ -125,6 +129,7 @@ export const EditContent = (props: Props) => {
|
||||
amount: (difference / 2).toString(),
|
||||
}).toString(),
|
||||
}}
|
||||
maxDecimals={6}
|
||||
className={styles.marginRight}
|
||||
showSymbol
|
||||
/>
|
||||
|
@ -21,6 +21,7 @@ export const RepayContent = (props: Props) => {
|
||||
denom: props.vault.denoms.secondary,
|
||||
amount: props.repayAmount.toString(),
|
||||
}}
|
||||
maxDecimals={6}
|
||||
showSymbol
|
||||
/>
|
||||
</li>
|
||||
@ -31,6 +32,7 @@ export const RepayContent = (props: Props) => {
|
||||
denom: props.vault.denoms.secondary,
|
||||
amount: props.repayAmount.toString(),
|
||||
}}
|
||||
maxDecimals={6}
|
||||
showSymbol
|
||||
/>
|
||||
</li>
|
||||
|
@ -26,6 +26,7 @@ export const UnlockContent = (props: Props) => {
|
||||
props.position.amounts.borrowedSecondary,
|
||||
).toString(),
|
||||
}}
|
||||
maxDecimals={6}
|
||||
showSymbol
|
||||
/>
|
||||
</>
|
||||
@ -38,6 +39,7 @@ export const UnlockContent = (props: Props) => {
|
||||
denom: props.vault.denoms.primary,
|
||||
amount: props.position.amounts.primary.toString(),
|
||||
}}
|
||||
maxDecimals={6}
|
||||
className={styles.marginRight}
|
||||
showSymbol
|
||||
/>
|
||||
@ -49,6 +51,7 @@ export const UnlockContent = (props: Props) => {
|
||||
denom: props.vault.denoms.secondary,
|
||||
amount: props.position.amounts.secondary.toString(),
|
||||
}}
|
||||
maxDecimals={6}
|
||||
showSymbol
|
||||
/>
|
||||
</>
|
||||
|
@ -52,13 +52,13 @@ export const ActiveVaultsTable = () => {
|
||||
const handleRowClick = (vault: ActiveVault) => {
|
||||
switch (vault.position.status) {
|
||||
case 'active':
|
||||
router.push(`/farm/vault/${vault.address}/edit`)
|
||||
router.push(`/farm/vault/${vault.address}/account/${vault.position.accountId}/edit`)
|
||||
return
|
||||
case 'unlocked':
|
||||
router.push(`/farm/vault/${vault.address}/close`)
|
||||
router.push(`/farm/vault/${vault.address}/account/${vault.position.accountId}/close`)
|
||||
return
|
||||
case 'unlocking':
|
||||
router.push(`/farm/vault/${vault.address}/repay`)
|
||||
router.push(`/farm/vault/${vault.address}/account/${vault.position.accountId}/repay`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -88,10 +88,8 @@ export const ActiveVaultsTableMobile = () => {
|
||||
}
|
||||
tooltip={
|
||||
<Apy
|
||||
apyData={{
|
||||
borrow: vault.position.apy.borrow,
|
||||
total: vault.position.apy.total,
|
||||
}}
|
||||
apyData={vault.position.apy}
|
||||
borrowRate={vault.position.apy.borrow}
|
||||
leverage={vault.position.currentLeverage}
|
||||
/>
|
||||
}
|
||||
|
@ -259,10 +259,6 @@ export const useActiveVaultsColumns = () => {
|
||||
if (row.original.position.apy?.net !== null) {
|
||||
const apy = new BigNumber(row.original.position.apy.net).toNumber()
|
||||
|
||||
const apyData = {
|
||||
total: row.original.apy || 0,
|
||||
borrow: row.original.position.apy.borrow,
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<TextTooltip
|
||||
@ -276,7 +272,11 @@ export const useActiveVaultsColumns = () => {
|
||||
</>
|
||||
}
|
||||
tooltip={
|
||||
<Apy apyData={apyData} leverage={row.original.position.currentLeverage} />
|
||||
<Apy
|
||||
apyData={row.original.apy}
|
||||
borrowRate={row.original.position.apy.borrow}
|
||||
leverage={row.original.position.currentLeverage}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
@ -374,7 +374,11 @@ export const useActiveVaultsColumns = () => {
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={() => router.push(`/farm/vault/${row.original.address}/${route}`)}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/farm/vault/${row.original.address}/account/${row.original.position.accountId}/${route}`,
|
||||
)
|
||||
}
|
||||
color='quaternary'
|
||||
prefix={prefix}
|
||||
variant='round'
|
||||
|
@ -30,17 +30,16 @@ export const AvailableVaultsTableMobile = () => {
|
||||
(asset) => asset.denom === vault.denoms.secondary,
|
||||
)
|
||||
const borrowRate = Math.min(
|
||||
Number(primaryBorrowAsset?.borrowRate ?? 0),
|
||||
Number(secondaryBorrowAsset?.borrowRate ?? 0),
|
||||
Number(primaryBorrowAsset?.borrowRate || 1000),
|
||||
Number(secondaryBorrowAsset?.borrowRate || 1000),
|
||||
)
|
||||
const maxBorrowRate = borrowRate * (ltvToLeverage(vault.ltv.contract) - 1)
|
||||
const minAPY = new BigNumber(vault.apy || 0).toNumber()
|
||||
const minAPY = new BigNumber(vault.apy.total || 0).toNumber()
|
||||
|
||||
const leverage = ltvToLeverage(vault.ltv.contract)
|
||||
const maxAPY =
|
||||
new BigNumber(minAPY).times(leverage).decimalPlaces(2).toNumber() - maxBorrowRate
|
||||
const apyDataNoLev = { total: vault.apy || 0, borrow: 0 }
|
||||
const apyDataLev = { total: vault.apy || 0, borrow: maxBorrowRate }
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={`${vault.address}-${i}`}
|
||||
@ -61,15 +60,16 @@ export const AvailableVaultsTableMobile = () => {
|
||||
<span>
|
||||
<TextTooltip
|
||||
hideStyling
|
||||
text={<AnimatedNumber amount={minAPY} suffix=' - ' />}
|
||||
tooltip={<Apy apyData={apyDataNoLev} leverage={1} />}
|
||||
text={<AnimatedNumber amount={Math.min(minAPY, maxAPY)} suffix=' - ' />}
|
||||
tooltip={<Apy apyData={vault.apy} borrowRate={0} leverage={1} />}
|
||||
/>
|
||||
<TextTooltip
|
||||
hideStyling
|
||||
text={<AnimatedNumber amount={maxAPY} suffix='%' />}
|
||||
text={<AnimatedNumber amount={Math.max(minAPY, maxAPY)} suffix='%' />}
|
||||
tooltip={
|
||||
<Apy
|
||||
apyData={apyDataLev}
|
||||
apyData={vault.apy}
|
||||
borrowRate={maxBorrowRate}
|
||||
leverage={ltvToLeverage(vault.ltv.contract)}
|
||||
/>
|
||||
}
|
||||
|
@ -103,18 +103,19 @@ export const useAvailableVaultsColumns = () => {
|
||||
|
||||
const maxBorrowRate = borrowRate * (ltvToLeverage(row.original.ltv.contract) - 1)
|
||||
|
||||
const minAPY = new BigNumber(row.original.apy).toNumber()
|
||||
const minAPY = row.original.apy.total ?? 0
|
||||
|
||||
const maxAPY = new BigNumber(minAPY).times(maxLeverage).toNumber() - maxBorrowRate
|
||||
const minDailyAPY = new BigNumber(convertApyToDailyApy(row.original.apy))
|
||||
|
||||
const minDailyAPY = new BigNumber(convertApyToDailyApy(minAPY))
|
||||
.decimalPlaces(2)
|
||||
.toNumber()
|
||||
const maxDailyAPY = new BigNumber(minDailyAPY)
|
||||
.times(maxLeverage)
|
||||
.decimalPlaces(2)
|
||||
.toNumber()
|
||||
const apyDataNoLev = { total: row.original.apy || 0, borrow: 0 }
|
||||
const apyDataLev = { total: row.original.apy || 0, borrow: maxBorrowRate }
|
||||
const apyDataNoLev = { ...row.original.apy }
|
||||
const apyDataLev = { ...row.original.apy }
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -122,26 +123,37 @@ export const useAvailableVaultsColumns = () => {
|
||||
hideStyling
|
||||
text={
|
||||
<AnimatedNumber
|
||||
amount={Number(formatValue(minAPY, 2, 2, true, false, false, true))}
|
||||
amount={Number(
|
||||
formatValue(Math.min(minAPY, maxAPY), 2, 2, true, false, false, true),
|
||||
)}
|
||||
/>
|
||||
}
|
||||
tooltip={<Apy apyData={apyDataNoLev} leverage={1} />}
|
||||
tooltip={<Apy apyData={apyDataNoLev} borrowRate={0} leverage={1} />}
|
||||
/>
|
||||
<span> - </span>
|
||||
<TextTooltip
|
||||
hideStyling
|
||||
text={
|
||||
<AnimatedNumber
|
||||
amount={Number(formatValue(maxAPY, 2, 2, true, false, false, true))}
|
||||
amount={Number(
|
||||
formatValue(Math.max(minAPY, maxAPY), 2, 2, true, false, false, true),
|
||||
)}
|
||||
suffix='%'
|
||||
/>
|
||||
}
|
||||
tooltip={
|
||||
<Apy apyData={apyDataLev} leverage={ltvToLeverage(row.original.ltv.contract)} />
|
||||
<Apy
|
||||
apyData={apyDataLev}
|
||||
borrowRate={maxBorrowRate}
|
||||
leverage={ltvToLeverage(row.original.ltv.contract)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<p className='s faded'>{`${minDailyAPY} - ${maxDailyAPY}%/${t('common.day')}`}</p>
|
||||
<p className='s faded'>{`${Math.min(minDailyAPY, maxDailyAPY)} - ${Math.max(
|
||||
minDailyAPY,
|
||||
maxDailyAPY,
|
||||
)}%/${t('common.day')}`}</p>
|
||||
</>
|
||||
)
|
||||
},
|
||||
|
@ -97,7 +97,7 @@ export const BreakdownTable = (props: Props) => {
|
||||
denom,
|
||||
amount: amount.toString(),
|
||||
}}
|
||||
maxDecimals={2}
|
||||
maxDecimals={6}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -225,12 +225,7 @@ export const BreakdownTable = (props: Props) => {
|
||||
)
|
||||
const trueBorrowRate = borrowRate * (Number(currentLeverage) - 1)
|
||||
|
||||
const apy = (props.vault.apy || 0) * currentLeverage - trueBorrowRate
|
||||
|
||||
const apyData = {
|
||||
total: props.vault.apy || 0,
|
||||
borrow: trueBorrowRate,
|
||||
}
|
||||
const apy = (props.vault.apy.total || 0) * currentLeverage - trueBorrowRate
|
||||
|
||||
return (
|
||||
<div className={containerClasses}>
|
||||
@ -251,7 +246,13 @@ export const BreakdownTable = (props: Props) => {
|
||||
abbreviated={false}
|
||||
/>
|
||||
}
|
||||
tooltip={<Apy apyData={apyData} leverage={currentLeverage} />}
|
||||
tooltip={
|
||||
<Apy
|
||||
apyData={props.vault.apy}
|
||||
borrowRate={trueBorrowRate}
|
||||
leverage={currentLeverage}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<Loading />
|
||||
|
@ -13,8 +13,8 @@ export const LiquidationNotification = () => {
|
||||
|
||||
const elligiblePositions = activeVaults.filter((vault) => vault.position.ltv > vault.ltv.contract)
|
||||
|
||||
const repayHandler = (address: string) => {
|
||||
router.push(`/farm/vault/${address}/repay`)
|
||||
const repayHandler = (address: string, accountId: string) => {
|
||||
router.push(`/farm/vault/${address}/account/${accountId}/repay`)
|
||||
return
|
||||
}
|
||||
|
||||
@ -33,7 +33,12 @@ export const LiquidationNotification = () => {
|
||||
})}
|
||||
</span>
|
||||
<Button
|
||||
onClick={() => repayHandler(elligiblePositions[0].address)}
|
||||
onClick={() =>
|
||||
repayHandler(
|
||||
elligiblePositions[0].address,
|
||||
elligiblePositions[0].position.accountId,
|
||||
)
|
||||
}
|
||||
color='tertiary'
|
||||
text={t('fields.notifications.liquidation.single.button')}
|
||||
></Button>
|
||||
|
@ -18,6 +18,7 @@
|
||||
@include typoXXS;
|
||||
border: none;
|
||||
min-height: unset;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Coin } from '@cosmjs/proto-signing'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import classNames from 'classnames'
|
||||
import { Button, DisplayCurrency, NumberInput } from 'components/common'
|
||||
import { findByDenom } from 'functions'
|
||||
@ -9,6 +10,8 @@ import useStore from 'store'
|
||||
|
||||
import styles from './TokenInput.module.scss'
|
||||
|
||||
BigNumber.config({ EXPONENTIAL_AT: [-24, 20] })
|
||||
|
||||
interface Props {
|
||||
tokens: string[]
|
||||
input: Input
|
||||
@ -70,7 +73,7 @@ export const TokenInput = (props: Props) => {
|
||||
color='quaternary'
|
||||
className={`xxsCaps faded ${styles.maxBtn}`}
|
||||
onClick={() => onValueEntered(maxAmount)}
|
||||
text={`${props.maxAmountLabel}: ${maxAmount / 10 ** asset.decimals}`}
|
||||
text={`${props.maxAmountLabel}: ${new BigNumber(maxAmount).shiftedBy(-1 * asset.decimals)}`}
|
||||
variant='transparent'
|
||||
/>
|
||||
<div className={styles.input}>
|
||||
|
@ -20,7 +20,9 @@ export const UnlockedNotification = () => {
|
||||
if (!vaultsUnlocked.length) return null
|
||||
|
||||
const exitVaultHandler = () => {
|
||||
router.push(`/farm/vault/${vaultsUnlocked[0].address}/close`)
|
||||
router.push(
|
||||
`/farm/vault/${vaultsUnlocked[0].address}/account/${vaultsUnlocked[0].position.accountId}/close`,
|
||||
)
|
||||
}
|
||||
|
||||
const unlockedContent = () => {
|
||||
|
@ -125,7 +125,7 @@ export const VAULT_CONFIGS: Vault[] = [
|
||||
secondary: 'USDC.n',
|
||||
},
|
||||
color: colors.usdc,
|
||||
lockup: 86400 * 14,
|
||||
lockup: 86400 * 1,
|
||||
provider: 'Apollo vault',
|
||||
description: { maxLeverage: 1.43, lpName: 'OSMO-USDC.n' },
|
||||
ltv: {
|
||||
@ -133,7 +133,12 @@ export const VAULT_CONFIGS: Vault[] = [
|
||||
contract: 0.3,
|
||||
liq: 0.4,
|
||||
},
|
||||
apy: 0,
|
||||
apy: {
|
||||
apys: null,
|
||||
fees: null,
|
||||
total: null,
|
||||
vaultAddress: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
address: 'osmo14lu7m4ganxs20258dazafrjfaulmfxruq9n0r0th90gs46jk3tuqwfkqwn',
|
||||
@ -148,7 +153,7 @@ export const VAULT_CONFIGS: Vault[] = [
|
||||
secondary: 'USDC.n',
|
||||
},
|
||||
color: colors.usdc,
|
||||
lockup: 86400 * 14,
|
||||
lockup: 86400 * 7,
|
||||
provider: 'Apollo vault',
|
||||
description: { maxLeverage: 1.43, lpName: 'OSMO-USDC.n' },
|
||||
ltv: {
|
||||
@ -156,7 +161,12 @@ export const VAULT_CONFIGS: Vault[] = [
|
||||
contract: 0.3,
|
||||
liq: 0.4,
|
||||
},
|
||||
apy: 0,
|
||||
apy: {
|
||||
apys: null,
|
||||
fees: null,
|
||||
total: null,
|
||||
vaultAddress: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
address: 'osmo1fmq9hw224fgz8lk48wyd0gfg028kvvzggt6c3zvnaqkw23x68cws5nd5em',
|
||||
@ -179,6 +189,11 @@ export const VAULT_CONFIGS: Vault[] = [
|
||||
contract: 0.3,
|
||||
liq: 0.4,
|
||||
},
|
||||
apy: 0,
|
||||
apy: {
|
||||
apys: null,
|
||||
fees: null,
|
||||
total: null,
|
||||
vaultAddress: '',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
@ -171,7 +171,12 @@ export const VAULT_CONFIGS: Vault[] = [
|
||||
contract: 0.63,
|
||||
liq: 0.65,
|
||||
},
|
||||
apy: 0,
|
||||
apy: {
|
||||
apys: null,
|
||||
fees: null,
|
||||
total: null,
|
||||
vaultAddress: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
address: 'osmo1jfmwayj8jqp9tfy4v4eks5c2jpnqdumn8x8xvfllng0wfes770qqp7jl4j',
|
||||
@ -194,7 +199,12 @@ export const VAULT_CONFIGS: Vault[] = [
|
||||
contract: 0.65,
|
||||
liq: 0.66,
|
||||
},
|
||||
apy: 0,
|
||||
apy: {
|
||||
apys: null,
|
||||
fees: null,
|
||||
total: null,
|
||||
vaultAddress: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
address: 'osmo1a6tcf60pyz8qq2n532dzcs7s7sj8klcmra04tvaqympzcvxqg9esn7xz7l',
|
||||
@ -217,6 +227,11 @@ export const VAULT_CONFIGS: Vault[] = [
|
||||
contract: 0.61,
|
||||
liq: 0.625,
|
||||
},
|
||||
apy: 0,
|
||||
apy: {
|
||||
apys: null,
|
||||
fees: null,
|
||||
total: null,
|
||||
vaultAddress: '',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
@ -37,3 +37,4 @@ export const FIELDS_TUTORIAL_KEY = 'fieldsHideTutorial'
|
||||
export const RED_BANK_TUTORIAL_KEY = 'redbankHideTutorial'
|
||||
export const DISPLAY_CURRENCY_KEY = 'displayCurrency'
|
||||
export const ENABLE_ANIMATIONS_KEY = 'enableAnimations'
|
||||
export const TERMS_OF_SERVICE = 'termsOfService'
|
||||
|
@ -22,9 +22,12 @@ export const DEFAULT_POSITION: Position = {
|
||||
net: 0,
|
||||
},
|
||||
apy: {
|
||||
borrow: 5.2,
|
||||
net: 7.7,
|
||||
apys: null,
|
||||
fees: null,
|
||||
total: 19,
|
||||
borrow: 5.2,
|
||||
net: 13.8,
|
||||
vaultAddress: '',
|
||||
},
|
||||
ltv: 0.5,
|
||||
currentLeverage: 1,
|
||||
|
@ -12,7 +12,7 @@ export const getTokenValueFromCoins = (assets: Asset[], coins: Coin[] = []) => {
|
||||
return formatValue(
|
||||
convertedValue,
|
||||
2,
|
||||
2,
|
||||
asset.decimals,
|
||||
true,
|
||||
convertedValue >= 0.01 ? false : '>',
|
||||
` ${asset.symbol}`,
|
||||
|
@ -1,7 +0,0 @@
|
||||
export const getDebtQuery = (address: string) => {
|
||||
return `
|
||||
{
|
||||
user_debts: { user: "${address}" }
|
||||
}`
|
||||
}
|
||||
1
|
@ -3,7 +3,6 @@ import {
|
||||
getContractQuery,
|
||||
getIncentiveQuery,
|
||||
getMarketQuery,
|
||||
getUserCollateralQuery,
|
||||
getUserIncentivesQuery,
|
||||
} from '.'
|
||||
|
||||
@ -47,14 +46,6 @@ export const getRedbankQuery = (
|
||||
return `query RedbankQuery {
|
||||
${REDBANK_WASM_KEY}: wasm {
|
||||
${wasmQueries}
|
||||
${
|
||||
address &&
|
||||
getContractQuery(
|
||||
'collateral',
|
||||
redBankContractAddress || '',
|
||||
getUserCollateralQuery(address),
|
||||
)
|
||||
}
|
||||
${
|
||||
address &&
|
||||
getContractQuery(
|
||||
|
@ -1,7 +0,0 @@
|
||||
export const getUserCollateralQuery = (address: string) => {
|
||||
return `{
|
||||
user_collaterals: {
|
||||
user: "${address}"
|
||||
}
|
||||
}`
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
export { getBalanceQuery } from './getBalanceQuery'
|
||||
export { getConfigQuery } from './getConfigQuery'
|
||||
export { getContractQuery } from './getContractQuery'
|
||||
export { getDebtQuery } from './getDebtQuery'
|
||||
export { getDepositDebtQuery } from './getDepositDebtQuery'
|
||||
export { getDepositsQuery } from './getDepositsQuery'
|
||||
export { getGlobalStateQuery } from './getGlobalStateQuery'
|
||||
@ -13,6 +12,5 @@ export { getRedbankQuery } from './getRedbankQuery'
|
||||
export { getSnapshotQuery } from './getSnapshotQuery'
|
||||
export { getStateQuery } from './getStateQuery'
|
||||
export { getUncollaterisedLoanLimitQuery } from './getUncollaterisedLoanLimitQuery'
|
||||
export { getUserCollateralQuery } from './getUserCollateralQuery'
|
||||
export { getUserIncentivesQuery } from './getUserIncentivesQuery'
|
||||
// @endindex
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { useMemo } from 'react'
|
||||
import useStore from 'store'
|
||||
|
||||
export const useActiveVault = (address: string) => {
|
||||
export const useActiveVault = (accountId: string) => {
|
||||
const activeVaults = useStore((s) => s.activeVaults)
|
||||
|
||||
return useMemo(() => {
|
||||
if (!activeVaults?.length) return
|
||||
return activeVaults.find((activeVault) => activeVault.address === address)
|
||||
}, [activeVaults, address])
|
||||
return activeVaults.find((activeVault) => activeVault.position.accountId === accountId)
|
||||
}, [activeVaults, accountId])
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import useStore from 'store'
|
||||
|
||||
export const usePrice = (denom: string): number => {
|
||||
const convertToBaseCurrency = useStore((s) => s.convertToBaseCurrency)
|
||||
return convertToBaseCurrency({ denom, amount: '1' })
|
||||
const getExchangeRate = useStore((s) => s.getExchangeRate)
|
||||
return getExchangeRate(denom)
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ export { useSpotPrice } from './useSpotPrice'
|
||||
export { useUnlockMessages } from './useUnlockMessages'
|
||||
export { useUsdPrice } from './useUsdPrice'
|
||||
export { useUserBalance } from './useUserBalance'
|
||||
export { useUserCollaterals } from './useUserCollaterals'
|
||||
export { useUserDebt } from './useUserDebt'
|
||||
export { useUserDeposit } from './useUserDeposit'
|
||||
export { useUserIcns } from './useUserIcns'
|
||||
// @endindex
|
||||
|
64
src/hooks/queries/useUserCollaterals.tsx
Normal file
64
src/hooks/queries/useUserCollaterals.tsx
Normal file
@ -0,0 +1,64 @@
|
||||
import { Coin } from '@cosmjs/stargate'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import useStore from 'store'
|
||||
import { QUERY_KEYS } from 'types/enums/queryKeys'
|
||||
|
||||
const QUERY_LIMIT = 10
|
||||
|
||||
export const useUserCollaterals = () => {
|
||||
const userWalletAddress = useStore((s) => s.userWalletAddress)
|
||||
const redbankContractAddress = useStore((s) => s.networkConfig?.contracts.redBank)
|
||||
const client = useStore((s) => s.client)
|
||||
|
||||
const resolveUserDeposits = (collaterals: UserCollateral[]): Coin[] => {
|
||||
return collaterals.map((collateral) => {
|
||||
return {
|
||||
denom: collateral.denom,
|
||||
amount: collateral.amount,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getCollaterals = async (
|
||||
contract: string,
|
||||
startAfter?: string,
|
||||
): Promise<UserCollateral[]> => {
|
||||
if (!client) return []
|
||||
return client.cosmWasmClient.queryContractSmart(contract, {
|
||||
user_collaterals: {
|
||||
user: userWalletAddress,
|
||||
limit: QUERY_LIMIT,
|
||||
start_after: startAfter,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
useQuery<UserCollateral[]>(
|
||||
[QUERY_KEYS.USER_COLLATERAL],
|
||||
async () => {
|
||||
let userCollateral: UserCollateral[] = []
|
||||
if (!redbankContractAddress) return userCollateral
|
||||
|
||||
let isMoreCollaterals = true
|
||||
|
||||
while (isMoreCollaterals) {
|
||||
const collateral = await getCollaterals(
|
||||
redbankContractAddress,
|
||||
userCollateral[userCollateral.length - 1]?.denom || '',
|
||||
)
|
||||
userCollateral = userCollateral.concat(collateral)
|
||||
|
||||
if (collateral.length < QUERY_LIMIT) isMoreCollaterals = false
|
||||
}
|
||||
|
||||
const userDeposits: Coin[] = resolveUserDeposits(userCollateral)
|
||||
useStore.setState({ userCollateral: userCollateral, userDeposits: userDeposits })
|
||||
return userCollateral
|
||||
},
|
||||
{
|
||||
enabled: !!redbankContractAddress && !!userWalletAddress && !!client,
|
||||
staleTime: 30000,
|
||||
refetchInterval: 30000,
|
||||
},
|
||||
)
|
||||
}
|
@ -1,55 +1,67 @@
|
||||
import { Coin } from '@cosmjs/stargate'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { getContractQuery, getDebtQuery } from 'functions/queries'
|
||||
import { gql, request } from 'graphql-request'
|
||||
import useStore from 'store'
|
||||
import { QUERY_KEYS } from 'types/enums/queryKeys'
|
||||
|
||||
const QUERY_LIMIT = 10
|
||||
|
||||
export interface UserDebtData {
|
||||
debts: {
|
||||
debts: [
|
||||
{
|
||||
denom: string
|
||||
amount_scaled: string
|
||||
amount: string
|
||||
enabled: boolean
|
||||
},
|
||||
]
|
||||
}
|
||||
denom: string
|
||||
amount_scaled: string
|
||||
amount: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
// ! Implement pagination. Currently there is a limit of 5 assets from the SC
|
||||
export const useUserDebt = () => {
|
||||
const hiveUrl = useStore((s) => s.networkConfig?.hiveUrl)
|
||||
const userWalletAddress = useStore((s) => s.userWalletAddress)
|
||||
const redbankContractAddress = useStore((s) => s.networkConfig?.contracts.redBank)
|
||||
const processUserDebtQuery = useStore((s) => s.processUserDebtQuery)
|
||||
const client = useStore((s) => s.client)
|
||||
|
||||
const debtsQuery = getContractQuery(
|
||||
'debts',
|
||||
redbankContractAddress || '',
|
||||
getDebtQuery(userWalletAddress),
|
||||
)
|
||||
const resolveDebtResponse = (debts: UserDebtData[]): Coin[] => {
|
||||
return debts.map((debt) => {
|
||||
return {
|
||||
denom: debt.denom,
|
||||
amount: debt.amount,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
useQuery<UserDebtData>(
|
||||
const getDebts = async (contract: string, startAfter?: string): Promise<UserDebtData[]> => {
|
||||
if (!client) return []
|
||||
return client.cosmWasmClient.queryContractSmart(contract, {
|
||||
user_debts: {
|
||||
user: userWalletAddress,
|
||||
limit: QUERY_LIMIT,
|
||||
start_after: startAfter,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
useQuery<Coin[]>(
|
||||
[QUERY_KEYS.USER_DEBT],
|
||||
async () => {
|
||||
return await request(
|
||||
hiveUrl!,
|
||||
gql`
|
||||
query UserDebtQuery {
|
||||
debts: wasm {
|
||||
${debtsQuery}
|
||||
}
|
||||
}
|
||||
`,
|
||||
)
|
||||
let userDebts: Coin[] = []
|
||||
if (!redbankContractAddress) return userDebts
|
||||
|
||||
let isMoreDebts = true
|
||||
|
||||
while (isMoreDebts) {
|
||||
const debts = await getDebts(
|
||||
redbankContractAddress,
|
||||
userDebts[userDebts.length - 1]?.denom || '',
|
||||
)
|
||||
userDebts = userDebts.concat(resolveDebtResponse(debts))
|
||||
|
||||
if (debts.length < QUERY_LIMIT) isMoreDebts = false
|
||||
}
|
||||
|
||||
useStore.setState({ userDebts: userDebts })
|
||||
return userDebts
|
||||
},
|
||||
{
|
||||
enabled:
|
||||
!!hiveUrl && !!redbankContractAddress && !!processUserDebtQuery && !!userWalletAddress,
|
||||
enabled: !!redbankContractAddress && !!userWalletAddress && !!client,
|
||||
staleTime: 30000,
|
||||
refetchInterval: 30000,
|
||||
onSuccess: processUserDebtQuery,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -1,55 +0,0 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { getContractQuery, getDepositsQuery } from 'functions/queries'
|
||||
import { gql, request } from 'graphql-request'
|
||||
import useStore from 'store'
|
||||
import { QUERY_KEYS } from 'types/enums/queryKeys'
|
||||
|
||||
export interface UserDepositData {
|
||||
deposits: {
|
||||
deposits:
|
||||
| [
|
||||
{
|
||||
denom: string
|
||||
amount_scaled: string
|
||||
amount: string
|
||||
enabled: boolean
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// ! Implement pagination. Currently there is a limit of 5 assets from the SC
|
||||
|
||||
export const useUserDeposit = () => {
|
||||
const hiveUrl = useStore((s) => s.networkConfig?.hiveUrl)
|
||||
const userWalletAddress = useStore((s) => s.userWalletAddress)
|
||||
const whitelistedAssets = useStore((s) => s.whitelistedAssets)
|
||||
const redbankContractAddress = useStore((s) => s.networkConfig?.contracts.redBank)
|
||||
const processUserDepositQuery = useStore((s) => s.processUserDepositQuery)
|
||||
|
||||
useQuery<UserDepositData>(
|
||||
[QUERY_KEYS.USER_DEPOSIT],
|
||||
async () => {
|
||||
return await request(
|
||||
hiveUrl!,
|
||||
gql`
|
||||
query UserDepositQuery {
|
||||
deposits: wasm {
|
||||
${getContractQuery(
|
||||
'deposits',
|
||||
redbankContractAddress || '',
|
||||
getDepositsQuery(userWalletAddress),
|
||||
)}
|
||||
}
|
||||
}
|
||||
`,
|
||||
)
|
||||
},
|
||||
{
|
||||
enabled: !!hiveUrl && !!redbankContractAddress && !!whitelistedAssets && !!userWalletAddress,
|
||||
staleTime: 30000,
|
||||
refetchInterval: 30000,
|
||||
onSuccess: processUserDepositQuery,
|
||||
},
|
||||
)
|
||||
}
|
@ -22,9 +22,12 @@ export const position: Position = {
|
||||
net: 0,
|
||||
},
|
||||
apy: {
|
||||
borrow: 5.2,
|
||||
net: 7.7,
|
||||
apys: null,
|
||||
fees: null,
|
||||
total: 19,
|
||||
borrow: 5.2,
|
||||
net: 13.8,
|
||||
vaultAddress: '',
|
||||
},
|
||||
ltv: 0.5,
|
||||
currentLeverage: 1,
|
||||
|
@ -125,26 +125,6 @@ export const redBankData: RedBankData = {
|
||||
index: '0.000000143742920378',
|
||||
last_updated: 1678369620,
|
||||
},
|
||||
collateral: [
|
||||
{
|
||||
denom: 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2',
|
||||
amount_scaled: '2559593418324',
|
||||
amount: '2564911',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
denom: 'ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858',
|
||||
amount_scaled: '20000000000000',
|
||||
amount: '20105061',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
denom: 'uosmo',
|
||||
amount_scaled: '48000037051741',
|
||||
amount: '48107901',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
unclaimedRewards: '4679062',
|
||||
},
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
export const vault: Vault = {
|
||||
address: 'test',
|
||||
apy: 10,
|
||||
apy: {
|
||||
apys: null,
|
||||
fees: null,
|
||||
total: null,
|
||||
vaultAddress: '',
|
||||
},
|
||||
color: 'test',
|
||||
denoms: {
|
||||
primary: 'OSMO',
|
||||
|
@ -11,7 +11,8 @@ const CloseVaultPosition = () => {
|
||||
const vaultConfigs = useStore((s) => s.vaultConfigs)
|
||||
const { mutate, data, isLoading, error } = useUpdateAccount()
|
||||
const vaultAddress = String(router.query.address)
|
||||
const activeVault = useActiveVault(vaultAddress)
|
||||
const accountId = String(router.query.id)
|
||||
const activeVault = useActiveVault(accountId)
|
||||
const { closeActions, closeFee } = useClosePosition({
|
||||
activeVault,
|
||||
isLoading: isLoading || !!data || !!error,
|
@ -113,7 +113,9 @@ const EditVault = (props: Props) => {
|
||||
const handleUnlockClick = useCallback(() => {
|
||||
if (!unlockFee) return
|
||||
if (showDisclaimer) {
|
||||
router.push(`/farm/vault/${props.activeVault.address}/unlock`)
|
||||
router.push(
|
||||
`/farm/vault/${props.activeVault.address}/account/${props.activeVault.position.accountId}/unlock`,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@ -125,6 +127,7 @@ const EditVault = (props: Props) => {
|
||||
})
|
||||
}, [
|
||||
props.activeVault.address,
|
||||
props.activeVault.position.accountId,
|
||||
showDisclaimer,
|
||||
router,
|
||||
unlockFee,
|
@ -7,8 +7,8 @@ import EditVault from './EditVault'
|
||||
const Edit = () => {
|
||||
const router = useRouter()
|
||||
|
||||
const vaultAddress = String(router.query.address)
|
||||
const activeVault = useActiveVault(vaultAddress)
|
||||
const accountId = String(router.query.id)
|
||||
const activeVault = useActiveVault(accountId)
|
||||
|
||||
if (!activeVault) return <></>
|
||||
|
@ -7,8 +7,8 @@ import RepayVault from './RepayVault'
|
||||
const Repay = () => {
|
||||
const router = useRouter()
|
||||
|
||||
const vaultAddress = String(router.query.address)
|
||||
const activeVault = useActiveVault(vaultAddress)
|
||||
const accountId = String(router.query.id)
|
||||
const activeVault = useActiveVault(accountId)
|
||||
|
||||
if (!activeVault) return <></>
|
||||
|
@ -12,8 +12,8 @@ import styles from './UnlockDisclaimer.module.scss'
|
||||
const Unlock = () => {
|
||||
const { t } = useTranslation()
|
||||
const router = useRouter()
|
||||
const address = String(router.query.address)
|
||||
const activeVault = useActiveVault(address)
|
||||
const accountId = String(router.query.id)
|
||||
const activeVault = useActiveVault(accountId)
|
||||
const {
|
||||
mutate: requestUnlock,
|
||||
data: unlockData,
|
@ -43,6 +43,7 @@ export interface CommonSlice {
|
||||
networkConfig?: NetworkConfig
|
||||
otherAssets: Asset[]
|
||||
queryErrors: string[]
|
||||
acceptedTermsOfService: boolean
|
||||
slippage: number
|
||||
tutorialSteps: { redbank: number; fields: number }
|
||||
userBalances: Coin[]
|
||||
|
@ -1,6 +1,4 @@
|
||||
import { Coin } from '@cosmjs/stargate'
|
||||
import { UserDebtData } from 'hooks/queries/useUserDebt'
|
||||
import { UserDepositData } from 'hooks/queries/useUserDeposit'
|
||||
import { State } from 'types/enums'
|
||||
|
||||
export interface RedBankSlice {
|
||||
@ -38,9 +36,5 @@ export interface RedBankSlice {
|
||||
// QUERY RELATED
|
||||
// ------------------
|
||||
previousRedBankQueryData?: RedBankData
|
||||
previousUserDebtQueryData?: UserDebtData
|
||||
previousUserDepositQueryData?: UserDepositData
|
||||
processRedBankQuery: (data: RedBankData, whitelistedAssets: Asset[]) => void
|
||||
processUserDebtQuery: (data: UserDebtData) => void
|
||||
processUserDepositQuery: (data: UserDepositData) => void
|
||||
}
|
||||
|
@ -5,14 +5,14 @@ export interface VaultsSlice {
|
||||
availableVaults: Vault[]
|
||||
activeVaults: ActiveVault[]
|
||||
creditAccounts?: Positions[]
|
||||
addAprToVaults: (aprs: AprData[]) => void
|
||||
addApyToVaults: (apys: ApyBreakdown[]) => void
|
||||
getCreditAccounts: (options?: Options) => Promise<Positions[]>
|
||||
vaultAssets?: VaultCoinsWithAddress[]
|
||||
getVaultAssets: (options?: Options) => Promise<VaultCoinsWithAddress[]>
|
||||
unlockTimes?: UnlockTimeWithAddress[]
|
||||
getUnlockTimes: (options?: Options) => Promise<UnlockTimeWithAddress[]>
|
||||
aprs?: AprData[] | null
|
||||
getAprs: (options?: Options) => Promise<null>
|
||||
apys?: ApyBreakdown[] | null
|
||||
getApys: (options?: Options) => Promise<null>
|
||||
caps?: VaultCapData[]
|
||||
getCaps: (options?: Options) => Promise<VaultCapData[]>
|
||||
lpTokens?: LpTokenWithAddress[]
|
||||
|
@ -45,6 +45,7 @@ const commonSlice = (
|
||||
marketDebts: [],
|
||||
otherAssets: [],
|
||||
queryErrors: [],
|
||||
acceptedTermsOfService: false,
|
||||
slippage: 0.02,
|
||||
tutorialSteps: { redbank: 1, fields: 1 },
|
||||
userBalances: [],
|
||||
@ -65,8 +66,16 @@ const commonSlice = (
|
||||
(exchangeRate) => exchangeRate.denom === coin.denom,
|
||||
)?.amount
|
||||
if (!exchangeRate) return 0
|
||||
const assets = [...get().whitelistedAssets, ...get().otherAssets]
|
||||
const baseDecimals = get().baseAsset?.decimals ?? 0
|
||||
const coinDecimals = assets.find((currency) => currency.denom === coin.denom)?.decimals ?? 0
|
||||
|
||||
return new BigNumber(coin.amount).times(exchangeRate).toNumber()
|
||||
const additionalDecimals = coinDecimals - baseDecimals
|
||||
|
||||
return new BigNumber(coin.amount)
|
||||
.times(exchangeRate)
|
||||
.shiftedBy(-1 * additionalDecimals)
|
||||
.toNumber()
|
||||
},
|
||||
convertValueToAmount: (coin: Coin) => {
|
||||
const exchangeRates = get().exchangeRates
|
||||
|
@ -2,8 +2,6 @@ import { Coin } from '@cosmjs/stargate'
|
||||
import { MARS_SYMBOL } from 'constants/appConstants'
|
||||
import { SECONDS_IN_YEAR } from 'constants/timeConstants'
|
||||
import { findByDenom } from 'functions'
|
||||
import { UserDebtData } from 'hooks/queries/useUserDebt'
|
||||
import { UserDepositData } from 'hooks/queries/useUserDeposit'
|
||||
import { demagnify, lookupDenomBySymbol } from 'libs/parse'
|
||||
import isEqual from 'lodash.isequal'
|
||||
import { RedBankSlice } from 'store/interfaces/redBank.interface'
|
||||
@ -166,7 +164,6 @@ const redBankSlice = (set: NamedSet<Store>, get: GetState<Store>): RedBankSlice
|
||||
marketInfo,
|
||||
marketIncentiveInfo,
|
||||
previousRedBankQueryData: data,
|
||||
userCollateral: data.rbwasmkey.collateral,
|
||||
userUnclaimedRewards,
|
||||
redBankState: State.READY,
|
||||
})
|
||||
@ -174,30 +171,6 @@ const redBankSlice = (set: NamedSet<Store>, get: GetState<Store>): RedBankSlice
|
||||
findCollateral: (denom: string) => {
|
||||
return get().userCollateral && get().userCollateral!.find((item) => item.denom === denom)
|
||||
},
|
||||
processUserDebtQuery: (data: UserDebtData) => {
|
||||
if (isEqual(data, get().previousUserDebtQueryData)) return
|
||||
|
||||
const debtsResponse = data.debts.debts
|
||||
set({
|
||||
previousUserDebtQueryData: data,
|
||||
userDebts: debtsResponse.map((debt) => {
|
||||
return { denom: debt.denom, amount: debt.amount }
|
||||
}),
|
||||
})
|
||||
},
|
||||
processUserDepositQuery: (data: UserDepositData) => {
|
||||
if (isEqual(data, get().previousUserDepositQueryData)) return
|
||||
|
||||
const deposits = data.deposits.deposits
|
||||
|
||||
if (!deposits) return
|
||||
|
||||
const userDeposits = deposits.map((deposit) => ({
|
||||
denom: deposit.denom,
|
||||
amount: deposit.amount,
|
||||
}))
|
||||
set({ previousUserDepositQueryData: data, userDeposits })
|
||||
},
|
||||
})
|
||||
|
||||
export default redBankSlice
|
||||
|
@ -19,21 +19,29 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
|
||||
isLoading: false,
|
||||
availableVaults: [],
|
||||
activeVaults: [],
|
||||
addAprToVaults: (aprs: AprData[]) => {
|
||||
addApyToVaults: (apys: ApyBreakdown[]) => {
|
||||
const updatedAvailableVaults = get().availableVaults.map((availableVault) => {
|
||||
const apr =
|
||||
(aprs?.find((apr) => apr.contractAddress === availableVault.address)?.apr || 0) * 100
|
||||
availableVault.apy = convertAprToApy(apr, 365)
|
||||
const apy = apys?.find((apy) => apy.vaultAddress === availableVault.address)
|
||||
|
||||
if (!apy) return availableVault
|
||||
availableVault.apy = apy
|
||||
return availableVault
|
||||
})
|
||||
|
||||
const updatedActiveVaults = get().activeVaults.map((activeVault) => {
|
||||
const apr = (aprs?.find((apr) => apr.contractAddress === activeVault.address)?.apr || 0) * 100
|
||||
const apy = convertAprToApy(apr, 365)
|
||||
const apy = apys?.find((apy) => apy.vaultAddress === activeVault.address)
|
||||
|
||||
if (!apy) return activeVault
|
||||
|
||||
activeVault.apy = apy
|
||||
activeVault.position.apy.total = apy
|
||||
activeVault.position.apy.net =
|
||||
apy * activeVault.position.currentLeverage - activeVault.position.apy.borrow
|
||||
activeVault.position.apy.borrow
|
||||
activeVault.position.apy = {
|
||||
...apy,
|
||||
borrow: activeVault.position.apy.borrow,
|
||||
net:
|
||||
(apy.total || 0) * activeVault.position.currentLeverage - activeVault.position.apy.borrow,
|
||||
}
|
||||
|
||||
return activeVault
|
||||
})
|
||||
|
||||
@ -93,6 +101,7 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
|
||||
},
|
||||
}),
|
||||
vaultAddress: lpToken.vaultAddress,
|
||||
accountId: lpToken.accountId,
|
||||
}
|
||||
})
|
||||
|
||||
@ -133,6 +142,7 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
|
||||
) / 1e6,
|
||||
),
|
||||
vaultAddress: creditAccount.vaults[0].vault.address,
|
||||
accountId: creditAccount.account_id,
|
||||
}
|
||||
})
|
||||
|
||||
@ -144,13 +154,14 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
|
||||
|
||||
return newUnlockTimes
|
||||
},
|
||||
getAprs: async (options?: Options) => {
|
||||
const aprs = get().aprs
|
||||
if (aprs && !options?.refetch) {
|
||||
get().addAprToVaults(aprs)
|
||||
getApys: async (options?: Options) => {
|
||||
const apys = get().apys
|
||||
if (apys && !options?.refetch) {
|
||||
get().addApyToVaults(apys)
|
||||
return null
|
||||
}
|
||||
|
||||
const vaultAddresses = get().vaultConfigs.map((vault) => vault.address)
|
||||
const networkConfig = get().networkConfig
|
||||
if (!networkConfig) return null
|
||||
|
||||
@ -158,31 +169,40 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
|
||||
const response = await fetch(networkConfig!.apolloAprUrl)
|
||||
|
||||
if (response.ok) {
|
||||
const data: FlatApr[] | NestedApr[] = await response.json()
|
||||
const data: ApolloAprResponse[] = await response.json()
|
||||
|
||||
const newAprs = data.map((aprData) => {
|
||||
try {
|
||||
const apr = aprData as FlatApr
|
||||
const aprTotal = apr.apr.reduce((prev, curr) => Number(curr.value) + prev, 0)
|
||||
const feeTotal = apr.fees.reduce((prev, curr) => Number(curr.value) + prev, 0)
|
||||
const filteredData = data.filter((aprData) =>
|
||||
vaultAddresses.includes(aprData.contract_address),
|
||||
)
|
||||
|
||||
const finalApr = aprTotal + feeTotal
|
||||
const newApys: ApyBreakdown[] = filteredData.map((aprData) => {
|
||||
const aprTotal = aprData.apr.aprs.reduce((prev, curr) => Number(curr.value) + prev, 0)
|
||||
const feeTotal = aprData.apr.fees.reduce((prev, curr) => Number(curr.value) + prev, 0)
|
||||
const finalApr = (aprTotal - feeTotal) * 100
|
||||
const finalApy = convertAprToApy(finalApr, 365)
|
||||
|
||||
return { contractAddress: aprData.contract_address, apr: finalApr }
|
||||
} catch {
|
||||
const apr = aprData as NestedApr
|
||||
const aprTotal = apr.apr.aprs.reduce((prev, curr) => Number(curr.value) + prev, 0)
|
||||
const feeTotal = apr.apr.fees.reduce((prev, curr) => Number(curr.value) + prev, 0)
|
||||
const apys = aprData.apr.aprs.map((apr) => ({
|
||||
type: apr.type,
|
||||
value: new BigNumber(apr.value).dividedBy(aprTotal).multipliedBy(finalApy).toNumber(),
|
||||
}))
|
||||
|
||||
const finalApr = aprTotal + feeTotal
|
||||
return { contractAddress: aprData.contract_address, apr: finalApr }
|
||||
const fees = aprData.apr.fees.map((fee) => ({
|
||||
type: fee.type,
|
||||
value: new BigNumber(fee.value).dividedBy(feeTotal).multipliedBy(finalApy).toNumber(),
|
||||
}))
|
||||
|
||||
return {
|
||||
vaultAddress: aprData.contract_address,
|
||||
total: finalApy,
|
||||
apys,
|
||||
fees,
|
||||
}
|
||||
})
|
||||
|
||||
set({
|
||||
aprs: newAprs,
|
||||
apys: newApys,
|
||||
})
|
||||
get().addAprToVaults(newAprs)
|
||||
get().addApyToVaults(newApys)
|
||||
}
|
||||
|
||||
return null
|
||||
@ -256,6 +276,7 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
|
||||
unlocked: amounts.unlocked,
|
||||
denom: vault?.denoms.lpToken || '',
|
||||
vaultAddress: creditAccount.vaults[0].vault.address,
|
||||
accountId: creditAccount.account_id,
|
||||
}
|
||||
})
|
||||
|
||||
@ -275,199 +296,218 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
|
||||
return Promise.all([vaultAssets, unlockTimes, caps]).then(
|
||||
([vaultAssets, unlockTimes, caps]) => {
|
||||
const { activeVaults, availableVaults } = get().vaultConfigs.reduce(
|
||||
(prev, curr) => {
|
||||
(prev, vaultConfig) => {
|
||||
const lpTokens = get().lpTokens
|
||||
const creditAccounts = get().creditAccounts
|
||||
|
||||
const creditAccountPosition = creditAccounts?.find(
|
||||
(position) => position.vaults[0].vault.address === curr.address,
|
||||
const creditAccountPositions = creditAccounts?.filter(
|
||||
(position) => position.vaults[0].vault.address === vaultConfig.address,
|
||||
)
|
||||
|
||||
curr.apy = null
|
||||
vaultConfig.apy = {
|
||||
apys: null,
|
||||
fees: null,
|
||||
total: null,
|
||||
vaultAddress: vaultConfig.address,
|
||||
}
|
||||
|
||||
curr.vaultCap = caps?.find((cap) => cap.address === curr.address)?.vaultCap
|
||||
vaultConfig.vaultCap = caps?.find(
|
||||
(cap) => cap.address === vaultConfig.address,
|
||||
)?.vaultCap
|
||||
|
||||
// No position = available vault
|
||||
if (!creditAccountPosition) {
|
||||
prev.availableVaults.push(curr)
|
||||
if (!creditAccountPositions?.length) {
|
||||
prev.availableVaults.push(vaultConfig)
|
||||
return prev
|
||||
}
|
||||
|
||||
// Position = active vault
|
||||
const primaryAndSecondaryAmount = vaultAssets.find(
|
||||
(vaultAsset) => vaultAsset.vaultAddress === curr.address,
|
||||
)
|
||||
creditAccountPositions.forEach((creditAccountPosition) => {
|
||||
const primaryAndSecondaryAmount = vaultAssets.find(
|
||||
(vaultAsset) => vaultAsset.accountId === creditAccountPosition.account_id,
|
||||
)
|
||||
|
||||
const vaultTokenAmounts = getAmountsFromActiveVault(
|
||||
creditAccountPosition.vaults[0].amount,
|
||||
)
|
||||
const vaultTokenAmounts = getAmountsFromActiveVault(
|
||||
creditAccountPosition.vaults[0].amount,
|
||||
)
|
||||
|
||||
const lpTokenAmounts = lpTokens?.find(
|
||||
(lpToken) => lpToken.vaultAddress === curr.address,
|
||||
)
|
||||
const lpTokenAmounts = lpTokens?.find(
|
||||
(lpToken) => lpToken.accountId === creditAccountPosition.account_id,
|
||||
)
|
||||
|
||||
if (!primaryAndSecondaryAmount || !vaultTokenAmounts || !lpTokenAmounts) {
|
||||
prev.availableVaults.push(curr)
|
||||
return prev
|
||||
}
|
||||
|
||||
let id: number | undefined
|
||||
try {
|
||||
id = (creditAccountPosition.vaults[0].amount as { locking: LockingVaultAmount })
|
||||
.locking.unlocking[0].id
|
||||
} catch {
|
||||
id = undefined
|
||||
}
|
||||
|
||||
// Should already filter out null values
|
||||
const unlockTime = unlockTimes.find(
|
||||
(unlockTime) => unlockTime?.vaultAddress === curr.address,
|
||||
)?.unlockAtTimestamp
|
||||
|
||||
const primaryAmount = Number(
|
||||
findByDenom(primaryAndSecondaryAmount.coins, curr.denoms.primary)?.amount || 0,
|
||||
)
|
||||
const secondaryAmount = Number(
|
||||
findByDenom(primaryAndSecondaryAmount.coins, curr.denoms.secondary)?.amount || 0,
|
||||
)
|
||||
|
||||
let primarySupplyAmount = 0
|
||||
let secondarySupplyAmount = 0
|
||||
let borrowedPrimaryAmount = 0
|
||||
let borrowedSecondaryAmount = 0
|
||||
|
||||
const debt = creditAccountPosition.debts[0]
|
||||
|
||||
if (debt) {
|
||||
if (debt.denom === curr.denoms.primary) {
|
||||
borrowedPrimaryAmount = Number(debt.amount)
|
||||
} else {
|
||||
borrowedSecondaryAmount = Number(debt.amount)
|
||||
if (!primaryAndSecondaryAmount || !vaultTokenAmounts || !lpTokenAmounts) {
|
||||
prev.availableVaults.push(vaultConfig)
|
||||
return prev
|
||||
}
|
||||
}
|
||||
|
||||
const borrowedDenom = debt?.denom || ''
|
||||
let id: number | undefined
|
||||
try {
|
||||
id = (creditAccountPosition.vaults[0].amount as { locking: LockingVaultAmount })
|
||||
.locking.unlocking[0].id
|
||||
} catch {
|
||||
id = undefined
|
||||
}
|
||||
|
||||
if (borrowedDenom === curr.denoms.primary) {
|
||||
if (borrowedPrimaryAmount > primaryAmount) {
|
||||
const swapped = Math.round(
|
||||
get().convertToBaseCurrency({
|
||||
denom: borrowedDenom,
|
||||
amount: (borrowedPrimaryAmount - primaryAmount).toString(),
|
||||
}),
|
||||
)
|
||||
// Should already filter out null values
|
||||
const unlockTime = unlockTimes.find(
|
||||
(unlockTime) => unlockTime?.accountId === creditAccountPosition.account_id,
|
||||
)?.unlockAtTimestamp
|
||||
|
||||
const rate = Number(
|
||||
get().exchangeRates?.find((coin) => coin.denom === curr.denoms.secondary)
|
||||
?.amount ?? 0,
|
||||
)
|
||||
primarySupplyAmount = 0
|
||||
secondarySupplyAmount = Math.floor(secondaryAmount - swapped / rate)
|
||||
const primaryAmount = Number(
|
||||
findByDenom(primaryAndSecondaryAmount.coins, vaultConfig.denoms.primary)?.amount ||
|
||||
0,
|
||||
)
|
||||
const secondaryAmount = Number(
|
||||
findByDenom(primaryAndSecondaryAmount.coins, vaultConfig.denoms.secondary)
|
||||
?.amount || 0,
|
||||
)
|
||||
|
||||
let primarySupplyAmount = 0
|
||||
let secondarySupplyAmount = 0
|
||||
let borrowedPrimaryAmount = 0
|
||||
let borrowedSecondaryAmount = 0
|
||||
|
||||
const debt = creditAccountPosition.debts[0]
|
||||
|
||||
if (debt) {
|
||||
if (debt.denom === vaultConfig.denoms.primary) {
|
||||
borrowedPrimaryAmount = Number(debt.amount)
|
||||
} else {
|
||||
borrowedSecondaryAmount = Number(debt.amount)
|
||||
}
|
||||
}
|
||||
|
||||
const borrowedDenom = debt?.denom || ''
|
||||
|
||||
if (borrowedDenom === vaultConfig.denoms.primary) {
|
||||
if (borrowedPrimaryAmount > primaryAmount) {
|
||||
const swapped = Math.round(
|
||||
get().convertToBaseCurrency({
|
||||
denom: borrowedDenom,
|
||||
amount: (borrowedPrimaryAmount - primaryAmount).toString(),
|
||||
}),
|
||||
)
|
||||
|
||||
const rate = Number(
|
||||
get().exchangeRates?.find((coin) => coin.denom === vaultConfig.denoms.secondary)
|
||||
?.amount ?? 0,
|
||||
)
|
||||
primarySupplyAmount = 0
|
||||
secondarySupplyAmount = Math.floor(secondaryAmount - swapped / rate)
|
||||
} else {
|
||||
primarySupplyAmount = primaryAmount - borrowedPrimaryAmount
|
||||
secondarySupplyAmount = secondaryAmount
|
||||
}
|
||||
} else if (borrowedDenom === vaultConfig.denoms.secondary) {
|
||||
if (borrowedSecondaryAmount > secondaryAmount) {
|
||||
const swapped = Math.round(
|
||||
get().convertToBaseCurrency({
|
||||
denom: borrowedDenom,
|
||||
amount: (borrowedSecondaryAmount - secondaryAmount).toString(),
|
||||
}),
|
||||
)
|
||||
const rate = Number(
|
||||
get().exchangeRates?.find((coin) => coin.denom === vaultConfig.denoms.primary)
|
||||
?.amount ?? 0,
|
||||
)
|
||||
secondarySupplyAmount = 0
|
||||
primarySupplyAmount = Math.floor(primaryAmount - swapped / rate)
|
||||
} else {
|
||||
secondarySupplyAmount = secondaryAmount - borrowedSecondaryAmount
|
||||
primarySupplyAmount = primaryAmount
|
||||
}
|
||||
} else {
|
||||
primarySupplyAmount = primaryAmount - borrowedPrimaryAmount
|
||||
primarySupplyAmount = primaryAmount
|
||||
secondarySupplyAmount = secondaryAmount
|
||||
}
|
||||
} else if (borrowedDenom === curr.denoms.secondary) {
|
||||
if (borrowedSecondaryAmount > secondaryAmount) {
|
||||
const swapped = Math.round(
|
||||
get().convertToBaseCurrency({
|
||||
denom: borrowedDenom,
|
||||
amount: (borrowedSecondaryAmount - secondaryAmount).toString(),
|
||||
}),
|
||||
)
|
||||
const rate = Number(
|
||||
get().exchangeRates?.find((coin) => coin.denom === curr.denoms.primary)?.amount ??
|
||||
0,
|
||||
)
|
||||
secondarySupplyAmount = 0
|
||||
primarySupplyAmount = Math.floor(primaryAmount - swapped / rate)
|
||||
} else {
|
||||
secondarySupplyAmount = secondaryAmount - borrowedSecondaryAmount
|
||||
primarySupplyAmount = primaryAmount
|
||||
|
||||
const borrowedAmount = Math.max(borrowedPrimaryAmount, borrowedSecondaryAmount)
|
||||
|
||||
const convertToBaseCurrency = get().convertToBaseCurrency
|
||||
const redBankAssets = get().redBankAssets
|
||||
const primarySupplyValue = convertToBaseCurrency({
|
||||
denom: vaultConfig.denoms.primary,
|
||||
amount: primarySupplyAmount.toString(),
|
||||
})
|
||||
|
||||
const secondarySupplyValue = convertToBaseCurrency({
|
||||
denom: vaultConfig.denoms.secondary,
|
||||
amount: secondarySupplyAmount.toString(),
|
||||
})
|
||||
|
||||
const borrowedValue = convertToBaseCurrency({
|
||||
denom: borrowedDenom,
|
||||
amount: borrowedAmount.toString(),
|
||||
})
|
||||
|
||||
const values = {
|
||||
primary: primarySupplyValue,
|
||||
secondary: secondarySupplyValue,
|
||||
borrowedPrimary: borrowedDenom === vaultConfig.denoms.primary ? borrowedValue : 0,
|
||||
borrowedSecondary:
|
||||
borrowedDenom === vaultConfig.denoms.secondary ? borrowedValue : 0,
|
||||
net: primarySupplyValue + secondarySupplyValue,
|
||||
total: primarySupplyValue + secondarySupplyValue + borrowedValue,
|
||||
}
|
||||
}
|
||||
|
||||
const borrowedAmount = Math.max(borrowedPrimaryAmount, borrowedSecondaryAmount)
|
||||
const leverage = getLeverageFromValues(values)
|
||||
|
||||
const convertToBaseCurrency = get().convertToBaseCurrency
|
||||
const redBankAssets = get().redBankAssets
|
||||
const primarySupplyValue = convertToBaseCurrency({
|
||||
denom: curr.denoms.primary,
|
||||
amount: primarySupplyAmount.toString(),
|
||||
})
|
||||
const borrowRate =
|
||||
redBankAssets.find((asset) => asset.denom === borrowedDenom)?.borrowRate || 0
|
||||
|
||||
const secondarySupplyValue = convertToBaseCurrency({
|
||||
denom: curr.denoms.secondary,
|
||||
amount: secondarySupplyAmount.toString(),
|
||||
})
|
||||
const trueBorrowRate = (leverage - 1) * borrowRate
|
||||
|
||||
const borrowedValue = convertToBaseCurrency({
|
||||
denom: borrowedDenom,
|
||||
amount: borrowedAmount.toString(),
|
||||
})
|
||||
const getPositionStatus = (unlockTime?: number) => {
|
||||
if (!unlockTime) return 'active'
|
||||
|
||||
const values = {
|
||||
primary: primarySupplyValue,
|
||||
secondary: secondarySupplyValue,
|
||||
borrowedPrimary: borrowedDenom === curr.denoms.primary ? borrowedValue : 0,
|
||||
borrowedSecondary: borrowedDenom === curr.denoms.secondary ? borrowedValue : 0,
|
||||
net: primarySupplyValue + secondarySupplyValue,
|
||||
total: primarySupplyValue + secondarySupplyValue + borrowedValue,
|
||||
}
|
||||
const isUnlocked = moment(unlockTime).isBefore(new Date())
|
||||
if (isUnlocked) return 'unlocked'
|
||||
|
||||
const leverage = getLeverageFromValues(values)
|
||||
return 'unlocking'
|
||||
}
|
||||
|
||||
const borrowRate =
|
||||
redBankAssets.find((asset) => asset.denom === borrowedDenom)?.borrowRate || 0
|
||||
|
||||
const trueBorrowRate = (leverage - 1) * borrowRate
|
||||
|
||||
const getPositionStatus = (unlockTime?: number) => {
|
||||
if (!unlockTime) return 'active'
|
||||
|
||||
const isUnlocked = moment(unlockTime).isBefore(new Date())
|
||||
if (isUnlocked) return 'unlocked'
|
||||
|
||||
return 'unlocking'
|
||||
}
|
||||
|
||||
const position: Position = {
|
||||
id: id,
|
||||
accountId: creditAccountPosition.account_id,
|
||||
amounts: {
|
||||
primary: primarySupplyAmount,
|
||||
secondary: secondarySupplyAmount,
|
||||
borrowedPrimary: borrowedDenom === curr.denoms.primary ? borrowedAmount : 0,
|
||||
borrowedSecondary: borrowedDenom === curr.denoms.secondary ? borrowedAmount : 0,
|
||||
lp: {
|
||||
amount: vaultTokenAmounts.unlocking,
|
||||
primary: Number(
|
||||
primaryAndSecondaryAmount.coins.find(
|
||||
(coin) => coin.denom === curr.denoms.primary,
|
||||
)?.amount || 0,
|
||||
),
|
||||
secondary: Number(
|
||||
primaryAndSecondaryAmount.coins.find(
|
||||
(coin) => coin.denom === curr.denoms.secondary,
|
||||
)?.amount || 0,
|
||||
),
|
||||
const position: Position = {
|
||||
id: id,
|
||||
accountId: creditAccountPosition.account_id,
|
||||
amounts: {
|
||||
primary: primarySupplyAmount,
|
||||
secondary: secondarySupplyAmount,
|
||||
borrowedPrimary:
|
||||
borrowedDenom === vaultConfig.denoms.primary ? borrowedAmount : 0,
|
||||
borrowedSecondary:
|
||||
borrowedDenom === vaultConfig.denoms.secondary ? borrowedAmount : 0,
|
||||
lp: {
|
||||
amount: vaultTokenAmounts.unlocking,
|
||||
primary: Number(
|
||||
primaryAndSecondaryAmount.coins.find(
|
||||
(coin) => coin.denom === vaultConfig.denoms.primary,
|
||||
)?.amount || 0,
|
||||
),
|
||||
secondary: Number(
|
||||
primaryAndSecondaryAmount.coins.find(
|
||||
(coin) => coin.denom === vaultConfig.denoms.secondary,
|
||||
)?.amount || 0,
|
||||
),
|
||||
},
|
||||
vault: vaultTokenAmounts.locked,
|
||||
},
|
||||
vault: vaultTokenAmounts.locked,
|
||||
},
|
||||
values,
|
||||
apy: {
|
||||
total: null,
|
||||
borrow: trueBorrowRate,
|
||||
net: null,
|
||||
},
|
||||
currentLeverage: leverage,
|
||||
ltv: leverageToLtv(leverage),
|
||||
...(unlockTime ? { unlockAtTimestamp: unlockTime } : {}),
|
||||
status: getPositionStatus(unlockTime),
|
||||
borrowDenom: borrowedDenom,
|
||||
}
|
||||
values,
|
||||
apy: {
|
||||
vaultAddress: vaultConfig.address,
|
||||
borrow: trueBorrowRate,
|
||||
total: null,
|
||||
net: null,
|
||||
apys: null,
|
||||
fees: null,
|
||||
},
|
||||
currentLeverage: leverage,
|
||||
ltv: leverageToLtv(leverage),
|
||||
...(unlockTime ? { unlockAtTimestamp: unlockTime } : {}),
|
||||
status: getPositionStatus(unlockTime),
|
||||
borrowDenom: borrowedDenom,
|
||||
}
|
||||
|
||||
prev.activeVaults.push({ ...curr, position })
|
||||
prev.activeVaults.push({ ...vaultConfig, position })
|
||||
})
|
||||
|
||||
return prev
|
||||
},
|
||||
@ -478,7 +518,7 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
|
||||
)
|
||||
|
||||
set({ activeVaults, availableVaults, isLoading: false })
|
||||
get().getAprs(options)
|
||||
get().getApys(options)
|
||||
},
|
||||
)
|
||||
},
|
||||
|
@ -16,4 +16,5 @@ export enum QUERY_KEYS {
|
||||
PROVIDE_LIQUIDITY = 'provideLiquidity',
|
||||
UNLOCK_MESSAGE = 'unlockMessage',
|
||||
USD_PRICE = 'usdPrice',
|
||||
USER_COLLATERAL = 'userCollateral',
|
||||
}
|
||||
|
38
src/types/interfaces/fields.d.ts
vendored
38
src/types/interfaces/fields.d.ts
vendored
@ -35,7 +35,7 @@ interface Vault {
|
||||
used: number
|
||||
max: number
|
||||
}
|
||||
apy: number | null
|
||||
apy: ApyBreakdown
|
||||
}
|
||||
|
||||
interface Position {
|
||||
@ -62,11 +62,7 @@ interface Position {
|
||||
total: number
|
||||
net: number
|
||||
}
|
||||
apy: {
|
||||
total: number | null
|
||||
borrow: number
|
||||
net: number | null
|
||||
}
|
||||
apy: PositionApyBreakdown
|
||||
ltv: number
|
||||
currentLeverage: number
|
||||
unlockAtTimestamp?: number
|
||||
@ -88,16 +84,19 @@ interface LpTokenWithAddress {
|
||||
unlocked: string
|
||||
denom: string
|
||||
vaultAddress: string
|
||||
accountId: string
|
||||
}
|
||||
|
||||
interface VaultCoinsWithAddress {
|
||||
coins: Coin[]
|
||||
vaultAddress: string
|
||||
accountId: string
|
||||
}
|
||||
|
||||
interface UnlockTimeWithAddress {
|
||||
unlockAtTimestamp: number
|
||||
vaultAddress: string
|
||||
accountId: string
|
||||
}
|
||||
|
||||
interface FieldsAction {
|
||||
@ -105,23 +104,26 @@ interface FieldsAction {
|
||||
values: string[]
|
||||
}
|
||||
|
||||
interface AprData {
|
||||
contractAddress: string
|
||||
apr: number
|
||||
interface PositionApyBreakdown extends ApyBreakdown {
|
||||
borrow: number
|
||||
net: number | null
|
||||
}
|
||||
|
||||
interface FlatApr {
|
||||
contract_address: string
|
||||
apr: { type: string; value: number | string }[]
|
||||
fees: { type: string; value: number | string }[]
|
||||
interface ApyBreakdown {
|
||||
vaultAddress: string
|
||||
apys: { type: string; value: number }[] | null
|
||||
fees: { type: string; value: number }[] | null
|
||||
total: number | null
|
||||
}
|
||||
|
||||
interface NestedApr {
|
||||
interface ApolloAprResponse {
|
||||
contract_address: string
|
||||
apr: {
|
||||
aprs: { type: string; value: number | string }[]
|
||||
fees: { type: string; value: number | string }[]
|
||||
}
|
||||
apr: AprBreakdown
|
||||
}
|
||||
|
||||
interface AprBreakdown {
|
||||
aprs: { type: string; value: number }[]
|
||||
fees: { type: string; value: string | number }[]
|
||||
}
|
||||
|
||||
interface VaultCapData {
|
||||
|
1
src/types/interfaces/redbank.d.ts
vendored
1
src/types/interfaces/redbank.d.ts
vendored
@ -21,7 +21,6 @@ interface RedBankData {
|
||||
stATOMMarketIncentive: MarketIncentive
|
||||
nUSDCMarket: Market
|
||||
nUSDCMarketIncentive: MarketIncentive
|
||||
collateral: UserCollateral[]
|
||||
unclaimedRewards: string
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user