This commit is contained in:
Linkie Link 2023-06-07 10:32:21 +02:00
parent e3d0e194ce
commit 9d36f50856
No known key found for this signature in database
GPG Key ID: 5318B0F2564D38EA
58 changed files with 705 additions and 481 deletions

View File

@ -26,22 +26,22 @@ const moduleExports = {
permanent: true, permanent: true,
}, },
{ {
source: '/farm/vault/:address/edit', source: '/farm/vault/:address/account/:id/edit',
destination: '/farm/', destination: '/farm/',
permanent: true, permanent: true,
}, },
{ {
source: '/farm/vault/:address/unlock', source: '/farm/vault/:address/account/:id/unlock',
destination: '/farm', destination: '/farm',
permanent: true, permanent: true,
}, },
{ {
source: '/farm/vault/:address/close', source: '/farm/vault/:address/account/:id/close',
destination: '/farm', destination: '/farm',
permanent: true, permanent: true,
}, },
{ {
source: '/farm/vault/:address/repay', source: '/farm/vault/:address/account/:id/repay',
destination: '/farm', destination: '/farm',
permanent: true, permanent: true,
}, },

View File

@ -1,7 +1,7 @@
{ {
"name": "mars", "name": "mars",
"homepage": "./", "homepage": "./",
"version": "1.4.6", "version": "1.4.7",
"license": "SEE LICENSE IN LICENSE FILE", "license": "SEE LICENSE IN LICENSE FILE",
"private": false, "private": false,
"scripts": { "scripts": {

View File

@ -18,10 +18,10 @@ import {
useUsdPrice, useUsdPrice,
useUserBalance, useUserBalance,
useUserDebt, useUserDebt,
useUserDeposit,
useUserIcns, useUserIcns,
} from 'hooks/queries' } from 'hooks/queries'
import { useSpotPrice } from 'hooks/queries/useSpotPrice' import { useSpotPrice } from 'hooks/queries/useSpotPrice'
import { useUserCollaterals } from 'hooks/queries/useUserCollaterals'
import { ReactNode, useEffect, useState } from 'react' import { ReactNode, useEffect, useState } from 'react'
import useStore from 'store' import useStore from 'store'
import { State } from 'types/enums' import { State } from 'types/enums'
@ -155,8 +155,8 @@ export const CommonContainer = ({ children }: CommonContainerProps) => {
useRedBank() useRedBank()
useUserBalance() useUserBalance()
useUserIcns() useUserIcns()
useUserDeposit()
useUserDebt() useUserDebt()
useUserCollaterals()
useMarsOracle() useMarsOracle()
useSpotPrice(MARS_SYMBOL) useSpotPrice(MARS_SYMBOL)
useUsdPrice() useUsdPrice()

View File

@ -1,9 +1,9 @@
import { useWalletManager, WalletConnectionStatus } from '@marsprotocol/wallet-connector' import { useWalletManager, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
import classNames from 'classnames' 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 { FieldsNotConnected } from 'components/fields'
import { RedbankNotConnected } from 'components/redbank' 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 { useAnimations } from 'hooks/data'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
@ -14,6 +14,9 @@ type Props = {
} }
export const Layout = ({ children }: Props) => { export const Layout = ({ children }: Props) => {
const alreadyAcceptedTOS = localStorage.getItem(TERMS_OF_SERVICE)
const currentlyAcceptedROS = useStore((s) => s.acceptedTermsOfService)
const router = useRouter() const router = useRouter()
const { status } = useWalletManager() const { status } = useWalletManager()
useAnimations() useAnimations()
@ -35,6 +38,8 @@ export const Layout = ({ children }: Props) => {
return ( return (
<div className={classNames('app', !enableAnimations && 'no-motion')}> <div className={classNames('app', !enableAnimations && 'no-motion')}>
{alreadyAcceptedTOS || currentlyAcceptedROS ? null : <TermsOfService />}
<div className={backgroundClasses} id='bg' /> <div className={backgroundClasses} id='bg' />
<Header /> <Header />
<div className='appContainer'> <div className='appContainer'>

View File

@ -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);
}
}
}

View 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>
)
}

View File

@ -6,6 +6,23 @@
@include layoutTooltip; @include layoutTooltip;
min-width: rem-calc(280); 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 { .item {
@include padding(1, 0); @include padding(1, 0);
display: flex; display: flex;

View File

@ -5,33 +5,45 @@ import { useTranslation } from 'react-i18next'
import styles from './Apy.module.scss' import styles from './Apy.module.scss'
interface VaultRate {
total: number
borrow: number
}
interface Props { interface Props {
apyData: VaultRate apyData: ApyBreakdown | PositionApyBreakdown
borrowRate: number
leverage: number leverage: number
} }
export const Apy = ({ apyData, leverage }: Props) => { export const Apy = ({ apyData, leverage, borrowRate }: Props) => {
const { t } = useTranslation() const { t } = useTranslation()
const totalApy = useMemo(() => apyData.total * leverage - apyData.borrow, [apyData, leverage]) const totalApy = useMemo(
const leveragedApy = useMemo(() => apyData.total * leverage, [apyData, leverage]) () => (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 ( return (
<div className={styles.container}> <div className={styles.container}>
<p className='sub2'>{t('fields.apyBreakdown')}</p> <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 && ( {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}> <div className={styles.item}>
<span className={styles.label}> <span className={styles.label}>
{t('fields.leveragedApy', { {t('fields.leveragedApy', {
@ -42,11 +54,19 @@ export const Apy = ({ apyData, leverage }: Props) => {
{formatValue(leveragedApy, 2, 2, true, false, '%', true)} {formatValue(leveragedApy, 2, 2, true, false, '%', true)}
</span> </span>
</div> </div>
{apyData.borrow > 0 && ( {borrowRate > 0 && (
<div className={classNames(styles.item, styles.border)}> <div className={classNames(styles.item, !performanceFee && styles.border)}>
<span className={styles.label}>{t('fields.borrowRateApy')}</span> <span className={styles.label}>{t('fields.borrowRateApy')}</span>
<span className={styles.value}> <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> </span>
</div> </div>
)} )}

View File

@ -30,6 +30,7 @@ export { MobileNav } from './MobileNav/MobileNav'
export { Notification } from './Notification/Notification' export { Notification } from './Notification/Notification'
export { NumberInput } from './NumberInput/NumberInput' export { NumberInput } from './NumberInput/NumberInput'
export { SVG } from './SVG/SVG' export { SVG } from './SVG/SVG'
export { TermsOfService } from './TermsOfService/TermsOfService'
export { TextTooltip } from './TextTooltip/TextTooltip' export { TextTooltip } from './TextTooltip/TextTooltip'
export { Title } from './Title/Title' export { Title } from './Title/Title'
export { Toggle } from './Toggle/Toggle' export { Toggle } from './Toggle/Toggle'

View File

@ -32,6 +32,7 @@ export const EditContent = (props: Props) => {
denom: props.vault.denoms.primary, denom: props.vault.denoms.primary,
amount: primaryAmount.toString(), amount: primaryAmount.toString(),
}} }}
maxDecimals={6}
className={styles.marginRight} className={styles.marginRight}
showSymbol showSymbol
/> />
@ -43,6 +44,7 @@ export const EditContent = (props: Props) => {
denom: props.vault.denoms.secondary, denom: props.vault.denoms.secondary,
amount: secondaryAmount.toString(), amount: secondaryAmount.toString(),
}} }}
maxDecimals={6}
className={styles.marginRight} className={styles.marginRight}
showSymbol showSymbol
/> />
@ -54,6 +56,7 @@ export const EditContent = (props: Props) => {
denom: props.position.borrowDenom || props.vault.denoms.secondary, denom: props.position.borrowDenom || props.vault.denoms.secondary,
amount: borrowedAmount.toString(), amount: borrowedAmount.toString(),
}} }}
maxDecimals={6}
className={styles.marginRight} className={styles.marginRight}
showSymbol showSymbol
/> />
@ -113,6 +116,7 @@ export const EditContent = (props: Props) => {
amount: (difference / 2).toString(), amount: (difference / 2).toString(),
}).toString(), }).toString(),
}} }}
maxDecimals={6}
className={styles.marginRight} className={styles.marginRight}
showSymbol showSymbol
/> />
@ -125,6 +129,7 @@ export const EditContent = (props: Props) => {
amount: (difference / 2).toString(), amount: (difference / 2).toString(),
}).toString(), }).toString(),
}} }}
maxDecimals={6}
className={styles.marginRight} className={styles.marginRight}
showSymbol showSymbol
/> />

View File

@ -21,6 +21,7 @@ export const RepayContent = (props: Props) => {
denom: props.vault.denoms.secondary, denom: props.vault.denoms.secondary,
amount: props.repayAmount.toString(), amount: props.repayAmount.toString(),
}} }}
maxDecimals={6}
showSymbol showSymbol
/> />
</li> </li>
@ -31,6 +32,7 @@ export const RepayContent = (props: Props) => {
denom: props.vault.denoms.secondary, denom: props.vault.denoms.secondary,
amount: props.repayAmount.toString(), amount: props.repayAmount.toString(),
}} }}
maxDecimals={6}
showSymbol showSymbol
/> />
</li> </li>

View File

@ -26,6 +26,7 @@ export const UnlockContent = (props: Props) => {
props.position.amounts.borrowedSecondary, props.position.amounts.borrowedSecondary,
).toString(), ).toString(),
}} }}
maxDecimals={6}
showSymbol showSymbol
/> />
</> </>
@ -38,6 +39,7 @@ export const UnlockContent = (props: Props) => {
denom: props.vault.denoms.primary, denom: props.vault.denoms.primary,
amount: props.position.amounts.primary.toString(), amount: props.position.amounts.primary.toString(),
}} }}
maxDecimals={6}
className={styles.marginRight} className={styles.marginRight}
showSymbol showSymbol
/> />
@ -49,6 +51,7 @@ export const UnlockContent = (props: Props) => {
denom: props.vault.denoms.secondary, denom: props.vault.denoms.secondary,
amount: props.position.amounts.secondary.toString(), amount: props.position.amounts.secondary.toString(),
}} }}
maxDecimals={6}
showSymbol showSymbol
/> />
</> </>

View File

@ -52,13 +52,13 @@ export const ActiveVaultsTable = () => {
const handleRowClick = (vault: ActiveVault) => { const handleRowClick = (vault: ActiveVault) => {
switch (vault.position.status) { switch (vault.position.status) {
case 'active': case 'active':
router.push(`/farm/vault/${vault.address}/edit`) router.push(`/farm/vault/${vault.address}/account/${vault.position.accountId}/edit`)
return return
case 'unlocked': case 'unlocked':
router.push(`/farm/vault/${vault.address}/close`) router.push(`/farm/vault/${vault.address}/account/${vault.position.accountId}/close`)
return return
case 'unlocking': case 'unlocking':
router.push(`/farm/vault/${vault.address}/repay`) router.push(`/farm/vault/${vault.address}/account/${vault.position.accountId}/repay`)
return return
} }
} }

View File

@ -88,10 +88,8 @@ export const ActiveVaultsTableMobile = () => {
} }
tooltip={ tooltip={
<Apy <Apy
apyData={{ apyData={vault.position.apy}
borrow: vault.position.apy.borrow, borrowRate={vault.position.apy.borrow}
total: vault.position.apy.total,
}}
leverage={vault.position.currentLeverage} leverage={vault.position.currentLeverage}
/> />
} }

View File

@ -259,10 +259,6 @@ export const useActiveVaultsColumns = () => {
if (row.original.position.apy?.net !== null) { if (row.original.position.apy?.net !== null) {
const apy = new BigNumber(row.original.position.apy.net).toNumber() const apy = new BigNumber(row.original.position.apy.net).toNumber()
const apyData = {
total: row.original.apy || 0,
borrow: row.original.position.apy.borrow,
}
return ( return (
<> <>
<TextTooltip <TextTooltip
@ -276,7 +272,11 @@ export const useActiveVaultsColumns = () => {
</> </>
} }
tooltip={ 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 ( return (
<Button <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' color='quaternary'
prefix={prefix} prefix={prefix}
variant='round' variant='round'

View File

@ -30,17 +30,16 @@ export const AvailableVaultsTableMobile = () => {
(asset) => asset.denom === vault.denoms.secondary, (asset) => asset.denom === vault.denoms.secondary,
) )
const borrowRate = Math.min( const borrowRate = Math.min(
Number(primaryBorrowAsset?.borrowRate ?? 0), Number(primaryBorrowAsset?.borrowRate || 1000),
Number(secondaryBorrowAsset?.borrowRate ?? 0), Number(secondaryBorrowAsset?.borrowRate || 1000),
) )
const maxBorrowRate = borrowRate * (ltvToLeverage(vault.ltv.contract) - 1) 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 leverage = ltvToLeverage(vault.ltv.contract)
const maxAPY = const maxAPY =
new BigNumber(minAPY).times(leverage).decimalPlaces(2).toNumber() - maxBorrowRate 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 ( return (
<Link <Link
key={`${vault.address}-${i}`} key={`${vault.address}-${i}`}
@ -61,15 +60,16 @@ export const AvailableVaultsTableMobile = () => {
<span> <span>
<TextTooltip <TextTooltip
hideStyling hideStyling
text={<AnimatedNumber amount={minAPY} suffix=' - ' />} text={<AnimatedNumber amount={Math.min(minAPY, maxAPY)} suffix=' - ' />}
tooltip={<Apy apyData={apyDataNoLev} leverage={1} />} tooltip={<Apy apyData={vault.apy} borrowRate={0} leverage={1} />}
/> />
<TextTooltip <TextTooltip
hideStyling hideStyling
text={<AnimatedNumber amount={maxAPY} suffix='%' />} text={<AnimatedNumber amount={Math.max(minAPY, maxAPY)} suffix='%' />}
tooltip={ tooltip={
<Apy <Apy
apyData={apyDataLev} apyData={vault.apy}
borrowRate={maxBorrowRate}
leverage={ltvToLeverage(vault.ltv.contract)} leverage={ltvToLeverage(vault.ltv.contract)}
/> />
} }

View File

@ -103,18 +103,19 @@ export const useAvailableVaultsColumns = () => {
const maxBorrowRate = borrowRate * (ltvToLeverage(row.original.ltv.contract) - 1) 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 maxAPY = new BigNumber(minAPY).times(maxLeverage).toNumber() - maxBorrowRate
const minDailyAPY = new BigNumber(convertApyToDailyApy(row.original.apy))
const minDailyAPY = new BigNumber(convertApyToDailyApy(minAPY))
.decimalPlaces(2) .decimalPlaces(2)
.toNumber() .toNumber()
const maxDailyAPY = new BigNumber(minDailyAPY) const maxDailyAPY = new BigNumber(minDailyAPY)
.times(maxLeverage) .times(maxLeverage)
.decimalPlaces(2) .decimalPlaces(2)
.toNumber() .toNumber()
const apyDataNoLev = { total: row.original.apy || 0, borrow: 0 } const apyDataNoLev = { ...row.original.apy }
const apyDataLev = { total: row.original.apy || 0, borrow: maxBorrowRate } const apyDataLev = { ...row.original.apy }
return ( return (
<> <>
@ -122,26 +123,37 @@ export const useAvailableVaultsColumns = () => {
hideStyling hideStyling
text={ text={
<AnimatedNumber <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> <span> - </span>
<TextTooltip <TextTooltip
hideStyling hideStyling
text={ text={
<AnimatedNumber <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='%' suffix='%'
/> />
} }
tooltip={ 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>
</> </>
) )
}, },

View File

@ -97,7 +97,7 @@ export const BreakdownTable = (props: Props) => {
denom, denom,
amount: amount.toString(), amount: amount.toString(),
}} }}
maxDecimals={2} maxDecimals={6}
/> />
) )
} }
@ -225,12 +225,7 @@ export const BreakdownTable = (props: Props) => {
) )
const trueBorrowRate = borrowRate * (Number(currentLeverage) - 1) const trueBorrowRate = borrowRate * (Number(currentLeverage) - 1)
const apy = (props.vault.apy || 0) * currentLeverage - trueBorrowRate const apy = (props.vault.apy.total || 0) * currentLeverage - trueBorrowRate
const apyData = {
total: props.vault.apy || 0,
borrow: trueBorrowRate,
}
return ( return (
<div className={containerClasses}> <div className={containerClasses}>
@ -251,7 +246,13 @@ export const BreakdownTable = (props: Props) => {
abbreviated={false} abbreviated={false}
/> />
} }
tooltip={<Apy apyData={apyData} leverage={currentLeverage} />} tooltip={
<Apy
apyData={props.vault.apy}
borrowRate={trueBorrowRate}
leverage={currentLeverage}
/>
}
/> />
) : ( ) : (
<Loading /> <Loading />

View File

@ -13,8 +13,8 @@ export const LiquidationNotification = () => {
const elligiblePositions = activeVaults.filter((vault) => vault.position.ltv > vault.ltv.contract) const elligiblePositions = activeVaults.filter((vault) => vault.position.ltv > vault.ltv.contract)
const repayHandler = (address: string) => { const repayHandler = (address: string, accountId: string) => {
router.push(`/farm/vault/${address}/repay`) router.push(`/farm/vault/${address}/account/${accountId}/repay`)
return return
} }
@ -33,7 +33,12 @@ export const LiquidationNotification = () => {
})} })}
</span> </span>
<Button <Button
onClick={() => repayHandler(elligiblePositions[0].address)} onClick={() =>
repayHandler(
elligiblePositions[0].address,
elligiblePositions[0].position.accountId,
)
}
color='tertiary' color='tertiary'
text={t('fields.notifications.liquidation.single.button')} text={t('fields.notifications.liquidation.single.button')}
></Button> ></Button>

View File

@ -18,6 +18,7 @@
@include typoXXS; @include typoXXS;
border: none; border: none;
min-height: unset; min-height: unset;
white-space: nowrap;
&:hover, &:hover,
&:focus, &:focus,

View File

@ -1,4 +1,5 @@
import { Coin } from '@cosmjs/proto-signing' import { Coin } from '@cosmjs/proto-signing'
import BigNumber from 'bignumber.js'
import classNames from 'classnames' import classNames from 'classnames'
import { Button, DisplayCurrency, NumberInput } from 'components/common' import { Button, DisplayCurrency, NumberInput } from 'components/common'
import { findByDenom } from 'functions' import { findByDenom } from 'functions'
@ -9,6 +10,8 @@ import useStore from 'store'
import styles from './TokenInput.module.scss' import styles from './TokenInput.module.scss'
BigNumber.config({ EXPONENTIAL_AT: [-24, 20] })
interface Props { interface Props {
tokens: string[] tokens: string[]
input: Input input: Input
@ -70,7 +73,7 @@ export const TokenInput = (props: Props) => {
color='quaternary' color='quaternary'
className={`xxsCaps faded ${styles.maxBtn}`} className={`xxsCaps faded ${styles.maxBtn}`}
onClick={() => onValueEntered(maxAmount)} onClick={() => onValueEntered(maxAmount)}
text={`${props.maxAmountLabel}: ${maxAmount / 10 ** asset.decimals}`} text={`${props.maxAmountLabel}: ${new BigNumber(maxAmount).shiftedBy(-1 * asset.decimals)}`}
variant='transparent' variant='transparent'
/> />
<div className={styles.input}> <div className={styles.input}>

View File

@ -20,7 +20,9 @@ export const UnlockedNotification = () => {
if (!vaultsUnlocked.length) return null if (!vaultsUnlocked.length) return null
const exitVaultHandler = () => { 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 = () => { const unlockedContent = () => {

View File

@ -125,7 +125,7 @@ export const VAULT_CONFIGS: Vault[] = [
secondary: 'USDC.n', secondary: 'USDC.n',
}, },
color: colors.usdc, color: colors.usdc,
lockup: 86400 * 14, lockup: 86400 * 1,
provider: 'Apollo vault', provider: 'Apollo vault',
description: { maxLeverage: 1.43, lpName: 'OSMO-USDC.n' }, description: { maxLeverage: 1.43, lpName: 'OSMO-USDC.n' },
ltv: { ltv: {
@ -133,7 +133,12 @@ export const VAULT_CONFIGS: Vault[] = [
contract: 0.3, contract: 0.3,
liq: 0.4, liq: 0.4,
}, },
apy: 0, apy: {
apys: null,
fees: null,
total: null,
vaultAddress: '',
},
}, },
{ {
address: 'osmo14lu7m4ganxs20258dazafrjfaulmfxruq9n0r0th90gs46jk3tuqwfkqwn', address: 'osmo14lu7m4ganxs20258dazafrjfaulmfxruq9n0r0th90gs46jk3tuqwfkqwn',
@ -148,7 +153,7 @@ export const VAULT_CONFIGS: Vault[] = [
secondary: 'USDC.n', secondary: 'USDC.n',
}, },
color: colors.usdc, color: colors.usdc,
lockup: 86400 * 14, lockup: 86400 * 7,
provider: 'Apollo vault', provider: 'Apollo vault',
description: { maxLeverage: 1.43, lpName: 'OSMO-USDC.n' }, description: { maxLeverage: 1.43, lpName: 'OSMO-USDC.n' },
ltv: { ltv: {
@ -156,7 +161,12 @@ export const VAULT_CONFIGS: Vault[] = [
contract: 0.3, contract: 0.3,
liq: 0.4, liq: 0.4,
}, },
apy: 0, apy: {
apys: null,
fees: null,
total: null,
vaultAddress: '',
},
}, },
{ {
address: 'osmo1fmq9hw224fgz8lk48wyd0gfg028kvvzggt6c3zvnaqkw23x68cws5nd5em', address: 'osmo1fmq9hw224fgz8lk48wyd0gfg028kvvzggt6c3zvnaqkw23x68cws5nd5em',
@ -179,6 +189,11 @@ export const VAULT_CONFIGS: Vault[] = [
contract: 0.3, contract: 0.3,
liq: 0.4, liq: 0.4,
}, },
apy: 0, apy: {
apys: null,
fees: null,
total: null,
vaultAddress: '',
},
}, },
] ]

View File

@ -171,7 +171,12 @@ export const VAULT_CONFIGS: Vault[] = [
contract: 0.63, contract: 0.63,
liq: 0.65, liq: 0.65,
}, },
apy: 0, apy: {
apys: null,
fees: null,
total: null,
vaultAddress: '',
},
}, },
{ {
address: 'osmo1jfmwayj8jqp9tfy4v4eks5c2jpnqdumn8x8xvfllng0wfes770qqp7jl4j', address: 'osmo1jfmwayj8jqp9tfy4v4eks5c2jpnqdumn8x8xvfllng0wfes770qqp7jl4j',
@ -194,7 +199,12 @@ export const VAULT_CONFIGS: Vault[] = [
contract: 0.65, contract: 0.65,
liq: 0.66, liq: 0.66,
}, },
apy: 0, apy: {
apys: null,
fees: null,
total: null,
vaultAddress: '',
},
}, },
{ {
address: 'osmo1a6tcf60pyz8qq2n532dzcs7s7sj8klcmra04tvaqympzcvxqg9esn7xz7l', address: 'osmo1a6tcf60pyz8qq2n532dzcs7s7sj8klcmra04tvaqympzcvxqg9esn7xz7l',
@ -217,6 +227,11 @@ export const VAULT_CONFIGS: Vault[] = [
contract: 0.61, contract: 0.61,
liq: 0.625, liq: 0.625,
}, },
apy: 0, apy: {
apys: null,
fees: null,
total: null,
vaultAddress: '',
},
}, },
] ]

View File

@ -37,3 +37,4 @@ export const FIELDS_TUTORIAL_KEY = 'fieldsHideTutorial'
export const RED_BANK_TUTORIAL_KEY = 'redbankHideTutorial' export const RED_BANK_TUTORIAL_KEY = 'redbankHideTutorial'
export const DISPLAY_CURRENCY_KEY = 'displayCurrency' export const DISPLAY_CURRENCY_KEY = 'displayCurrency'
export const ENABLE_ANIMATIONS_KEY = 'enableAnimations' export const ENABLE_ANIMATIONS_KEY = 'enableAnimations'
export const TERMS_OF_SERVICE = 'termsOfService'

View File

@ -22,9 +22,12 @@ export const DEFAULT_POSITION: Position = {
net: 0, net: 0,
}, },
apy: { apy: {
borrow: 5.2, apys: null,
net: 7.7, fees: null,
total: 19, total: 19,
borrow: 5.2,
net: 13.8,
vaultAddress: '',
}, },
ltv: 0.5, ltv: 0.5,
currentLeverage: 1, currentLeverage: 1,

View File

@ -12,7 +12,7 @@ export const getTokenValueFromCoins = (assets: Asset[], coins: Coin[] = []) => {
return formatValue( return formatValue(
convertedValue, convertedValue,
2, 2,
2, asset.decimals,
true, true,
convertedValue >= 0.01 ? false : '>', convertedValue >= 0.01 ? false : '>',
` ${asset.symbol}`, ` ${asset.symbol}`,

View File

@ -1,7 +0,0 @@
export const getDebtQuery = (address: string) => {
return `
{
user_debts: { user: "${address}" }
}`
}
1

View File

@ -3,7 +3,6 @@ import {
getContractQuery, getContractQuery,
getIncentiveQuery, getIncentiveQuery,
getMarketQuery, getMarketQuery,
getUserCollateralQuery,
getUserIncentivesQuery, getUserIncentivesQuery,
} from '.' } from '.'
@ -47,14 +46,6 @@ export const getRedbankQuery = (
return `query RedbankQuery { return `query RedbankQuery {
${REDBANK_WASM_KEY}: wasm { ${REDBANK_WASM_KEY}: wasm {
${wasmQueries} ${wasmQueries}
${
address &&
getContractQuery(
'collateral',
redBankContractAddress || '',
getUserCollateralQuery(address),
)
}
${ ${
address && address &&
getContractQuery( getContractQuery(

View File

@ -1,7 +0,0 @@
export const getUserCollateralQuery = (address: string) => {
return `{
user_collaterals: {
user: "${address}"
}
}`
}

View File

@ -2,7 +2,6 @@
export { getBalanceQuery } from './getBalanceQuery' export { getBalanceQuery } from './getBalanceQuery'
export { getConfigQuery } from './getConfigQuery' export { getConfigQuery } from './getConfigQuery'
export { getContractQuery } from './getContractQuery' export { getContractQuery } from './getContractQuery'
export { getDebtQuery } from './getDebtQuery'
export { getDepositDebtQuery } from './getDepositDebtQuery' export { getDepositDebtQuery } from './getDepositDebtQuery'
export { getDepositsQuery } from './getDepositsQuery' export { getDepositsQuery } from './getDepositsQuery'
export { getGlobalStateQuery } from './getGlobalStateQuery' export { getGlobalStateQuery } from './getGlobalStateQuery'
@ -13,6 +12,5 @@ export { getRedbankQuery } from './getRedbankQuery'
export { getSnapshotQuery } from './getSnapshotQuery' export { getSnapshotQuery } from './getSnapshotQuery'
export { getStateQuery } from './getStateQuery' export { getStateQuery } from './getStateQuery'
export { getUncollaterisedLoanLimitQuery } from './getUncollaterisedLoanLimitQuery' export { getUncollaterisedLoanLimitQuery } from './getUncollaterisedLoanLimitQuery'
export { getUserCollateralQuery } from './getUserCollateralQuery'
export { getUserIncentivesQuery } from './getUserIncentivesQuery' export { getUserIncentivesQuery } from './getUserIncentivesQuery'
// @endindex // @endindex

View File

@ -1,11 +1,11 @@
import { useMemo } from 'react' import { useMemo } from 'react'
import useStore from 'store' import useStore from 'store'
export const useActiveVault = (address: string) => { export const useActiveVault = (accountId: string) => {
const activeVaults = useStore((s) => s.activeVaults) const activeVaults = useStore((s) => s.activeVaults)
return useMemo(() => { return useMemo(() => {
if (!activeVaults?.length) return if (!activeVaults?.length) return
return activeVaults.find((activeVault) => activeVault.address === address) return activeVaults.find((activeVault) => activeVault.position.accountId === accountId)
}, [activeVaults, address]) }, [activeVaults, accountId])
} }

View File

@ -1,6 +1,6 @@
import useStore from 'store' import useStore from 'store'
export const usePrice = (denom: string): number => { export const usePrice = (denom: string): number => {
const convertToBaseCurrency = useStore((s) => s.convertToBaseCurrency) const getExchangeRate = useStore((s) => s.getExchangeRate)
return convertToBaseCurrency({ denom, amount: '1' }) return getExchangeRate(denom)
} }

View File

@ -14,7 +14,7 @@ export { useSpotPrice } from './useSpotPrice'
export { useUnlockMessages } from './useUnlockMessages' export { useUnlockMessages } from './useUnlockMessages'
export { useUsdPrice } from './useUsdPrice' export { useUsdPrice } from './useUsdPrice'
export { useUserBalance } from './useUserBalance' export { useUserBalance } from './useUserBalance'
export { useUserCollaterals } from './useUserCollaterals'
export { useUserDebt } from './useUserDebt' export { useUserDebt } from './useUserDebt'
export { useUserDeposit } from './useUserDeposit'
export { useUserIcns } from './useUserIcns' export { useUserIcns } from './useUserIcns'
// @endindex // @endindex

View 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,
},
)
}

View File

@ -1,55 +1,67 @@
import { Coin } from '@cosmjs/stargate'
import { useQuery } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query'
import { getContractQuery, getDebtQuery } from 'functions/queries'
import { gql, request } from 'graphql-request'
import useStore from 'store' import useStore from 'store'
import { QUERY_KEYS } from 'types/enums/queryKeys' import { QUERY_KEYS } from 'types/enums/queryKeys'
const QUERY_LIMIT = 10
export interface UserDebtData { export interface UserDebtData {
debts: { denom: string
debts: [ amount_scaled: string
{ amount: string
denom: string enabled: boolean
amount_scaled: string
amount: string
enabled: boolean
},
]
}
} }
// ! Implement pagination. Currently there is a limit of 5 assets from the SC
export const useUserDebt = () => { export const useUserDebt = () => {
const hiveUrl = useStore((s) => s.networkConfig?.hiveUrl)
const userWalletAddress = useStore((s) => s.userWalletAddress) const userWalletAddress = useStore((s) => s.userWalletAddress)
const redbankContractAddress = useStore((s) => s.networkConfig?.contracts.redBank) const redbankContractAddress = useStore((s) => s.networkConfig?.contracts.redBank)
const processUserDebtQuery = useStore((s) => s.processUserDebtQuery) const client = useStore((s) => s.client)
const debtsQuery = getContractQuery( const resolveDebtResponse = (debts: UserDebtData[]): Coin[] => {
'debts', return debts.map((debt) => {
redbankContractAddress || '', return {
getDebtQuery(userWalletAddress), 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], [QUERY_KEYS.USER_DEBT],
async () => { async () => {
return await request( let userDebts: Coin[] = []
hiveUrl!, if (!redbankContractAddress) return userDebts
gql`
query UserDebtQuery { let isMoreDebts = true
debts: wasm {
${debtsQuery} 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: enabled: !!redbankContractAddress && !!userWalletAddress && !!client,
!!hiveUrl && !!redbankContractAddress && !!processUserDebtQuery && !!userWalletAddress,
staleTime: 30000, staleTime: 30000,
refetchInterval: 30000, refetchInterval: 30000,
onSuccess: processUserDebtQuery,
}, },
) )
} }

View File

@ -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,
},
)
}

View File

@ -22,9 +22,12 @@ export const position: Position = {
net: 0, net: 0,
}, },
apy: { apy: {
borrow: 5.2, apys: null,
net: 7.7, fees: null,
total: 19, total: 19,
borrow: 5.2,
net: 13.8,
vaultAddress: '',
}, },
ltv: 0.5, ltv: 0.5,
currentLeverage: 1, currentLeverage: 1,

View File

@ -125,26 +125,6 @@ export const redBankData: RedBankData = {
index: '0.000000143742920378', index: '0.000000143742920378',
last_updated: 1678369620, 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', unclaimedRewards: '4679062',
}, },
} }

View File

@ -1,6 +1,11 @@
export const vault: Vault = { export const vault: Vault = {
address: 'test', address: 'test',
apy: 10, apy: {
apys: null,
fees: null,
total: null,
vaultAddress: '',
},
color: 'test', color: 'test',
denoms: { denoms: {
primary: 'OSMO', primary: 'OSMO',

View File

@ -11,7 +11,8 @@ const CloseVaultPosition = () => {
const vaultConfigs = useStore((s) => s.vaultConfigs) const vaultConfigs = useStore((s) => s.vaultConfigs)
const { mutate, data, isLoading, error } = useUpdateAccount() const { mutate, data, isLoading, error } = useUpdateAccount()
const vaultAddress = String(router.query.address) const vaultAddress = String(router.query.address)
const activeVault = useActiveVault(vaultAddress) const accountId = String(router.query.id)
const activeVault = useActiveVault(accountId)
const { closeActions, closeFee } = useClosePosition({ const { closeActions, closeFee } = useClosePosition({
activeVault, activeVault,
isLoading: isLoading || !!data || !!error, isLoading: isLoading || !!data || !!error,

View File

@ -113,7 +113,9 @@ const EditVault = (props: Props) => {
const handleUnlockClick = useCallback(() => { const handleUnlockClick = useCallback(() => {
if (!unlockFee) return if (!unlockFee) return
if (showDisclaimer) { if (showDisclaimer) {
router.push(`/farm/vault/${props.activeVault.address}/unlock`) router.push(
`/farm/vault/${props.activeVault.address}/account/${props.activeVault.position.accountId}/unlock`,
)
return return
} }
@ -125,6 +127,7 @@ const EditVault = (props: Props) => {
}) })
}, [ }, [
props.activeVault.address, props.activeVault.address,
props.activeVault.position.accountId,
showDisclaimer, showDisclaimer,
router, router,
unlockFee, unlockFee,

View File

@ -7,8 +7,8 @@ import EditVault from './EditVault'
const Edit = () => { const Edit = () => {
const router = useRouter() const router = useRouter()
const vaultAddress = String(router.query.address) const accountId = String(router.query.id)
const activeVault = useActiveVault(vaultAddress) const activeVault = useActiveVault(accountId)
if (!activeVault) return <></> if (!activeVault) return <></>

View File

@ -7,8 +7,8 @@ import RepayVault from './RepayVault'
const Repay = () => { const Repay = () => {
const router = useRouter() const router = useRouter()
const vaultAddress = String(router.query.address) const accountId = String(router.query.id)
const activeVault = useActiveVault(vaultAddress) const activeVault = useActiveVault(accountId)
if (!activeVault) return <></> if (!activeVault) return <></>

View File

@ -12,8 +12,8 @@ import styles from './UnlockDisclaimer.module.scss'
const Unlock = () => { const Unlock = () => {
const { t } = useTranslation() const { t } = useTranslation()
const router = useRouter() const router = useRouter()
const address = String(router.query.address) const accountId = String(router.query.id)
const activeVault = useActiveVault(address) const activeVault = useActiveVault(accountId)
const { const {
mutate: requestUnlock, mutate: requestUnlock,
data: unlockData, data: unlockData,

View File

@ -43,6 +43,7 @@ export interface CommonSlice {
networkConfig?: NetworkConfig networkConfig?: NetworkConfig
otherAssets: Asset[] otherAssets: Asset[]
queryErrors: string[] queryErrors: string[]
acceptedTermsOfService: boolean
slippage: number slippage: number
tutorialSteps: { redbank: number; fields: number } tutorialSteps: { redbank: number; fields: number }
userBalances: Coin[] userBalances: Coin[]

View File

@ -1,6 +1,4 @@
import { Coin } from '@cosmjs/stargate' import { Coin } from '@cosmjs/stargate'
import { UserDebtData } from 'hooks/queries/useUserDebt'
import { UserDepositData } from 'hooks/queries/useUserDeposit'
import { State } from 'types/enums' import { State } from 'types/enums'
export interface RedBankSlice { export interface RedBankSlice {
@ -38,9 +36,5 @@ export interface RedBankSlice {
// QUERY RELATED // QUERY RELATED
// ------------------ // ------------------
previousRedBankQueryData?: RedBankData previousRedBankQueryData?: RedBankData
previousUserDebtQueryData?: UserDebtData
previousUserDepositQueryData?: UserDepositData
processRedBankQuery: (data: RedBankData, whitelistedAssets: Asset[]) => void processRedBankQuery: (data: RedBankData, whitelistedAssets: Asset[]) => void
processUserDebtQuery: (data: UserDebtData) => void
processUserDepositQuery: (data: UserDepositData) => void
} }

View File

@ -5,14 +5,14 @@ export interface VaultsSlice {
availableVaults: Vault[] availableVaults: Vault[]
activeVaults: ActiveVault[] activeVaults: ActiveVault[]
creditAccounts?: Positions[] creditAccounts?: Positions[]
addAprToVaults: (aprs: AprData[]) => void addApyToVaults: (apys: ApyBreakdown[]) => void
getCreditAccounts: (options?: Options) => Promise<Positions[]> getCreditAccounts: (options?: Options) => Promise<Positions[]>
vaultAssets?: VaultCoinsWithAddress[] vaultAssets?: VaultCoinsWithAddress[]
getVaultAssets: (options?: Options) => Promise<VaultCoinsWithAddress[]> getVaultAssets: (options?: Options) => Promise<VaultCoinsWithAddress[]>
unlockTimes?: UnlockTimeWithAddress[] unlockTimes?: UnlockTimeWithAddress[]
getUnlockTimes: (options?: Options) => Promise<UnlockTimeWithAddress[]> getUnlockTimes: (options?: Options) => Promise<UnlockTimeWithAddress[]>
aprs?: AprData[] | null apys?: ApyBreakdown[] | null
getAprs: (options?: Options) => Promise<null> getApys: (options?: Options) => Promise<null>
caps?: VaultCapData[] caps?: VaultCapData[]
getCaps: (options?: Options) => Promise<VaultCapData[]> getCaps: (options?: Options) => Promise<VaultCapData[]>
lpTokens?: LpTokenWithAddress[] lpTokens?: LpTokenWithAddress[]

View File

@ -45,6 +45,7 @@ const commonSlice = (
marketDebts: [], marketDebts: [],
otherAssets: [], otherAssets: [],
queryErrors: [], queryErrors: [],
acceptedTermsOfService: false,
slippage: 0.02, slippage: 0.02,
tutorialSteps: { redbank: 1, fields: 1 }, tutorialSteps: { redbank: 1, fields: 1 },
userBalances: [], userBalances: [],
@ -65,8 +66,16 @@ const commonSlice = (
(exchangeRate) => exchangeRate.denom === coin.denom, (exchangeRate) => exchangeRate.denom === coin.denom,
)?.amount )?.amount
if (!exchangeRate) return 0 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) => { convertValueToAmount: (coin: Coin) => {
const exchangeRates = get().exchangeRates const exchangeRates = get().exchangeRates

View File

@ -2,8 +2,6 @@ import { Coin } from '@cosmjs/stargate'
import { MARS_SYMBOL } from 'constants/appConstants' import { MARS_SYMBOL } from 'constants/appConstants'
import { SECONDS_IN_YEAR } from 'constants/timeConstants' import { SECONDS_IN_YEAR } from 'constants/timeConstants'
import { findByDenom } from 'functions' import { findByDenom } from 'functions'
import { UserDebtData } from 'hooks/queries/useUserDebt'
import { UserDepositData } from 'hooks/queries/useUserDeposit'
import { demagnify, lookupDenomBySymbol } from 'libs/parse' import { demagnify, lookupDenomBySymbol } from 'libs/parse'
import isEqual from 'lodash.isequal' import isEqual from 'lodash.isequal'
import { RedBankSlice } from 'store/interfaces/redBank.interface' import { RedBankSlice } from 'store/interfaces/redBank.interface'
@ -166,7 +164,6 @@ const redBankSlice = (set: NamedSet<Store>, get: GetState<Store>): RedBankSlice
marketInfo, marketInfo,
marketIncentiveInfo, marketIncentiveInfo,
previousRedBankQueryData: data, previousRedBankQueryData: data,
userCollateral: data.rbwasmkey.collateral,
userUnclaimedRewards, userUnclaimedRewards,
redBankState: State.READY, redBankState: State.READY,
}) })
@ -174,30 +171,6 @@ const redBankSlice = (set: NamedSet<Store>, get: GetState<Store>): RedBankSlice
findCollateral: (denom: string) => { findCollateral: (denom: string) => {
return get().userCollateral && get().userCollateral!.find((item) => item.denom === denom) 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 export default redBankSlice

View File

@ -19,21 +19,29 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
isLoading: false, isLoading: false,
availableVaults: [], availableVaults: [],
activeVaults: [], activeVaults: [],
addAprToVaults: (aprs: AprData[]) => { addApyToVaults: (apys: ApyBreakdown[]) => {
const updatedAvailableVaults = get().availableVaults.map((availableVault) => { const updatedAvailableVaults = get().availableVaults.map((availableVault) => {
const apr = const apy = apys?.find((apy) => apy.vaultAddress === availableVault.address)
(aprs?.find((apr) => apr.contractAddress === availableVault.address)?.apr || 0) * 100
availableVault.apy = convertAprToApy(apr, 365) if (!apy) return availableVault
availableVault.apy = apy
return availableVault return availableVault
}) })
const updatedActiveVaults = get().activeVaults.map((activeVault) => { const updatedActiveVaults = get().activeVaults.map((activeVault) => {
const apr = (aprs?.find((apr) => apr.contractAddress === activeVault.address)?.apr || 0) * 100 const apy = apys?.find((apy) => apy.vaultAddress === activeVault.address)
const apy = convertAprToApy(apr, 365)
if (!apy) return activeVault
activeVault.apy = apy activeVault.apy = apy
activeVault.position.apy.total = apy activeVault.position.apy.borrow
activeVault.position.apy.net = activeVault.position.apy = {
apy * activeVault.position.currentLeverage - activeVault.position.apy.borrow ...apy,
borrow: activeVault.position.apy.borrow,
net:
(apy.total || 0) * activeVault.position.currentLeverage - activeVault.position.apy.borrow,
}
return activeVault return activeVault
}) })
@ -93,6 +101,7 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
}, },
}), }),
vaultAddress: lpToken.vaultAddress, vaultAddress: lpToken.vaultAddress,
accountId: lpToken.accountId,
} }
}) })
@ -133,6 +142,7 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
) / 1e6, ) / 1e6,
), ),
vaultAddress: creditAccount.vaults[0].vault.address, 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 return newUnlockTimes
}, },
getAprs: async (options?: Options) => { getApys: async (options?: Options) => {
const aprs = get().aprs const apys = get().apys
if (aprs && !options?.refetch) { if (apys && !options?.refetch) {
get().addAprToVaults(aprs) get().addApyToVaults(apys)
return null return null
} }
const vaultAddresses = get().vaultConfigs.map((vault) => vault.address)
const networkConfig = get().networkConfig const networkConfig = get().networkConfig
if (!networkConfig) return null if (!networkConfig) return null
@ -158,31 +169,40 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
const response = await fetch(networkConfig!.apolloAprUrl) const response = await fetch(networkConfig!.apolloAprUrl)
if (response.ok) { if (response.ok) {
const data: FlatApr[] | NestedApr[] = await response.json() const data: ApolloAprResponse[] = await response.json()
const newAprs = data.map((aprData) => { const filteredData = data.filter((aprData) =>
try { vaultAddresses.includes(aprData.contract_address),
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 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 } const apys = aprData.apr.aprs.map((apr) => ({
} catch { type: apr.type,
const apr = aprData as NestedApr value: new BigNumber(apr.value).dividedBy(aprTotal).multipliedBy(finalApy).toNumber(),
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 finalApr = aprTotal + feeTotal const fees = aprData.apr.fees.map((fee) => ({
return { contractAddress: aprData.contract_address, apr: finalApr } type: fee.type,
value: new BigNumber(fee.value).dividedBy(feeTotal).multipliedBy(finalApy).toNumber(),
}))
return {
vaultAddress: aprData.contract_address,
total: finalApy,
apys,
fees,
} }
}) })
set({ set({
aprs: newAprs, apys: newApys,
}) })
get().addAprToVaults(newAprs) get().addApyToVaults(newApys)
} }
return null return null
@ -256,6 +276,7 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
unlocked: amounts.unlocked, unlocked: amounts.unlocked,
denom: vault?.denoms.lpToken || '', denom: vault?.denoms.lpToken || '',
vaultAddress: creditAccount.vaults[0].vault.address, 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( return Promise.all([vaultAssets, unlockTimes, caps]).then(
([vaultAssets, unlockTimes, caps]) => { ([vaultAssets, unlockTimes, caps]) => {
const { activeVaults, availableVaults } = get().vaultConfigs.reduce( const { activeVaults, availableVaults } = get().vaultConfigs.reduce(
(prev, curr) => { (prev, vaultConfig) => {
const lpTokens = get().lpTokens const lpTokens = get().lpTokens
const creditAccounts = get().creditAccounts const creditAccounts = get().creditAccounts
const creditAccountPosition = creditAccounts?.find( const creditAccountPositions = creditAccounts?.filter(
(position) => position.vaults[0].vault.address === curr.address, (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 // No position = available vault
if (!creditAccountPosition) { if (!creditAccountPositions?.length) {
prev.availableVaults.push(curr) prev.availableVaults.push(vaultConfig)
return prev return prev
} }
// Position = active vault creditAccountPositions.forEach((creditAccountPosition) => {
const primaryAndSecondaryAmount = vaultAssets.find( const primaryAndSecondaryAmount = vaultAssets.find(
(vaultAsset) => vaultAsset.vaultAddress === curr.address, (vaultAsset) => vaultAsset.accountId === creditAccountPosition.account_id,
) )
const vaultTokenAmounts = getAmountsFromActiveVault( const vaultTokenAmounts = getAmountsFromActiveVault(
creditAccountPosition.vaults[0].amount, creditAccountPosition.vaults[0].amount,
) )
const lpTokenAmounts = lpTokens?.find( const lpTokenAmounts = lpTokens?.find(
(lpToken) => lpToken.vaultAddress === curr.address, (lpToken) => lpToken.accountId === creditAccountPosition.account_id,
) )
if (!primaryAndSecondaryAmount || !vaultTokenAmounts || !lpTokenAmounts) { if (!primaryAndSecondaryAmount || !vaultTokenAmounts || !lpTokenAmounts) {
prev.availableVaults.push(curr) prev.availableVaults.push(vaultConfig)
return prev 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)
} }
}
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) { // Should already filter out null values
if (borrowedPrimaryAmount > primaryAmount) { const unlockTime = unlockTimes.find(
const swapped = Math.round( (unlockTime) => unlockTime?.accountId === creditAccountPosition.account_id,
get().convertToBaseCurrency({ )?.unlockAtTimestamp
denom: borrowedDenom,
amount: (borrowedPrimaryAmount - primaryAmount).toString(),
}),
)
const rate = Number( const primaryAmount = Number(
get().exchangeRates?.find((coin) => coin.denom === curr.denoms.secondary) findByDenom(primaryAndSecondaryAmount.coins, vaultConfig.denoms.primary)?.amount ||
?.amount ?? 0, 0,
) )
primarySupplyAmount = 0 const secondaryAmount = Number(
secondarySupplyAmount = Math.floor(secondaryAmount - swapped / rate) 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 { } else {
primarySupplyAmount = primaryAmount - borrowedPrimaryAmount primarySupplyAmount = primaryAmount
secondarySupplyAmount = secondaryAmount secondarySupplyAmount = secondaryAmount
} }
} else if (borrowedDenom === curr.denoms.secondary) {
if (borrowedSecondaryAmount > secondaryAmount) { const borrowedAmount = Math.max(borrowedPrimaryAmount, borrowedSecondaryAmount)
const swapped = Math.round(
get().convertToBaseCurrency({ const convertToBaseCurrency = get().convertToBaseCurrency
denom: borrowedDenom, const redBankAssets = get().redBankAssets
amount: (borrowedSecondaryAmount - secondaryAmount).toString(), const primarySupplyValue = convertToBaseCurrency({
}), denom: vaultConfig.denoms.primary,
) amount: primarySupplyAmount.toString(),
const rate = Number( })
get().exchangeRates?.find((coin) => coin.denom === curr.denoms.primary)?.amount ??
0, const secondarySupplyValue = convertToBaseCurrency({
) denom: vaultConfig.denoms.secondary,
secondarySupplyAmount = 0 amount: secondarySupplyAmount.toString(),
primarySupplyAmount = Math.floor(primaryAmount - swapped / rate) })
} else {
secondarySupplyAmount = secondaryAmount - borrowedSecondaryAmount const borrowedValue = convertToBaseCurrency({
primarySupplyAmount = primaryAmount 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 borrowRate =
const redBankAssets = get().redBankAssets redBankAssets.find((asset) => asset.denom === borrowedDenom)?.borrowRate || 0
const primarySupplyValue = convertToBaseCurrency({
denom: curr.denoms.primary,
amount: primarySupplyAmount.toString(),
})
const secondarySupplyValue = convertToBaseCurrency({ const trueBorrowRate = (leverage - 1) * borrowRate
denom: curr.denoms.secondary,
amount: secondarySupplyAmount.toString(),
})
const borrowedValue = convertToBaseCurrency({ const getPositionStatus = (unlockTime?: number) => {
denom: borrowedDenom, if (!unlockTime) return 'active'
amount: borrowedAmount.toString(),
})
const values = { const isUnlocked = moment(unlockTime).isBefore(new Date())
primary: primarySupplyValue, if (isUnlocked) return 'unlocked'
secondary: secondarySupplyValue,
borrowedPrimary: borrowedDenom === curr.denoms.primary ? borrowedValue : 0,
borrowedSecondary: borrowedDenom === curr.denoms.secondary ? borrowedValue : 0,
net: primarySupplyValue + secondarySupplyValue,
total: primarySupplyValue + secondarySupplyValue + borrowedValue,
}
const leverage = getLeverageFromValues(values) return 'unlocking'
}
const borrowRate = const position: Position = {
redBankAssets.find((asset) => asset.denom === borrowedDenom)?.borrowRate || 0 id: id,
accountId: creditAccountPosition.account_id,
const trueBorrowRate = (leverage - 1) * borrowRate amounts: {
primary: primarySupplyAmount,
const getPositionStatus = (unlockTime?: number) => { secondary: secondarySupplyAmount,
if (!unlockTime) return 'active' borrowedPrimary:
borrowedDenom === vaultConfig.denoms.primary ? borrowedAmount : 0,
const isUnlocked = moment(unlockTime).isBefore(new Date()) borrowedSecondary:
if (isUnlocked) return 'unlocked' borrowedDenom === vaultConfig.denoms.secondary ? borrowedAmount : 0,
lp: {
return 'unlocking' amount: vaultTokenAmounts.unlocking,
} primary: Number(
primaryAndSecondaryAmount.coins.find(
const position: Position = { (coin) => coin.denom === vaultConfig.denoms.primary,
id: id, )?.amount || 0,
accountId: creditAccountPosition.account_id, ),
amounts: { secondary: Number(
primary: primarySupplyAmount, primaryAndSecondaryAmount.coins.find(
secondary: secondarySupplyAmount, (coin) => coin.denom === vaultConfig.denoms.secondary,
borrowedPrimary: borrowedDenom === curr.denoms.primary ? borrowedAmount : 0, )?.amount || 0,
borrowedSecondary: borrowedDenom === curr.denoms.secondary ? borrowedAmount : 0, ),
lp: { },
amount: vaultTokenAmounts.unlocking, vault: vaultTokenAmounts.locked,
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,
),
}, },
vault: vaultTokenAmounts.locked, values,
}, apy: {
values, vaultAddress: vaultConfig.address,
apy: { borrow: trueBorrowRate,
total: null, total: null,
borrow: trueBorrowRate, net: null,
net: null, apys: null,
}, fees: null,
currentLeverage: leverage, },
ltv: leverageToLtv(leverage), currentLeverage: leverage,
...(unlockTime ? { unlockAtTimestamp: unlockTime } : {}), ltv: leverageToLtv(leverage),
status: getPositionStatus(unlockTime), ...(unlockTime ? { unlockAtTimestamp: unlockTime } : {}),
borrowDenom: borrowedDenom, status: getPositionStatus(unlockTime),
} borrowDenom: borrowedDenom,
}
prev.activeVaults.push({ ...curr, position }) prev.activeVaults.push({ ...vaultConfig, position })
})
return prev return prev
}, },
@ -478,7 +518,7 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
) )
set({ activeVaults, availableVaults, isLoading: false }) set({ activeVaults, availableVaults, isLoading: false })
get().getAprs(options) get().getApys(options)
}, },
) )
}, },

View File

@ -16,4 +16,5 @@ export enum QUERY_KEYS {
PROVIDE_LIQUIDITY = 'provideLiquidity', PROVIDE_LIQUIDITY = 'provideLiquidity',
UNLOCK_MESSAGE = 'unlockMessage', UNLOCK_MESSAGE = 'unlockMessage',
USD_PRICE = 'usdPrice', USD_PRICE = 'usdPrice',
USER_COLLATERAL = 'userCollateral',
} }

View File

@ -35,7 +35,7 @@ interface Vault {
used: number used: number
max: number max: number
} }
apy: number | null apy: ApyBreakdown
} }
interface Position { interface Position {
@ -62,11 +62,7 @@ interface Position {
total: number total: number
net: number net: number
} }
apy: { apy: PositionApyBreakdown
total: number | null
borrow: number
net: number | null
}
ltv: number ltv: number
currentLeverage: number currentLeverage: number
unlockAtTimestamp?: number unlockAtTimestamp?: number
@ -88,16 +84,19 @@ interface LpTokenWithAddress {
unlocked: string unlocked: string
denom: string denom: string
vaultAddress: string vaultAddress: string
accountId: string
} }
interface VaultCoinsWithAddress { interface VaultCoinsWithAddress {
coins: Coin[] coins: Coin[]
vaultAddress: string vaultAddress: string
accountId: string
} }
interface UnlockTimeWithAddress { interface UnlockTimeWithAddress {
unlockAtTimestamp: number unlockAtTimestamp: number
vaultAddress: string vaultAddress: string
accountId: string
} }
interface FieldsAction { interface FieldsAction {
@ -105,23 +104,26 @@ interface FieldsAction {
values: string[] values: string[]
} }
interface AprData { interface PositionApyBreakdown extends ApyBreakdown {
contractAddress: string borrow: number
apr: number net: number | null
} }
interface FlatApr { interface ApyBreakdown {
contract_address: string vaultAddress: string
apr: { type: string; value: number | string }[] apys: { type: string; value: number }[] | null
fees: { type: string; value: number | string }[] fees: { type: string; value: number }[] | null
total: number | null
} }
interface NestedApr { interface ApolloAprResponse {
contract_address: string contract_address: string
apr: { apr: AprBreakdown
aprs: { type: string; value: number | string }[] }
fees: { type: string; value: number | string }[]
} interface AprBreakdown {
aprs: { type: string; value: number }[]
fees: { type: string; value: string | number }[]
} }
interface VaultCapData { interface VaultCapData {

View File

@ -21,7 +21,6 @@ interface RedBankData {
stATOMMarketIncentive: MarketIncentive stATOMMarketIncentive: MarketIncentive
nUSDCMarket: Market nUSDCMarket: Market
nUSDCMarketIncentive: MarketIncentive nUSDCMarketIncentive: MarketIncentive
collateral: UserCollateral[]
unclaimedRewards: string unclaimedRewards: string
} }
} }