Merge pull request #18 from mars-protocol/v1.4.0

releave v.1.4.0
This commit is contained in:
Dane 2023-03-13 15:24:33 -07:00 committed by GitHub
commit 98d4735538
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 1165 additions and 512 deletions

View File

@ -7,7 +7,7 @@ import {
WalletConnectionStatus,
} from '@marsprotocol/wallet-connector'
import { useQueryClient } from '@tanstack/react-query'
import { MARS_SYMBOL, USDC_SYMBOL } from 'constants/appConstants'
import { MARS_SYMBOL } from 'constants/appConstants'
import { IS_TESTNET } from 'constants/env'
import {
useBlockHeight,
@ -155,7 +155,6 @@ export const CommonContainer = ({ children }: CommonContainerProps) => {
useUserDebt()
useMarsOracle()
useSpotPrice(MARS_SYMBOL)
useSpotPrice(USDC_SYMBOL)
useDepositAndDebt()
useRedBank()

View File

@ -1,5 +1,6 @@
import { Coin } from '@cosmjs/stargate'
import { AnimatedNumber } from 'components/common'
import { useEffect, useState } from 'react'
import useStore from 'store'
interface Props {
@ -20,7 +21,15 @@ export const DisplayCurrency = ({
const networkConfig = useStore((s) => s.networkConfig)
const convertToDisplayCurrency = useStore((s) => s.convertToDisplayCurrency)
const amount = convertToDisplayCurrency(coin)
const displayCurrency = networkConfig?.displayCurrency
const [displayCurrency, setDisplayCurrency] = useState<DisplayCurrency>(
networkConfig?.displayCurrency,
)
useEffect(() => {
if (!networkConfig) return
if (displayCurrency.denom !== networkConfig?.displayCurrency.denom)
setDisplayCurrency(networkConfig?.displayCurrency)
}, [networkConfig?.displayCurrency, displayCurrency.denom, networkConfig])
if (!displayCurrency) return null

View File

@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'
import useStore from 'store'
import { DocURL } from 'types/enums/docURL'
import { version } from '../../../../package.json'
import packageInfo from '../../../../package.json'
import styles from './Footer.module.scss'
export const Footer = () => {
@ -211,7 +211,7 @@ export const Footer = () => {
</div>
</div>
<div className={styles.version}>
<p className='faded xs'>Mars Protocol v{version}</p>
<p className='faded xs'>Mars Protocol v{packageInfo.version}</p>
</div>
</div>
</footer>

View File

@ -18,6 +18,10 @@
outline: none;
background: $alphaBlack10;
.marsAmount {
margin: 0 space(1);
}
svg {
margin-top: space(-0.5);
height: rem-calc(19);

View File

@ -153,13 +153,15 @@ export const IncentivesButton = () => {
}}
>
<SVG.Logo />
<DisplayCurrency
className={styles.balance}
coin={{
amount: unclaimedRewards,
denom: marsDenom,
}}
/>
<span>
<AnimatedNumber
amount={Number(unclaimedRewards) / 1e6}
minDecimals={2}
maxDecimals={2}
className={styles.marsAmount}
/>
{MARS_SYMBOL}
</span>
</button>
{showDetails && (

View File

@ -43,6 +43,17 @@
flex: 0 0 100%;
flex-wrap: wrap;
display: flex;
flex-direction: column;
.select {
border-radius: $borderRadiusS;
border: rem-calc(1) solid $alphaBlack20;
@include padding(1, 2);
color: $colorSecondaryDark;
background-color: $fontColorLightPrimary;
outline: none;
width: 100%;
}
.name {
@include margin(1, 0);
@ -62,12 +73,16 @@
}
}
&.reduceMotion {
flex-direction: row;
}
.content {
@include margin(1, 0);
display: flex;
flex-wrap: wrap;
flex: 1;
justify-content: flex-end;
justify-content: space-between;
gap: space(2);
.button {

View File

@ -1,4 +1,5 @@
import { useWalletManager, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
import { useQueryClient } from '@tanstack/react-query'
import BigNumber from 'bignumber.js'
import classNames from 'classnames'
import { Button, NumberInput, SVG, Toggle, Tooltip } from 'components/common'
@ -12,15 +13,39 @@ import styles from './Settings.module.scss'
export const Settings = () => {
const { t } = useTranslation()
const inputPlaceholder = '...'
const queryClient = useQueryClient()
const slippages = [0.02, 0.03]
const [showDetails, setShowDetails] = useState(false)
const slippage = useStore((s) => s.slippage)
const networkConfig = useStore((s) => s.networkConfig)
const baseCurrency = useStore((s) => s.baseCurrency)
const whitelistedAssets = useStore((s) => s.whitelistedAssets)
const otherAssets = useStore((s) => s.otherAssets)
const assets: Asset[] = [...whitelistedAssets, ...otherAssets]
const [customSlippage, setCustomSlippage] = useState<string>(inputPlaceholder)
const [inputRef, setInputRef] = useState<React.RefObject<HTMLInputElement>>()
const [isCustom, setIsCustom] = useState(false)
const enableAnimations = useStore((s) => s.enableAnimations)
const { status } = useWalletManager()
const exchangeRates = useStore((s) => s.exchangeRates)
const [displayCurrency, setDisplayCurrency] = useState<DisplayCurrency>(() => {
const currency = {
denom: baseCurrency.denom,
prefix: '',
suffix: baseCurrency.symbol,
decimals: 2,
}
const currentCurrency = assets.find(
(asset) => asset.denom === networkConfig?.displayCurrency.denom,
)
if (currentCurrency) {
currency.denom = currentCurrency.denom
currency.prefix = ''
currency.suffix = currentCurrency.symbol
}
return currency
})
const onInputChange = (value: number) => {
setCustomSlippage(value.toString())
@ -57,6 +82,24 @@ export const Settings = () => {
localStorage.setItem('enableAnimations', reduce ? 'false' : 'true')
}
const changeDisplayCurrency = (denom: string) => {
const selectedAsset = assets.find((asset) => asset.denom === denom)
if (!selectedAsset || !networkConfig || !exchangeRates?.length) return
const newDisplayCurrency = {
denom: selectedAsset.denom,
prefix: '',
suffix: selectedAsset.symbol,
decimals: 2,
}
const exchangeRate = exchangeRates.find((rate) => rate.denom === newDisplayCurrency.denom)
if (!exchangeRate) return
setDisplayCurrency(newDisplayCurrency)
useStore.setState({ networkConfig: { ...networkConfig, displayCurrency: newDisplayCurrency } })
useStore.setState({ baseToDisplayCurrencyRatio: 1 / Number(exchangeRate.amount) })
localStorage.setItem('displayCurrency', JSON.stringify(newDisplayCurrency))
queryClient.invalidateQueries()
}
if (status !== WalletConnectionStatus.Connected) return null
return (
@ -75,7 +118,7 @@ export const Settings = () => {
<p className={styles.text}>{t('common.settings')}</p>
</div>
<div className={styles.settings}>
<div className={styles.setting}>
<div className={`${styles.setting} ${styles.reduceMotion}`}>
<div className={styles.name}>
{t('common.reduceMotion')}
<Tooltip content={t('common.tooltips.reduceMotion')} className={styles.tooltip} />
@ -88,6 +131,31 @@ export const Settings = () => {
/>
</div>
</div>
<div className={styles.setting}>
<div className={styles.name}>
{t('common.displayCurrency')}
<Tooltip
content={t('common.tooltips.displayCurrency', {
baseCurrencySymbol: baseCurrency.symbol,
})}
className={styles.tooltip}
/>
</div>
<div className={styles.content}>
<select
onChange={(e) => changeDisplayCurrency(e.target.value)}
className={classNames([styles.select, 's'])}
tabIndex={2}
value={displayCurrency.denom}
>
{assets.map((currency) => (
<option key={currency.denom} value={currency.denom}>
{`${currency.name} (${currency.symbol})`}
</option>
))}
</select>
</div>
</div>
</div>
{FIELDS_FEATURE && (
<>

View File

@ -1,4 +1,5 @@
import Tippy from '@tippyjs/react'
import classNames from 'classnames'
import styles from './TextTooltip.module.scss'
@ -7,6 +8,7 @@ interface Props {
tooltip: string | React.ReactNode
hideUnderline?: boolean
hideStyling?: boolean
className?: string
}
export const TextTooltip = (props: Props) => {
return (
@ -23,7 +25,13 @@ export const TextTooltip = (props: Props) => {
)
}}
>
<span className={props.hideUnderline ? styles.pointer : styles.tooltip} style={{}}>
<span
className={classNames(
props.hideUnderline ? styles.pointer : styles.tooltip,
props.className,
)}
style={{}}
>
{props.text}
</span>
</Tippy>

View File

@ -66,7 +66,7 @@ export const Tutorial = (props: Props) => {
}
const maxLeverage = props.availableVault
? formatValue(ltvToLeverage(props.availableVault?.ltv.max), 2, 2)
? formatValue(ltvToLeverage(props.availableVault?.ltv.contract), 2, 2)
: ''
const content = (

View File

@ -15,11 +15,16 @@ export const EditContent = (props: Props) => {
const { t } = useTranslation()
const convertValueToAmount = useStore((s) => s.convertValueToAmount)
const borrowKey =
props.position.borrowDenom === props.vault.denoms.primary
? 'borrowedPrimary'
: 'borrowedSecondary'
const primaryAmount = props.position.amounts.primary - (props.prevPosition?.amounts.primary || 0)
const secondaryAmount =
props.position.amounts.secondary - (props.prevPosition?.amounts.secondary || 0)
const borrowedAmount =
props.position.amounts.borrowed - (props.prevPosition?.amounts.borrowed || 0)
props.position.amounts[borrowKey] - (props.prevPosition?.amounts[borrowKey] || 0)
const depositPrimary = (
<TokenBalance
@ -43,10 +48,10 @@ export const EditContent = (props: Props) => {
/>
)
const borrowSecondary = (
const borrow = (
<TokenBalance
coin={{
denom: props.vault.denoms.secondary,
denom: props.position.borrowDenom || props.vault.denoms.secondary,
amount: borrowedAmount.toString(),
}}
className={styles.marginRight}
@ -78,14 +83,14 @@ export const EditContent = (props: Props) => {
return (
<li>
<span className={styles.marginRight}>{t('redbank.borrow')}</span>
{borrowSecondary}
{borrow}
</li>
)
}
const getSwapMessage = () => {
const primaryValue = props.position.values.primary
const secondaryValue = props.position.values.secondary + props.position.values.borrowed
const primaryValue = props.position.values.primary + props.position.values.borrowedPrimary
const secondaryValue = props.position.values.secondary + props.position.values.borrowedSecondary
const difference = Math.abs(primaryValue - secondaryValue)
if (difference < SWAP_THRESHOLD) return null

View File

@ -15,14 +15,21 @@ export const UnlockContent = (props: Props) => {
<>
<li>{t('fields.removeLiquidity', { vault: props.vault.provider })}</li>
<li>
<span className={styles.marginRight}>{t('redbank.repay')}</span>
<TokenBalance
coin={{
denom: props.vault.denoms.secondary,
amount: props.position.amounts.borrowed.toString(),
}}
showSymbol
/>
{props.position.borrowDenom && (
<>
<span className={styles.marginRight}>{t('redbank.repay')}</span>
<TokenBalance
coin={{
denom: props.position.borrowDenom,
amount: Math.max(
props.position.amounts.borrowedPrimary,
props.position.amounts.borrowedSecondary,
).toString(),
}}
showSymbol
/>
</>
)}
</li>
<li>
<span className={styles.marginRight}>{t('redbank.withdraw')}</span>

View File

@ -123,7 +123,10 @@ export const ActiveVaultsTableMobile = () => {
<DisplayCurrency
coin={{
denom: baseCurrency.denom,
amount: vault.position.values.borrowed.toString(),
amount: (vault.position.borrowDenom === vault.denoms.primary
? vault.position.amounts.borrowedPrimary
: vault.position.amounts.borrowedSecondary
).toString(),
}}
className={styles.inline}
/>
@ -136,9 +139,13 @@ export const ActiveVaultsTableMobile = () => {
<div className={styles.borrowCapacity}>
<BorrowCapacity
showPercentageText={true}
max={getLiqBorrowValue(vault, maxBorrowValue)}
max={getLiqBorrowValue(vault, vault.position.values.net)}
limit={maxBorrowValue}
balance={vault.position.values.borrowed}
balance={
vault.position.borrowDenom === vault.denoms.primary
? vault.position.values.borrowedPrimary
: vault.position.values.borrowedSecondary
}
showTitle={false}
barHeight={'16px'}
hideValues

View File

@ -75,13 +75,16 @@ export const useActiveVaultsColumns = () => {
cell: ({ row }) => {
const primaryCoin = {
denom: row.original.denoms.primary,
amount: row.original.position.amounts.primary.toString(),
amount: (
row.original.position.amounts.primary + row.original.position.amounts.borrowedPrimary
).toString(),
}
const secondaryCoin = {
denom: row.original.denoms.secondary,
amount: (
row.original.position.amounts.secondary + row.original.position.amounts.borrowed
row.original.position.amounts.secondary +
row.original.position.amounts.borrowedSecondary
).toString(),
}
@ -97,9 +100,13 @@ export const useActiveVaultsColumns = () => {
}
tooltip={
<>
<TokenBalance coin={primaryCoin} maxDecimals={2} showSymbol />
{Number(primaryCoin.amount) > 0 && (
<TokenBalance coin={primaryCoin} maxDecimals={2} showSymbol />
)}
<br />
<TokenBalance coin={secondaryCoin} maxDecimals={2} showSymbol />
{Number(secondaryCoin.amount) > 0 && (
<TokenBalance coin={secondaryCoin} maxDecimals={2} showSymbol />
)}
</>
}
/>
@ -134,41 +141,50 @@ export const useActiveVaultsColumns = () => {
}
tooltip={
<>
<TokenBalance coin={coins[0]} maxDecimals={2} showSymbol />
{Number(coins[0].amount) > 0 && (
<TokenBalance coin={coins[0]} maxDecimals={2} showSymbol />
)}
<br />
<TokenBalance coin={coins[1]} maxDecimals={2} showSymbol />
{Number(coins[1].amount) > 0 && (
<TokenBalance coin={coins[1]} maxDecimals={2} showSymbol />
)}{' '}
</>
}
/>
)
},
}),
columnHelper.accessor('position.values.borrowed', {
columnHelper.accessor('position.values.borrowedPrimary', {
enableSorting: true,
header: () => (
<TextTooltip text={t('common.borrowed')} tooltip={t('fields.tooltips.borrowValue')} />
),
cell: (info) => {
cell: ({ row }) => {
const borrowAsset = whitelistedAssets.find(
(asset) => asset.denom === info.row.original.denoms.secondary,
(asset) => asset.denom === row.original.position.borrowDenom,
)
if (!borrowAsset) return
const borrowKey =
row.original.position.borrowDenom === row.original.denoms.primary
? 'borrowedPrimary'
: 'borrowedSecondary'
return (
<TextTooltip
text={
<DisplayCurrency
coin={{
denom: baseCurrency.denom,
amount: info.row.original.position.values.borrowed.toString(),
amount: row.original.position.values[borrowKey].toString(),
}}
/>
}
tooltip={
<TokenBalance
coin={{
denom: info.row.original.denoms.secondary,
amount: info.row.original.position.amounts.borrowed.toString(),
denom: borrowAsset.denom,
amount: row.original.position.amounts[borrowKey].toString(),
}}
maxDecimals={2}
showSymbol
@ -291,7 +307,7 @@ export const useActiveVaultsColumns = () => {
</p>
<p className='s faded'>
{formatValue(
ltvToLeverage(row.original.ltv.max),
ltvToLeverage(row.original.ltv.contract),
2,
2,
false,
@ -303,7 +319,7 @@ export const useActiveVaultsColumns = () => {
)
},
}),
columnHelper.accessor('position.amounts.borrowed', {
columnHelper.accessor('position.amounts.borrowedPrimary', {
enableSorting: false,
header: () => (
<TextTooltip
@ -313,13 +329,19 @@ export const useActiveVaultsColumns = () => {
),
cell: ({ row }) => {
const maxBorrowValue = getMaxBorrowValue(row.original, row.original.position)
const borrowKey =
row.original.position.borrowDenom === row.original.denoms.primary
? 'borrowedPrimary'
: 'borrowedSecondary'
const liqBorrowValue = getLiqBorrowValue(row.original, row.original.position.values.net)
return (
<BorrowCapacity
showPercentageText={true}
max={getLiqBorrowValue(row.original, maxBorrowValue)}
max={liqBorrowValue}
limit={maxBorrowValue}
balance={row.original.position.values.borrowed}
balance={row.original.position.values[borrowKey]}
showTitle={false}
barHeight={'16px'}
hideValues

View File

@ -24,11 +24,20 @@ export const AvailableVaultsTableMobile = () => {
>
<div className={styles.container}>
{availableVaults.map((vault, i) => {
const borrowAsset = redBankAssets.find((asset) => asset.denom === vault.denoms.secondary)
const maxBorrowRate = Number(borrowAsset?.borrowRate ?? 0) * vault.ltv.max
const primaryBorrowAsset = redBankAssets.find(
(asset) => asset.denom === vault.denoms.primary,
)
const secondaryBorrowAsset = redBankAssets.find(
(asset) => asset.denom === vault.denoms.secondary,
)
const borrowRate = Math.min(
Number(primaryBorrowAsset?.borrowRate ?? 0),
Number(secondaryBorrowAsset?.borrowRate ?? 0),
)
const maxBorrowRate = borrowRate * (ltvToLeverage(vault.ltv.contract) - 1)
const minAPY = new BigNumber(vault.apy || 0).toNumber()
const leverage = ltvToLeverage(vault.ltv.max)
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 }
@ -60,7 +69,10 @@ export const AvailableVaultsTableMobile = () => {
hideStyling
text={<AnimatedNumber amount={maxAPY} suffix='%' />}
tooltip={
<Apy apyData={apyDataLev} leverage={ltvToLeverage(vault.ltv.max)} />
<Apy
apyData={apyDataLev}
leverage={ltvToLeverage(vault.ltv.contract)}
/>
}
/>
</span>

View File

@ -69,7 +69,7 @@ export const useAvailableVaultsColumns = () => {
return (
<>
<p className='m'>
{formatValue(ltvToLeverage(row.original.ltv.max), 2, 2, false, false, 'x')}
{formatValue(ltvToLeverage(row.original.ltv.contract), 2, 2, false, false, 'x')}
</p>
<p className='s faded'>{t('global.max_lower')}</p>
</>
@ -87,12 +87,20 @@ export const useAvailableVaultsColumns = () => {
return <Loading />
}
const maxLeverage = ltvToLeverage(row.original.ltv.max)
const borrowAsset = redBankAssets.find(
const maxLeverage = ltvToLeverage(row.original.ltv.contract)
const primaryBorrowAsset = redBankAssets.find(
(asset) => asset.denom === row.original.denoms.primary,
)
const secondaryBorrowAsset = redBankAssets.find(
(asset) => asset.denom === row.original.denoms.secondary,
)
const maxBorrowRate =
Number(borrowAsset?.borrowRate ?? 0) * (ltvToLeverage(row.original.ltv.max) - 1)
const borrowRate = Math.min(
Number(primaryBorrowAsset?.borrowRate ?? 0),
Number(secondaryBorrowAsset?.borrowRate ?? 0),
)
const maxBorrowRate = borrowRate * (ltvToLeverage(row.original.ltv.contract) - 1)
const minAPY = new BigNumber(row.original.apy).toNumber()
@ -128,7 +136,7 @@ export const useAvailableVaultsColumns = () => {
/>
}
tooltip={
<Apy apyData={apyDataLev} leverage={ltvToLeverage(row.original.ltv.max)} />
<Apy apyData={apyDataLev} leverage={ltvToLeverage(row.original.ltv.contract)} />
}
/>

View File

@ -18,7 +18,11 @@ export const BreakdownGraph = (props: Props) => {
const { t } = useTranslation()
const convertToDisplayCurrency = useStore((s) => s.convertToDisplayCurrency)
const baseCurrency = useStore((s) => s.baseCurrency)
const yAxisLimit = Math.max(props.position.values.net, props.position.values.borrowed)
const borrowKey =
props.position.borrowDenom === props.vault.denoms.primary
? 'borrowedPrimary'
: 'borrowedSecondary'
const yAxisLimit = Math.max(props.position.values.net, props.position.values[borrowKey])
const containerClasses = classNames([
styles.container,
@ -40,7 +44,7 @@ export const BreakdownGraph = (props: Props) => {
denom: props.vault.denoms.primary,
}),
convertToDisplayCurrency({
amount: props.position.values.borrowed.toString(),
amount: props.position.values[borrowKey].toString(),
denom: props.vault.denoms.primary,
}),
],
@ -123,7 +127,11 @@ export const BreakdownGraph = (props: Props) => {
<div className={`${styles.bar3}`} style={{ height: `${getBarHeightPercentage(3)}%` }}>
<AssetBar
type='debt'
symbol={props.vault.symbols.secondary}
symbol={
borrowKey === 'borrowedPrimary'
? props.vault.symbols.primary
: props.vault.symbols.secondary
}
height={getBarHeightPercentage(3)}
showLabel={hasSpaceForLabel('debt')}
/>

View File

@ -29,6 +29,10 @@
width: 100%;
border-collapse: collapse;
td {
min-width: space(12);
}
tr {
td:nth-child(2) {
width: 20px;

View File

@ -47,10 +47,16 @@ export const BreakdownTable = (props: Props) => {
const primaryPrice = usePrice(props.vault.denoms.primary)
const secondaryPrice = usePrice(props.vault.denoms.secondary)
const primaryRedBankAsset = useRedBankAsset(props.vault.denoms.primary)
const secondaryRedBankAsset = useRedBankAsset(props.vault.denoms.secondary)
const primaryChange = props.newPosition.amounts.primary - props.prevPosition.amounts.primary
const secondaryChange = props.newPosition.amounts.secondary - props.prevPosition.amounts.secondary
const borrowedChange = props.newPosition.amounts.borrowed - props.prevPosition.amounts.borrowed
const borrowKey =
props.newPosition.borrowDenom === props.vault.denoms.primary
? 'borrowedPrimary'
: 'borrowedSecondary'
const borrowedChange =
props.newPosition.amounts[borrowKey] - props.prevPosition.amounts[borrowKey]
const containerClasses = classNames([
props.className,
@ -71,15 +77,15 @@ export const BreakdownTable = (props: Props) => {
denom = props.vault.denoms.secondary
break
case AmountType.DEBT:
amount = props.newPosition.amounts.borrowed
amount = props.newPosition.amounts[borrowKey]
denom = props.vault.denoms.secondary
break
case AmountType.POSITION_PRIMARY:
amount = props.newPosition.amounts.primary
amount = props.newPosition.amounts.primary + props.newPosition.amounts.borrowedPrimary
denom = props.vault.denoms.primary
break
case AmountType.POSITION_SECONDARY:
amount = props.newPosition.amounts.secondary + props.newPosition.amounts.borrowed
amount = props.newPosition.amounts.secondary + props.newPosition.amounts.borrowedSecondary
denom = props.vault.denoms.secondary
break
}
@ -122,7 +128,9 @@ export const BreakdownTable = (props: Props) => {
)
}
const getValueText = (type: 'net' | 'borrowed' | 'total') => (
const getValueText = (
type: 'primary' | 'secondary' | 'net' | 'borrowedPrimary' | 'borrowedSecondary' | 'total',
) => (
<DisplayCurrency
prefixClass='s faded'
valueClass='m faded'
@ -152,7 +160,8 @@ export const BreakdownTable = (props: Props) => {
)
}
const isReducingDebt = props.newPosition.amounts.borrowed < props.prevPosition.amounts.borrowed
const isReducingDebt =
props.newPosition.amounts[borrowKey] < props.prevPosition.amounts[borrowKey]
if (isReducingDebt) {
return (
<>
@ -208,8 +217,12 @@ export const BreakdownTable = (props: Props) => {
/* APY CALCULATION */
const currentLeverage = props.newPosition.currentLeverage
const trueBorrowRate =
Number(secondaryRedBankAsset?.borrowRate ?? 0) * (Number(currentLeverage) - 1)
const borrowRate = Number(
borrowKey === 'borrowedPrimary'
? primaryRedBankAsset?.borrowRate
: secondaryRedBankAsset?.borrowRate,
)
const trueBorrowRate = borrowRate * (Number(currentLeverage) - 1)
const apy = (props.vault.apy || 0) * currentLeverage - trueBorrowRate
@ -291,9 +304,11 @@ export const BreakdownTable = (props: Props) => {
<tr className={styles.debtRow}>
<td className={`${styles.showDesktop} faded`}>{t('common.debt')}</td>
<td className={styles.alignRight}>{getTokenBalance(AmountType.DEBT)}</td>
<td>{secondaryAsset?.symbol}</td>
<td>
{borrowKey === 'borrowedPrimary' ? primaryAsset?.symbol : secondaryAsset?.symbol}
</td>
<td>{getChangeText(borrowedChange, 'secondary', true)}</td>
<td>{getValueText('borrowed')}</td>
<td>{getValueText(borrowKey)}</td>
</tr>
<tr className={`${styles.labelRow} faded`}>
@ -320,11 +335,14 @@ export const BreakdownTable = (props: Props) => {
</tbody>
</table>
{!props.isRepay && <div className={styles.reduceMessage}>{getWarningMessage()}</div>}
<BorrowCapacity
className={styles.borrowCapacity}
limit={maxBorrowValue}
max={getLiqBorrowValue(props.vault, maxBorrowValue)}
balance={props.newPosition.values.borrowed}
max={getLiqBorrowValue(props.vault, props.newPosition.values.net)}
balance={
props.newPosition.values.borrowedPrimary + props.newPosition.values.borrowedSecondary
}
barHeight={'24px'}
showPercentageText
/>

View File

@ -2,6 +2,7 @@ import classNames from 'classnames'
import { Tutorial } from 'components/common'
import { TokenInput } from 'components/fields'
import { FIELDS_TUTORIAL_KEY } from 'constants/appConstants'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import useStore from 'store'
@ -9,33 +10,106 @@ import styles from './BorrowInput.module.scss'
interface Props {
vault: Vault
borrowAmount: number
borrowedPrimaryAmount: number
borrowedSecondaryAmount: number
maxAmount: number
onChange: (amount: number) => void
prevPosition?: Position
onChangePrimary: (amount: number) => void
onChangeSecondary: (amount: number) => void
}
export const BorrowInput = (props: Props) => {
const { t } = useTranslation()
const redBankAssets = useStore((s) => s.redBankAssets)
const showTutorial = !localStorage.getItem(FIELDS_TUTORIAL_KEY)
const asset = redBankAssets.find((asset) => asset.denom === props.vault.denoms.secondary)
const primaryAsset = redBankAssets.find((asset) => asset.denom === props.vault.denoms.primary)
const secondaryAsset = redBankAssets.find((asset) => asset.denom === props.vault.denoms.secondary)
const containerClasses = classNames([styles.container])
const input: Input = {
visible: true,
const [cachedPrimaryAmount, setCachedPrimaryAmount] = useState(props.borrowedPrimaryAmount)
const [cachedSecondaryAmount, setCachedSecondaryAmount] = useState(props.borrowedSecondaryAmount)
const primaryInputVisisble =
props.borrowedPrimaryAmount > 0 ||
props.prevPosition?.borrowDenom === props.vault.denoms.primary
? true
: false
const [primaryInput, setPrimaryInput] = useState<Input>({
visible: primaryInputVisisble,
denom: props.vault.denoms.primary,
symbol: props.vault.symbols.primary,
})
const [secondaryInput, setSecondaryInput] = useState<Input>({
visible: !primaryInputVisisble,
denom: props.vault.denoms.secondary,
symbol: props.vault.symbols.secondary,
})
const selectInput = (symbol: string) => {
if (symbol === primaryInput.symbol) {
primaryInput.visible = true
secondaryInput.visible = false
setCachedSecondaryAmount(props.borrowedSecondaryAmount)
props.onChangePrimary(cachedPrimaryAmount)
} else {
primaryInput.visible = false
secondaryInput.visible = true
setCachedPrimaryAmount(props.borrowedPrimaryAmount)
props.onChangeSecondary(cachedSecondaryAmount)
}
setPrimaryInput({ ...primaryInput })
setSecondaryInput({ ...secondaryInput })
}
const onChangePrimary = (amount: number) => {
setCachedPrimaryAmount(amount)
props.onChangePrimary(amount)
}
const onChangeSecondary = (amount: number) => {
setCachedSecondaryAmount(amount)
props.onChangeSecondary(amount)
}
const tokenInput = (
<TokenInput
input={input}
amount={props.borrowAmount}
maxAmount={props.maxAmount}
maxAmountLabel={t('global.max')}
onChange={props.onChange}
tokens={[props.vault.symbols.secondary]}
borrowRate={asset?.borrowRate}
/>
<>
{primaryInput.visible && (
<TokenInput
input={primaryInput}
amount={props.borrowedPrimaryAmount}
onChange={onChangePrimary}
tokens={
props.prevPosition?.borrowDenom === props.vault.denoms.primary
? [primaryInput.symbol]
: [primaryInput.symbol, secondaryInput.symbol]
}
onSelect={selectInput}
maxAmountLabel={t('global.max')}
maxAmount={props.maxAmount}
borrowRate={primaryAsset?.borrowRate}
disableGasWarning
/>
)}
{secondaryInput.visible && (
<TokenInput
input={secondaryInput}
amount={props.borrowedSecondaryAmount}
onChange={onChangeSecondary}
tokens={
props.prevPosition?.borrowDenom === props.vault.denoms.secondary
? [secondaryInput.symbol]
: [primaryInput.symbol, secondaryInput.symbol]
}
onSelect={selectInput}
maxAmountLabel={t('global.max')}
maxAmount={props.maxAmount}
borrowRate={secondaryAsset?.borrowRate}
disableGasWarning
/>
)}
</>
)
return (

View File

@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js'
import classNames from 'classnames'
import { InputSlider, Tutorial } from 'components/common'
import { FIELDS_TUTORIAL_KEY } from 'constants/appConstants'
import { formatValue, ltvToLeverage } from 'libs/parse'
import { formatValue, leverageToLtv, ltvToLeverage } from 'libs/parse'
import React from 'react'
import { useTranslation } from 'react-i18next'
import colors from 'styles/_assets.module.scss'
@ -29,7 +29,7 @@ export const LeverageSlider = (props: Props) => {
isLeverage
value={props.leverage}
maxValue={props.leverageMax}
leverageMax={ltvToLeverage(props.vault.ltv.max)}
leverageMax={ltvToLeverage(props.vault.ltv.contract)}
leverageLimit={props.leverageLimit}
onChange={props.onChange}
sliderColor={colors.secondary}
@ -51,8 +51,11 @@ export const LeverageSlider = (props: Props) => {
) : (
<>{slider}</>
)}
{leverageToLtv(props.leverage) > props.vault.ltv.max && (
<span className={styles.warning}>{t('fields.messages.closeToMaxLtv')}</span>
)}
{props.leverage >= props.leverageLimit &&
props.leverageLimit < ltvToLeverage(props.vault.ltv.max) && (
props.leverageLimit < ltvToLeverage(props.vault.ltv.contract) && (
<span className={styles.warning}>
{t('fields.messages.unableToIncreaseLeverage', {
leverage: formatValue(props.leverageLimit, 2, 2),

View File

@ -54,6 +54,6 @@
@media only screen and (min-width: $bpMediumHigh) {
.grid {
grid-template-areas: 'supply leverage borrow';
grid-template-columns: auto 2fr 1fr;
grid-auto-columns: auto;
}
}

View File

@ -20,7 +20,9 @@ export const PositionInput = (props: Props) => {
const marketAssetLiquidity = useStore((s) => s.marketAssetLiquidity)
const convertToBaseCurrency = useStore((s) => s.convertToBaseCurrency)
const convertValueToAmount = useStore((s) => s.convertValueToAmount)
const [maxAllowedLeverage, setMaxAllowedLeverage] = useState(ltvToLeverage(props.vault.ltv.max))
const [maxAllowedLeverage, setMaxAllowedLeverage] = useState(
ltvToLeverage(props.vault.ltv.contract),
)
const tutorialStep = useStore((s) => s.tutorialSteps['fields'])
const showTutorial = !localStorage.getItem(FIELDS_TUTORIAL_KEY)
@ -33,13 +35,26 @@ export const PositionInput = (props: Props) => {
denom: props.vault.denoms.secondary,
amount: position.amounts.secondary.toString(),
})
const borrowValue = convertToBaseCurrency({
denom: props.vault.denoms.secondary,
amount: position.amounts.borrowed.toString(),
const borrowedPrimaryValue = convertToBaseCurrency({
denom: props.vault.denoms.primary,
amount: position.amounts.borrowedPrimary.toString(),
})
position.values.borrowed = borrowValue
position.values.total = borrowValue + primaryValue + secondaryValue
const borrowedSecondaryValue = convertToBaseCurrency({
denom: props.vault.denoms.secondary,
amount: position.amounts.borrowedSecondary.toString(),
})
position.values.borrowedPrimary = borrowedPrimaryValue
position.values.borrowedSecondary = borrowedSecondaryValue
position.values.total =
borrowedPrimaryValue + borrowedSecondaryValue + primaryValue + secondaryValue
position.values.net = primaryValue + secondaryValue
if (borrowedPrimaryValue > 0) {
props.position.borrowDenom = props.vault.denoms.primary
} else if (borrowedSecondaryValue > 0) {
props.position.borrowDenom = props.vault.denoms.secondary
}
return position
}
@ -53,20 +68,27 @@ export const PositionInput = (props: Props) => {
computeBorrowAmount()
}
const onBorrowChange = (amount: number) => {
const onBorrowChange = (amount: number, type: 'primary' | 'secondary') => {
props.position.borrowDenom =
type === 'primary' ? props.vault.denoms.primary : props.vault.denoms.secondary
const borrowValue = convertToBaseCurrency({
denom: props.vault.denoms.secondary,
denom: props.position.borrowDenom,
amount: amount.toString(),
})
props.position.amounts.borrowed = amount
props.position.values.borrowed = borrowValue
const borrowKey = type === 'primary' ? 'borrowedPrimary' : 'borrowedSecondary'
const secondaryBorrowKey = type !== 'primary' ? 'borrowedPrimary' : 'borrowedSecondary'
props.position.amounts[borrowKey] = amount
props.position.amounts[secondaryBorrowKey] = 0
props.position.values[borrowKey] = borrowValue
props.position.values[secondaryBorrowKey] = 0
props.position.currentLeverage = getLeverageFromValues(props.position.values)
props.setPosition({ ...updateValues(props.position) })
computeBorrowAmount()
}
const computeMaxAllowedLeverage = () => {
const maxBorrowValue = convertToBaseCurrency({
denom: props.vault.denoms.secondary,
denom: props.position.borrowDenom || props.vault.denoms.secondary,
amount: computeMaxBorrowAmount().toString(),
})
@ -76,16 +98,20 @@ export const PositionInput = (props: Props) => {
props.position.values.primary,
props.position.values.secondary,
)
: ltvToLeverage(props.vault.ltv.max)
: ltvToLeverage(props.vault.ltv.contract)
}
const computeBorrowAmount = (leverage?: number) => {
const supplyBorrowRatio = leverage ? leverage - 1 : props.position.currentLeverage - 1
const supplyValue = props.position.values.primary + props.position.values.secondary
const borrowValue = new BigNumber(supplyValue).times(supplyBorrowRatio).toNumber()
const borrowDenom = props.position.borrowDenom
? props.position.borrowDenom
: props.vault.denoms.secondary
const borrowAmount = Math.floor(
convertValueToAmount({
denom: props.vault.denoms.secondary,
denom: borrowDenom,
amount: borrowValue.toString(),
}),
)
@ -97,12 +123,15 @@ export const PositionInput = (props: Props) => {
: props.position.currentLeverage
const maxAllowedLeverage = computeMaxAllowedLeverage()
const borrowKey =
borrowDenom === props.vault.denoms.primary ? 'borrowedPrimary' : 'borrowedSecondary'
if (targetLeverage > maxAllowedLeverage) {
const borrowAmount = computeMaxBorrowAmount()
props.position.amounts.borrowed = borrowAmount
props.position.amounts[borrowKey] = borrowAmount
props.position.currentLeverage = maxAllowedLeverage
} else {
props.position.amounts.borrowed = borrowAmount
props.position.amounts[borrowKey] = borrowAmount
props.position.currentLeverage = targetLeverage
}
@ -111,25 +140,28 @@ export const PositionInput = (props: Props) => {
}
const computeMaxBorrowAmount = () => {
const borrowDenom = props.position.borrowDenom || props.vault.denoms.secondary
const maxAmount = Math.floor(
convertValueToAmount({
denom: props.vault.denoms.secondary,
denom: borrowDenom,
amount: getMaxBorrowValue(props.vault, props.position).toString(),
}),
)
const marketLiquidity = Number(
marketAssetLiquidity.find((market) => market.denom === props.vault.denoms.secondary)
?.amount || 0,
marketAssetLiquidity.find((market) => market.denom === borrowDenom)?.amount || 0,
)
const borrowKey =
borrowDenom === props.vault.denoms.primary ? 'borrowedPrimary' : 'borrowedSecondary'
const maxBorrowAmount = Math.min(
maxAmount,
(props.prevPosition?.amounts.borrowed || 0) + marketLiquidity,
(props.prevPosition?.amounts[borrowKey] || 0) + marketLiquidity,
)
if (props.prevPosition) {
return Math.max(maxBorrowAmount, props.prevPosition.amounts.borrowed)
return Math.max(maxBorrowAmount, props.prevPosition.amounts[borrowKey])
}
return maxBorrowAmount
@ -154,7 +186,7 @@ export const PositionInput = (props: Props) => {
leverage={props.position.currentLeverage || 1}
leverageLimit={maxAllowedLeverage}
leverageMax={Math.max(
ltvToLeverage(props.vault.ltv.max),
ltvToLeverage(props.vault.ltv.contract),
props.prevPosition?.currentLeverage || 1,
)}
onChange={computeBorrowAmount}
@ -162,10 +194,13 @@ export const PositionInput = (props: Props) => {
</Highlight>
<Highlight show={tutorialStep === 3 || !showTutorial} className={styles.borrow}>
<BorrowInput
borrowAmount={props.position.amounts.borrowed}
onChange={onBorrowChange}
borrowedPrimaryAmount={props.position.amounts.borrowedPrimary}
borrowedSecondaryAmount={props.position.amounts.borrowedSecondary}
onChangePrimary={(amount) => onBorrowChange(amount, 'primary')}
onChangeSecondary={(amount) => onBorrowChange(amount, 'secondary')}
maxAmount={computeMaxBorrowAmount()}
vault={props.vault}
prevPosition={props.prevPosition}
/>
</Highlight>
</div>

View File

@ -5,6 +5,7 @@
flex-direction: column;
gap: space(2);
flex: 1;
min-width: rem-calc(160);
.input {
display: flex;

View File

@ -16,6 +16,7 @@ interface Props {
maxAmount?: number
maxAmountLabel: string
borrowRate?: number
disableGasWarning?: boolean
onChange: (amount: number) => void
onSelect?: (denom: string) => void
}
@ -62,7 +63,10 @@ export const TokenInput = (props: Props) => {
10 ** asset.decimals
const showGasWarning =
props.maxAmount && props.amount >= props.maxAmount && asset.denom === baseCurrency.denom
props.maxAmount &&
props.amount >= props.maxAmount &&
asset.denom === baseCurrency.denom &&
!props.disableGasWarning
return (
<div className={styles.wrapper}>

View File

@ -19,18 +19,28 @@ interface Props {
export const RepayInput = (props: Props) => {
const { t } = useTranslation()
const convertToBaseCurrency = useStore((s) => s.convertToBaseCurrency)
const debtAmount = props.prevPosition.amounts.borrowed
const borrowKey =
props.position.borrowDenom === props.vault.denoms.primary
? 'borrowedPrimary'
: 'borrowedSecondary'
const supplyKey = borrowKey === 'borrowedPrimary' ? 'primary' : 'secondary'
const debtAmount = props.prevPosition.amounts[borrowKey]
const userBalances = useStore((s) => s.userBalances)
const borrowSymbol =
props.position.borrowDenom === props.vault.denoms.primary
? props.vault.symbols.primary
: props.vault.symbols.secondary
const walletBalance = Number(
(findByDenom(userBalances, props.vault.denoms.secondary) as Coin)?.amount || 0,
)
const maxRepayAmount = Math.min(walletBalance, debtAmount)
const amount = props.prevPosition.amounts.borrowed - props.position.amounts.borrowed
const amount = props.prevPosition.amounts[borrowKey] - props.position.amounts[borrowKey]
const maxValue = Math.max(
getLeverageFromValues(props.prevPosition.values),
ltvToLeverage(props.vault.ltv.max),
ltvToLeverage(props.vault.ltv.contract),
)
const updateValues = (position: Position) => {
@ -42,21 +52,29 @@ export const RepayInput = (props: Props) => {
denom: props.vault.denoms.secondary,
amount: position.amounts.secondary.toString(),
})
const borrowValue = convertToBaseCurrency({
denom: props.vault.denoms.secondary,
amount: position.amounts.borrowed.toString(),
const borrowedPrimaryValue = convertToBaseCurrency({
denom: props.vault.denoms.primary,
amount: position.amounts.borrowedPrimary.toString(),
})
const borrowedSecondaryValue = convertToBaseCurrency({
denom: props.vault.denoms.secondary,
amount: position.amounts.borrowedSecondary.toString(),
})
position.values.primary = primaryValue
position.values.secondary = secondaryValue
position.values.borrowed = borrowValue
position.values.total = borrowValue + primaryValue + secondaryValue
position.values.borrowedPrimary = borrowedPrimaryValue
position.values.borrowedSecondary = borrowedSecondaryValue
position.values.total =
borrowedPrimaryValue + borrowedSecondaryValue + primaryValue + secondaryValue
position.values.net = primaryValue + secondaryValue
position.currentLeverage = getLeverageFromValues(position.values)
return position
}
const handleChange = (amount: number) => {
props.position.amounts.borrowed = props.prevPosition.amounts.borrowed - amount
props.position.amounts.secondary = props.prevPosition.amounts.secondary + amount
props.position.amounts[borrowKey] = props.prevPosition.amounts[borrowKey] - amount
props.position.amounts[supplyKey] = props.prevPosition.amounts[supplyKey] + amount
props.setPosition({ ...updateValues(props.position) })
}
@ -66,21 +84,22 @@ export const RepayInput = (props: Props) => {
<p className={styles.headline}>{t('fields.repayDebt')}</p>
<TokenInput
amount={amount}
tokens={[props.vault.symbols.secondary]}
tokens={[borrowSymbol]}
input={{
denom: props.vault.denoms.secondary,
symbol: props.vault.symbols.secondary,
denom: props.position.borrowDenom || props.vault.denoms.secondary,
symbol: borrowSymbol,
visible: true,
}}
maxAmount={maxRepayAmount}
maxAmountLabel={t('global.max')}
onChange={handleChange}
disableGasWarning
/>
</div>
<RepayLeverage
value={props.position.currentLeverage}
maxValue={maxValue}
leverageMax={ltvToLeverage(props.vault.ltv.max)}
leverageMax={ltvToLeverage(props.vault.ltv.contract)}
/>
<div>
<strong className='m'>{t('fields.repayingDebtFromWallet')}</strong>

View File

@ -91,6 +91,12 @@ export const AssetTable = ({ data, columns, type, disabled = false }: Props) =>
</thead>
<tbody>
{table.getRowModel().rows.map((row) => {
if (
(type === 'deposit' && !row.original.depositEnabled) ||
(type === 'borrow' && !row.original.borrowEnabled)
)
return null
if (row.depth === 1) {
return (
<React.Fragment key={`${row.id}_subrow`}>

View File

@ -86,14 +86,16 @@ export const MetricsRow = ({ row, type }: Props) => {
valueClass='s'
labelClass='xs faded'
/>
<ValueWithLabel
percent={utilizationRatePercent}
label={t('redbank.utilizationRate')}
orientation='left'
prefixClass='xxs faded prefix'
valueClass='s'
labelClass='xs faded'
/>
{row.original.borrowEnabled && (
<ValueWithLabel
percent={utilizationRatePercent}
label={t('redbank.utilizationRate')}
orientation='left'
prefixClass='xxs faded prefix'
valueClass='s'
labelClass='xs faded'
/>
)}
</div>
</div>
</td>

View File

@ -70,17 +70,15 @@ export const useBorrowColumns = () => {
header: () => (
<TextTooltip text={t('common.rate')} tooltip={t('redbank.tooltips.borrow.rate')} />
),
cell: (info) => {
return (
<AnimatedNumber
amount={info.getValue()}
suffix='%'
abbreviated={false}
rounded={false}
className='m'
/>
)
},
cell: (info) => (
<AnimatedNumber
amount={info.getValue()}
suffix='%'
abbreviated={false}
rounded={false}
className='m'
/>
),
}),
columnHelper.accessor('marketLiquidity', {
enableSorting: enableSorting,
@ -100,15 +98,13 @@ export const useBorrowColumns = () => {
}),
columnHelper.display({
id: 'actions',
cell: ({ row }) => {
return (
<Button
color='quaternary'
prefix={row.getIsExpanded() ? <SVG.Collapse /> : <SVG.Expand />}
variant='round'
/>
)
},
cell: ({ row }) => (
<Button
color='quaternary'
prefix={row.getIsExpanded() ? <SVG.Collapse /> : <SVG.Expand />}
variant='round'
/>
),
}),
],
// eslint-disable-next-line react-hooks/exhaustive-deps

View File

@ -18,6 +18,10 @@
&.axlUSDC {
background-image: $colorGradientAxlUSDC;
}
&.stATOM {
background-image: $colorGradientStATOM;
}
}
.logo {
@ -25,3 +29,8 @@
margin: auto;
width: rem-calc(32);
}
.notBorrowable {
width: 100%;
display: block;
}

View File

@ -59,37 +59,45 @@ export const useDepositColumns = () => {
tooltip={t('redbank.tooltips.deposit.deposited')}
/>
),
cell: (info) => {
return (
<CellAmount
amount={Number(info.row.original.depositBalance)}
decimals={info.row.original.decimals}
denom={info.row.original.denom}
noBalanceText={t('common.noDeposit')}
/>
)
},
cell: (info) => (
<CellAmount
amount={Number(info.row.original.depositBalance)}
decimals={info.row.original.decimals}
denom={info.row.original.denom}
noBalanceText={t('common.noDeposit')}
/>
),
}),
columnHelper.accessor('apy', {
enableSorting: enableSorting,
header: () => (
<TextTooltip text={t('common.apr')} tooltip={t('redbank.tooltips.deposit.apy')} />
),
cell: (info) => (
<TextTooltip
hideStyling
text={
<AnimatedNumber
amount={info.getValue() + Number(info.row.original.incentiveInfo?.apy || 0)}
suffix='%'
abbreviated={false}
rounded={false}
className='m'
/>
}
tooltip={<Apr data={info.row.original} />}
/>
),
cell: (info) =>
info.row.original.borrowEnabled ? (
<TextTooltip
hideStyling
text={
<AnimatedNumber
amount={info.getValue() + Number(info.row.original.incentiveInfo?.apy || 0)}
suffix='%'
abbreviated={false}
rounded={false}
className='m'
/>
}
tooltip={<Apr data={info.row.original} />}
/>
) : (
<TextTooltip
text=''
hideUnderline
className={styles.notBorrowable}
tooltip={t('redbank.tooltips.deposit.notBorrowable', {
symbol: info.row.original.symbol,
})}
/>
),
}),
columnHelper.accessor('depositCap', {
enableSorting: enableSorting,
@ -108,7 +116,6 @@ export const useDepositColumns = () => {
'faded',
percent >= 100 ? 'colorInfoLoss' : '',
)
return (
<>
<p className='number'>
@ -129,15 +136,13 @@ export const useDepositColumns = () => {
}),
columnHelper.display({
id: 'actions',
cell: ({ row }) => {
return (
<Button
color='quaternary'
prefix={row.getIsExpanded() ? <SVG.Collapse /> : <SVG.Expand />}
variant='round'
/>
)
},
cell: ({ row }) => (
<Button
color='quaternary'
prefix={row.getIsExpanded() ? <SVG.Collapse /> : <SVG.Expand />}
variant='round'
/>
),
}),
]
// eslint-disable-next-line react-hooks/exhaustive-deps

View File

@ -42,6 +42,8 @@ export const RedbankNotConnected = () => {
depositBalanceBaseCurrency: 0,
depositCap: 0,
depositLiquidity: 0,
borrowEnabled: true,
depositEnabled: true,
})
})

View File

@ -14,7 +14,6 @@ export const ASSETS: { [denom: string]: Asset } = {
denom: 'uosmo',
color: colors.osmo,
decimals: 6,
hasOraclePrice: true,
logo: osmo,
poolId: 678,
},
@ -25,7 +24,6 @@ export const ASSETS: { [denom: string]: Asset } = {
color: colors.atom,
logo: atom,
decimals: 6,
hasOraclePrice: true,
},
juno: {
symbol: 'JUNO',
@ -34,7 +32,6 @@ export const ASSETS: { [denom: string]: Asset } = {
color: colors.juno,
logo: juno,
decimals: 6,
hasOraclePrice: true,
},
}
@ -46,7 +43,6 @@ const OTHER_ASSETS: { [denom: string]: OtherAsset } = {
color: colors.mars,
logo: mars,
decimals: 6,
hasOraclePrice: true,
poolId: 768,
},
axlusdc: {
@ -57,7 +53,6 @@ const OTHER_ASSETS: { [denom: string]: OtherAsset } = {
color: colors.axlusdc,
logo: axlusdc,
decimals: 6,
hasOraclePrice: false,
poolId: 674,
},
}
@ -70,6 +65,7 @@ export const NETWORK_CONFIG: NetworkConfig = {
rpcUrl: URL_RPC ?? 'https://rpc-test.osmosis.zone/',
restUrl: URL_REST ?? 'https://lcd-test.osmosis.zone/',
apolloAprUrl: 'https://api.apollo.farm/api/vault_infos/v2/osmo-test-4',
priceApiUrl: 'https://api-osmosis.imperator.co/tokens/v2/OSMO',
contracts: {
redBank: 'osmo1t0dl6r27phqetfu0geaxrng0u9zn8qgrdwztapt5xr32adtwptaq6vwg36',
incentives: 'osmo1zxs8fry3m8j94pqg7h4muunyx86en27cl0xgk76fc839xg2qnn6qtpjs48',
@ -93,30 +89,6 @@ export const NETWORK_CONFIG: NetworkConfig = {
}
export const VAULT_CONFIGS: Vault[] = [
{
address: 'osmo1zktjv92f76epswjvyxzzt3yyskpw7k6jsyu0kmq4zzc5fphrjumqlahctp',
name: 'OSMO-ATOM LP (1 day)',
denoms: {
primary: 'uosmo',
secondary: 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2',
lpToken: 'gamm/pool/1',
},
symbols: {
primary: 'OSMO',
secondary: 'ATOM',
},
color: '#DD5B65',
lockup: 86400,
provider: 'Apollo vault',
description:
'Up to 2.67× leveraged yield farming with auto compounding of the OSMO-ATOM LP tokens.',
ltv: {
max: 0.625,
contract: 0.63,
liq: 0.65,
},
apy: 0,
},
{
address: 'osmo1tp2m6g39h8mvhnu3plqjyen5s63023gj8w873l8wvly0cd77l6hsaa73wt',
name: 'OSMO-ATOM LP (14 day)',
@ -133,11 +105,11 @@ export const VAULT_CONFIGS: Vault[] = [
lockup: 86400 * 14,
provider: 'Apollo vault',
description:
'Up to 2.67× leveraged yield farming with auto compounding of the OSMO-ATOM LP tokens.',
'Up to 1.43× leveraged yield farming with auto compounding of the OSMO-ATOM LP tokens.',
ltv: {
max: 0.625,
contract: 0.63,
liq: 0.65,
max: 0.295,
contract: 0.3,
liq: 0.4,
},
apy: 0,
},
@ -157,11 +129,11 @@ export const VAULT_CONFIGS: Vault[] = [
lockup: 86400 * 1,
provider: 'Apollo vault',
description:
'Up to 1.67× leveraged yield farming with auto compounding of the OSMO-JUNO LP tokens.',
'Up to 1.43× leveraged yield farming with auto compounding of the OSMO-JUNO LP tokens.',
ltv: {
max: 0.4,
contract: 0.4115,
liq: 0.441,
max: 0.295,
contract: 0.3,
liq: 0.4,
},
apy: 0,
},
@ -181,11 +153,11 @@ export const VAULT_CONFIGS: Vault[] = [
lockup: 86400 * 14,
provider: 'Apollo vault',
description:
'Up to 1.67× leveraged yield farming with auto compounding of the OSMO-JUNO LP tokens.',
'Up to 1.43× leveraged yield farming with auto compounding of the OSMO-JUNO LP tokens.',
ltv: {
max: 0.4,
contract: 4.115,
liq: 0.441,
max: 0.295,
contract: 0.3,
liq: 0.4,
},
apy: 0,
},

View File

@ -4,6 +4,7 @@ import atom from 'images/atom.svg'
import axlusdc from 'images/axlusdc.svg'
import mars from 'images/mars.svg'
import osmo from 'images/osmo.svg'
import statom from 'images/statom.svg'
import colors from 'styles/_assets.module.scss'
export const ASSETS: { [denom: string]: Asset } = {
@ -13,17 +14,15 @@ export const ASSETS: { [denom: string]: Asset } = {
denom: 'uosmo',
color: colors.osmo,
decimals: 6,
hasOraclePrice: true,
logo: osmo,
},
axlusdc: {
symbol: 'axlUSDC',
name: 'Axelar USDC',
denom: 'ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858',
color: colors.axlUSDC,
color: colors.axlusdc,
logo: axlusdc,
decimals: 6,
hasOraclePrice: true,
poolId: 678,
},
atom: {
@ -33,7 +32,16 @@ export const ASSETS: { [denom: string]: Asset } = {
color: colors.atom,
logo: atom,
decimals: 6,
hasOraclePrice: true,
},
statom: {
symbol: 'stATOM',
name: 'Stride Atom',
denom: 'ibc/C140AFD542AE77BD7DCC83F13FDD8C5E5BB8C4929785E6EC2F4C636F98F17901',
color: colors.statom,
logo: statom,
decimals: 6,
poolId: 803,
poolBase: 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2',
},
}
@ -45,7 +53,6 @@ const OTHER_ASSETS: { [denom: string]: OtherAsset } = {
color: colors.mars,
logo: mars,
decimals: 6,
hasOraclePrice: true,
poolId: 907,
},
}
@ -57,6 +64,7 @@ export const NETWORK_CONFIG: NetworkConfig = {
rpcUrl: URL_RPC ?? 'https://rpc-osmosis.blockapsis.com/',
restUrl: URL_REST ?? 'https://lcd-osmosis.blockapsis.com/',
apolloAprUrl: 'https://api.apollo.farm/api/vault_infos/v2/osmosis-1',
priceApiUrl: 'https://api-osmosis.imperator.co/tokens/v2/OSMO',
contracts: {
redBank: 'osmo1c3ljch9dfw5kf52nfwpxd2zmj2ese7agnx0p9tenkrryasrle5sqf3ftpg',
incentives: 'osmo1nkahswfr8shg8rlxqwup0vgahp0dk4x8w6tkv3rra8rratnut36sk22vrm',
@ -66,7 +74,7 @@ export const NETWORK_CONFIG: NetworkConfig = {
},
assets: {
base: ASSETS.osmo,
whitelist: [ASSETS.osmo, ASSETS.atom, ASSETS.axlusdc],
whitelist: [ASSETS.osmo, ASSETS.atom, ASSETS.axlusdc, ASSETS.statom],
other: [OTHER_ASSETS.mars],
},
displayCurrency: {
@ -129,7 +137,7 @@ export const VAULT_CONFIGS: Vault[] = [
description:
'Up to 2.78× leveraged yield farming with auto compounding of the OSMO-axlUSDC LP tokens.',
ltv: {
max: 0.64,
max: 0.645,
contract: 0.65,
liq: 0.66,
},

View File

@ -5,6 +5,7 @@ export const FORUM_URL = 'https://forum.marsprotocol.io/'
export const MARS_SYMBOL = 'MARS'
export const MARS_DECIMALS = 6
export const USDC_SYMBOL = 'axlUSDC'
export const STATOM_SYMBOL = 'stATOM'
/* borrow capacity */
export const DEFAULT_SLIPPAGE = 0.01

View File

@ -4,7 +4,8 @@ export const DEFAULT_POSITION: Position = {
amounts: {
primary: 0,
secondary: 0,
borrowed: 0,
borrowedPrimary: 0,
borrowedSecondary: 0,
lp: {
amount: '0',
primary: 0,
@ -15,7 +16,8 @@ export const DEFAULT_POSITION: Position = {
values: {
primary: 0,
secondary: 0,
borrowed: 0,
borrowedPrimary: 0,
borrowedSecondary: 0,
total: 0,
net: 0,
},
@ -26,4 +28,5 @@ export const DEFAULT_POSITION: Position = {
},
ltv: 0.5,
currentLeverage: 1,
borrowDenom: null,
}

View File

@ -8,7 +8,10 @@ export const getClosePositionActions = (
const swapMessage: Action[] = []
// Increase the borrow amount by factor to account for an increase of borrow over time
const borrowAmount = Math.ceil(vault.position.amounts.borrowed * 1.001)
const borrowAmount = Math.ceil(
Math.max(vault.position.amounts.borrowedPrimary, vault.position.amounts.borrowedSecondary) *
1.001,
)
const secondaryAmount = vault.position.amounts.lp.secondary
if (secondaryAmount < borrowAmount) {
@ -46,11 +49,11 @@ export const getClosePositionActions = (
},
},
...swapMessage,
...(vault.position.amounts.borrowed
...(Math.max(vault.position.amounts.borrowedPrimary, vault.position.amounts.borrowedSecondary)
? [
{
repay: {
denom: vault.denoms.secondary,
denom: vault.position.borrowDenom || vault.denoms.secondary,
amount: 'account_balance' as ActionAmount,
},
},

View File

@ -1,7 +1,7 @@
export const getCoinFromPosition = (
position: Position,
vault: Vault,
type: 'primary' | 'secondary' | 'borrowed',
type: 'primary' | 'secondary' | 'borrowedPrimary' | 'borrowedSecondary',
) => {
const denom = type === 'primary' ? vault.denoms.primary : vault.denoms.secondary
return {

View File

@ -3,7 +3,17 @@ import BigNumber from 'bignumber.js'
export const getLeverageFromValues = (values: {
primary: number
secondary: number
borrowed: number
borrowedPrimary: number
borrowedSecondary: number
net: number
total: number
}) => Number(new BigNumber(values.borrowed).div(values.net || 1).plus(1))
}) =>
Number(
new BigNumber(
values.borrowedPrimary > values.borrowedSecondary
? values.borrowedPrimary
: values.borrowedSecondary,
)
.div(values.net || 1)
.plus(1),
)

View File

@ -1,3 +1,8 @@
export const getLiqBorrowValue = (vault: Vault, maxBorrowValue: number) => {
return ((vault.ltv.liq - vault.ltv.max) / vault.ltv.max + 1) * maxBorrowValue
import BigNumber from 'bignumber.js'
import { ltvToLeverage } from 'libs/parse'
export const getLiqBorrowValue = (vault: Vault, netValue: number) => {
const liqLev = ltvToLeverage(vault.ltv.liq)
return new BigNumber(netValue).times(liqLev - 1).toNumber()
}

View File

@ -9,5 +9,5 @@ export const getMaxAllowedLeverage = (
new BigNumber(borrowValue)
.div(new BigNumber(primaryValue).plus(secondaryValue))
.plus(1)
.toPrecision(2),
.toPrecision(4),
)

View File

@ -2,5 +2,8 @@ import BigNumber from 'bignumber.js'
import { ltvToLeverage } from 'libs/parse'
export const getMaxBorrowValue = (vault: Vault, position: Position): number => {
return new BigNumber(ltvToLeverage(vault.ltv.max)).minus(1).times(position.values.net).toNumber()
return new BigNumber(ltvToLeverage(vault.ltv.contract))
.minus(1)
.times(position.values.net)
.toNumber()
}

View File

@ -9,7 +9,7 @@ import { vault } from 'mocks/vault'
describe('getMaxBorrowValue', () => {
test('should return 0 when netValue = 0', () => {
vault.ltv.max = 0.5
vault.ltv.contract = 0.5
position.values.net = 0
const maxBorrowValue = getMaxBorrowValue(vault, position)
@ -17,7 +17,7 @@ describe('getMaxBorrowValue', () => {
})
test('should return 0 when maxLTV = 0', () => {
vault.ltv.max = 0
vault.ltv.contract = 0
position.values.net = 100
const maxBorrowValue = getMaxBorrowValue(vault, position)
@ -25,7 +25,7 @@ describe('getMaxBorrowValue', () => {
})
test('should return 1x netValue when maxLTV = 0.5', () => {
vault.ltv.max = 0.5
vault.ltv.contract = 0.5
position.values.net = 100
const maxBorrowValue = getMaxBorrowValue(vault, position)
@ -33,7 +33,7 @@ describe('getMaxBorrowValue', () => {
})
test('should return 4.5x netValue when maxLTV = ', () => {
vault.ltv.max = 0.75
vault.ltv.contract = 0.75
position.values.net = 100
const maxBorrowValue = getMaxBorrowValue(vault, position)
@ -62,7 +62,8 @@ describe('getLeverageFromValues', () => {
const values = {
primary: 0,
secondary: 0,
borrowed: 0,
borrowedPrimary: 0,
borrowedSecondary: 0,
net: 0,
total: 0,
}
@ -73,14 +74,14 @@ describe('getLeverageFromValues', () => {
})
test('should return 1.5 when borrowed = 50 and net = 100', () => {
values.borrowed = 50
values.borrowedPrimary = 50
values.net = 100
const leverage = getLeverageFromValues(values)
expect(leverage).toBe(1.5)
})
test('should return 2.25 when borrowed = 125 and net = 100', () => {
values.borrowed = 125
values.borrowedPrimary = 125
values.net = 100
const leverage = getLeverageFromValues(values)
expect(leverage).toBe(2.25)

View File

@ -3,9 +3,10 @@ import { Coin } from '@cosmjs/proto-signing'
export const updateExchangeRate = (coin: Coin, exchangeRates: Coin[]) => {
const assetIndex = exchangeRates.findIndex((xAsset) => xAsset.denom === coin.denom)
if (assetIndex > -1) {
exchangeRates[assetIndex] = coin
if (coin.amount !== '0.00') exchangeRates[assetIndex] = coin
} else {
exchangeRates.push(coin)
}
return exchangeRates
}

View File

@ -12,6 +12,7 @@ export { useRepayPosition } from './useRepayPosition'
export { useRequestUnlockPosition } from './useRequestUnlockPosition'
export { useSpotPrice } from './useSpotPrice'
export { useUnlockMessages } from './useUnlockMessages'
export { useUsdPrice } from './useUsdPrice'
export { useUserBalance } from './useUserBalance'
export { useUserDebt } from './useUserDebt'
export { useUserDeposit } from './useUserDeposit'

View File

@ -15,6 +15,8 @@ export interface DepositAndDebtData {
JUNODebt: string
axlUSDCDeposits: string
axlUSDCDebt: string
stATOMDeposits: string
stATOMDebt: string
}
}

View File

@ -27,15 +27,20 @@ export const useEditPosition = (props: Props) => {
props.position.amounts.primary - (props.prevPosition?.amounts.primary || 0)
const secondaryAmount =
props.position.amounts.secondary - (props.prevPosition?.amounts.secondary || 0)
const borrowedAmount =
props.position.amounts.borrowed - (props.prevPosition?.amounts.borrowed || 0)
const borrowedPrimaryAmount =
props.position.amounts.borrowedPrimary - (props.prevPosition?.amounts.borrowedPrimary || 0)
const borrowedSecondaryAmount =
props.position.amounts.borrowedSecondary -
(props.prevPosition?.amounts.borrowedSecondary || 0)
const editPosition = {
...props.position,
amounts: {
primary: primaryAmount,
secondary: secondaryAmount,
borrowed: borrowedAmount,
borrowedPrimary: borrowedPrimaryAmount,
borrowedSecondary: borrowedSecondaryAmount,
lp: {
amount: '0',
primary: 0,
@ -47,15 +52,15 @@ export const useEditPosition = (props: Props) => {
const primaryBaseAmount = convertToBaseCurrency({
denom: props.vault.denoms.primary,
amount: primaryAmount.toString(),
amount: (primaryAmount + borrowedPrimaryAmount).toString(),
})
const secondaryBaseAmount = convertToBaseCurrency({
denom: props.vault.denoms.secondary,
amount: (secondaryAmount + borrowedAmount).toString(),
amount: (secondaryAmount + borrowedSecondaryAmount).toString(),
})
let primaryAfterSwap = primaryAmount
let secondaryAfterSwap = secondaryAmount + borrowedAmount
let primaryAfterSwap = primaryAmount + borrowedPrimaryAmount
let secondaryAfterSwap = secondaryAmount + borrowedSecondaryAmount
// If difference is larger than threshold, initiate a swap
const difference = primaryBaseAmount - secondaryBaseAmount
@ -105,7 +110,8 @@ export const useEditPosition = (props: Props) => {
}, [
props.position.amounts.primary,
props.position.amounts.secondary,
props.position.amounts.borrowed,
props.position.amounts.borrowedPrimary,
props.position.amounts.borrowedSecondary,
])
const { data: minLpToReceive } = useProvideLiquidity({
@ -128,9 +134,12 @@ export const useEditPosition = (props: Props) => {
amount: editPosition.amounts.secondary.toString(),
}
const borrow = editPosition.amounts.borrowed && {
denom: props.vault.denoms.secondary,
amount: editPosition.amounts.borrowed.toString(),
const borrow = editPosition.borrowDenom && {
denom: editPosition.borrowDenom,
amount: Math.max(
editPosition.amounts.borrowedPrimary,
editPosition.amounts.borrowedSecondary,
).toString(),
}
if (primary) coins.supply.push(primary)
@ -139,12 +148,12 @@ export const useEditPosition = (props: Props) => {
const primaryBaseAmount = convertToBaseCurrency({
denom: props.vault.denoms.primary,
amount: String(editPosition.amounts.primary),
amount: String(editPosition.amounts.primary + editPosition.amounts.borrowedPrimary),
})
const secondaryBaseAmount = convertToBaseCurrency({
denom: props.vault.denoms.secondary,
amount: String(editPosition.amounts.secondary + editPosition.amounts.borrowed),
amount: String(editPosition.amounts.secondary + editPosition.amounts.borrowedSecondary),
})
const swapMessage: Action[] = []

View File

@ -14,16 +14,29 @@ export const useRepayPosition = (props: Props) => {
const [amount, setAmount] = useState(0)
useEffect(() => {
setAmount(props.prevPosition.amounts.borrowed - props.repayPosition.amounts.borrowed)
}, [props.repayPosition.amounts.borrowed, props.prevPosition.amounts.borrowed])
const borrowKey =
props.prevPosition.borrowDenom === props.activeVault.denoms.primary
? 'borrowedPrimary'
: 'borrowedSecondary'
setAmount(props.prevPosition.amounts[borrowKey] - props.repayPosition.amounts[borrowKey])
}, [
props.repayPosition.amounts.borrowedPrimary,
props.repayPosition.amounts.borrowedSecondary,
props.prevPosition.amounts.borrowedPrimary,
props.prevPosition.amounts.borrowedSecondary,
props.activeVault.denoms.primary,
props.prevPosition.amounts,
props.prevPosition.borrowDenom,
props.repayPosition.amounts,
])
const [actions, funds] = useMemo(() => {
if (!amount) return [[], []]
return getRepayActionsAndFunds({
denom: props.activeVault.denoms.secondary,
denom: props.activeVault.position.borrowDenom || props.activeVault.denoms.secondary,
amount: amount.toString(),
})
}, [amount, props.activeVault.denoms.secondary])
}, [amount, props.activeVault.denoms.secondary, props.activeVault.position.borrowDenom])
const { data: fee } = useEstimateFarmFee({
accountId: props.prevPosition.accountId,

View File

@ -9,15 +9,16 @@ const poolsEndpoint = 'osmosis/gamm/v1beta1/pools/'
export const useSpotPrice = (symbol: string) => {
const networkConfig = useStore((s) => s.networkConfig)
const displayCurrency = networkConfig?.displayCurrency
const lcd = useStore((s) => s.chainInfo?.rest)
const exchangeRates = useStore((s) => s.exchangeRates)
const asset = useAsset({ symbol })
const displayCurrency = networkConfig?.displayCurrency
const poolBase = asset?.poolBase
? exchangeRates?.find((ratesAsset) => ratesAsset.denom === asset.poolBase)
: true
useQuery<PoolResponse>(
[QUERY_KEYS.MARS_PRICE, asset?.poolId],
[QUERY_KEYS.SPOT_PRICE, asset?.poolId],
async () => {
return fetch(`${lcd}${poolsEndpoint}${asset?.poolId}`).then(async (response) => {
const data = await response.json()
@ -25,7 +26,7 @@ export const useSpotPrice = (symbol: string) => {
})
},
{
enabled: !!lcd && !!asset && !!asset.poolId,
enabled: !!lcd && !!asset && !!asset.poolId && !!poolBase,
staleTime: 30000,
refetchInterval: 30000,
onSuccess: (data) => {
@ -44,11 +45,22 @@ export const useSpotPrice = (symbol: string) => {
.div(targetAssetAmount)
.multipliedBy(otherAssetWeight.div(targetAssetWeight))
if (displayCurrency.denom === asset.denom) {
const hasDisplayCurrency = exchangeRates?.find(
(ratesAsset) => ratesAsset.denom === displayCurrency.denom,
)
if (displayCurrency.denom === asset.denom && !hasDisplayCurrency) {
useStore.setState({ baseToDisplayCurrencyRatio: 1 / rate.toNumber() })
} else {
const coin = { denom: asset.denom, amount: rate.toString() }
useStore.setState({ exchangeRates: updateExchangeRate(coin, exchangeRates || []) })
if (typeof poolBase === 'object') {
const baseRate = Number(rate.toString()) * Number(poolBase.amount)
coin.amount = baseRate.toString()
useStore.setState({ exchangeRates: updateExchangeRate(coin, exchangeRates || []) })
} else {
useStore.setState({ exchangeRates: updateExchangeRate(coin, exchangeRates || []) })
}
}
},
},

View File

@ -0,0 +1,46 @@
import { useQuery } from '@tanstack/react-query'
import { updateExchangeRate } from 'functions'
import useStore from 'store'
import { QUERY_KEYS } from 'types/enums/queryKeys'
interface CoinPriceData {
price: number
denom: string
symbol: string
liquidity: number
liquidity_24h_change: number
volume_24h: number
volume_24h_change: number
name: string
price_24h_change: number
exponent: number
display: string
}
export const useUsdPrice = () => {
const apiUrl = useStore((s) => s.networkConfig?.priceApiUrl ?? '')
const exchangeRates = useStore((s) => s.exchangeRates ?? [])
const networkConfig = useStore((s) => s.networkConfig)
const displayCurrency = networkConfig?.displayCurrency
useQuery<CoinPriceData[]>(
[QUERY_KEYS.USD_PRICE],
async () => {
const res = await fetch(apiUrl)
return res.json()
},
{
enabled: !!apiUrl && !!exchangeRates.length && !!displayCurrency,
staleTime: 30000,
refetchInterval: 30000,
onSuccess: (data) => {
const coin = { denom: 'usd', amount: (1 / data[0].price).toString() }
if (displayCurrency.denom === coin.denom) {
useStore.setState({ baseToDisplayCurrencyRatio: data[0].price })
}
updateExchangeRate(coin, exchangeRates)
},
},
)
}

38
src/images/statom.svg Normal file
View File

@ -0,0 +1,38 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 2500 2500" style="enable-background:new 0 0 2500 2500;" xml:space="preserve">
<circle style="fill:#E50571;" cx="1250" cy="1250" r="1250"/>
<circle style="fill:#FFFFFF;" cx="1250" cy="1250" r="128.6"/>
<path style="fill:#FFFFFF;" d="M1709.1,1253.5c336.5-233.9,543.2-454.9,491.6-544.9c-33.3-58.1-168.1-50.8-359.4,8.1
c-13-22.6-36.8-37.7-64.1-37.7c-41.2,0-74.6,34.5-74.6,77.2c0,2.8,0.2,5.6,0.4,8.3c-69.8,26.5-144.5,58.1-222.5,94.3
c-35.3-409.1-124.1-699.3-228-699.3c-103.7,0-192.4,289.3-227.8,697.4c-370.9-173.9-665.9-242-718-152.1
c-32.5,56.3,35.9,164.2,173.9,294.7c-1.5,6.1-2.3,12.5-2.3,19.1c0,42.6,33.4,77.2,74.6,77.2c10.5,0,20.5-2.3,29.6-6.3
c62.9,52.3,134.6,106.9,213.5,162.2c-336.6,233.9-543.3,454.9-491.6,544.9c51.7,90.1,347.6,22.7,720.2-150.2
c7.9,91.7,18.5,177.4,31.4,255.2c-19.5,13.9-32.3,37.2-32.3,63.5c0,37.3,25.6,68.4,59.6,75.6c43.9,188.9,103.6,305.1,169.3,305.1
c103.8,0,192.4-289.4,227.8-697.6c371,174,665.9,242.2,717.9,152.3C2250.4,1710.6,2044.5,1488.8,1709.1,1253.5z M1725.6,811.8
c13.4,13.3,31.6,21.5,51.7,21.5c37.1,0,67.9-28,73.6-64.8c190.7-59.8,268.1-52.8,295.1-45.4c9.1,2.5,15,11.2,14,20.6
c-5.1,56-104.2,157-104.2,157c-132.1,131.9-271.4,241.7-387.7,324.4c-54.8-37.5-112.7-75.2-173.3-112.6
c-2-69.4-5.4-136.8-10.2-201.5C1576.7,870.1,1656.5,837.6,1725.6,811.8z M1398,1397.6c-47.4,29.1-96.7,57-146.1,83.3
c-33.6-18.8-52-30.1-52-30.1c-46.3-25-92.5-52-137.6-79.9c-2.2-85.5-2.7-164-2.4-227.6c71-43.2,136.9-81.4,190.9-112.1
c76.2,41.6,145,81.1,199.8,113.2c0.4,34.7-0.1,54.1-0.1,54.1c1.7,55.9,1.3,112.8-0.5,169.1C1417,1387.3,1398,1397.6,1398,1397.6z
M1448,1421.5c-2.5,53.4-6.2,105.5-10.4,154.9c-54.3-25.8-101-49.7-138.3-69.6c24.9-13.7,50-27.8,75.2-42.2
C1399.3,1450.2,1423.8,1435.9,1448,1421.5z M1203.7,1506c-45.2,23.2-90.1,44.9-133,65c-3-49.4-5.2-98.4-6.8-145.8
c21.7,13.1,43.8,26.1,66.1,39C1154.8,1478.5,1179.3,1492.4,1203.7,1506z M1009,1337.3c-43.2-27.8-85-56-124.2-83.4
c41.7-27.4,83.5-53.9,124.1-79.1c-0.4,25.7-0.6,51.7-0.6,77.9C1008.2,1281.1,1008.5,1309.3,1009,1337.3z M1066.8,934.6
c44.8,22.5,88.9,45.4,131.2,68c-22.3,12.4-44.8,25-67.3,37.9c-23.7,13.6-47.2,27.3-70.2,41.1c0.8-61.8,2.1-99.1,2.1-99.1
C1063.9,966.2,1065.4,950.3,1066.8,934.6z M1304.9,1000.9c54.2-30.2,87.3-47.7,87.3-47.7c16.4-7.6,32.4-15,48-22.1
c4.9,59.5,7.5,111.6,9,153.5c-24.3-14.6-48.9-29.2-73.9-43.7C1351.8,1027.3,1328.3,1013.9,1304.9,1000.9z M1496.2,1171.4
c53.4,31.9,85.3,51.8,85.3,51.8c15.2,10.6,30,21.1,44.5,31.4c-49.7,34.2-93.9,62.7-129.8,85c0.5-28.7,0.8-57.8,0.8-87.1
C1496.9,1225.3,1496.7,1198.2,1496.2,1171.4z M1238.2,214.2c6.7-6.6,17.1-7.4,24.7-2c45.9,32.5,84.4,168.5,84.4,168.5
c48.7,180.9,74.7,357.1,88.4,499.3c-59.3,28.4-120.2,59.3-182.3,92.6c-61.6-33.4-122.2-64.5-181.2-93
C1124.6,369.7,1208.5,243.6,1238.2,214.2z M619.2,1054c5.3-10.6,8.4-22.7,8.4-35.5c0-42.6-33.4-77.2-74.6-77.2
c-16.7,0-32,5.7-44.5,15.2C364.1,818,351.7,750.1,351.7,750.1l-0.1-0.3c-10-23.7,11.1-29.4,11.1-29.4
c93.5-17.6,224.7,22.6,224.7,22.6c123.5,28.1,280.4,94.2,432.9,168.6c-4.7,64.4-8.1,131.3-10.1,200.3
c-59.6,36.5-116.7,73.2-170.8,109.8C736.8,1148.2,656.4,1084.2,619.2,1054z M736.1,1711.7c-280.1,100.9-369.7,68.8-369.7,68.8h0
c-25.7-3.2-19.9-24.4-19.9-24.4c31.5-89.7,132-183.2,132-183.2c86.2-92.8,222-195.4,362.8-290c53.6,36.5,110.1,73.2,169,109.7
c2,69.5,5.4,136.9,10.2,201.7C859.5,1666.9,736.1,1711.7,736.1,1711.7z M1393.7,1929c-51.8,293.2-124.2,355-124.2,355
c-15.4,20.6-30.9,5.1-30.9,5.1c-62.1-72-93.2-205.8-93.2-205.8c-4.9-15.7-9.5-32.1-13.8-49.3c24.5-12.7,41.3-38.8,41.3-69
c0-41.1-31-74.7-70.2-77c-12.9-83.4-22.1-174.2-28.5-265.2c57.8-27.8,117.3-58,177.7-90.4c61.7,33.4,122.3,64.5,181.2,93.1
C1416.1,1800.5,1393.7,1929,1393.7,1929z M2147.6,1782.8c-51.1,23.4-188.1-11.5-188.1-11.5c-180.4-48.4-345.3-113.9-475.1-173.2
c4.9-65.8,8.4-134.4,10.5-205.1c59.6-36.5,116.7-73.3,170.8-109.9c414.9,300.9,482,436.8,492.5,477.1
C2160.7,1769.5,2156.1,1778.9,2147.6,1782.8z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -1,5 +1,6 @@
// @index(['./*.ts'], f => `export { ${f.name.split('.')[0]} } from '${f.path}'`)
export { position } from './position'
export { redBankAssets } from './redBankAssets'
export { redBankData } from './redBankData'
export { vault } from './vault'
// @endindex

View File

@ -4,7 +4,8 @@ export const position: Position = {
amounts: {
primary: 0,
secondary: 0,
borrowed: 0,
borrowedPrimary: 0,
borrowedSecondary: 0,
lp: {
amount: '0',
primary: 0,
@ -15,7 +16,8 @@ export const position: Position = {
values: {
primary: 0,
secondary: 0,
borrowed: 0,
borrowedPrimary: 0,
borrowedSecondary: 0,
total: 0,
net: 0,
},
@ -26,4 +28,5 @@ export const position: Position = {
},
ltv: 0.5,
currentLeverage: 1,
borrowDenom: null,
}

View File

@ -11,13 +11,14 @@ export const redBankAssets: RedBankAsset[] = [
depositBalanceBaseCurrency: 0,
depositCap: 1_000_000,
depositLiquidity: 5_000,
hasOraclePrice: true,
isCollateral: true,
logo: '',
marketLiquidity: '5000',
name: 'Atom',
symbol: 'ATOM',
walletBalance: '100',
borrowEnabled: true,
depositEnabled: true,
},
{
borrowRate: 30,
@ -31,12 +32,13 @@ export const redBankAssets: RedBankAsset[] = [
depositBalanceBaseCurrency: 0,
depositCap: 1_000_000,
depositLiquidity: 5_000,
hasOraclePrice: true,
isCollateral: true,
logo: '',
marketLiquidity: '5000',
name: 'Osmo',
symbol: 'OSMO',
walletBalance: '100',
borrowEnabled: true,
depositEnabled: true,
},
]

150
src/mocks/redBankData.ts Normal file
View File

@ -0,0 +1,150 @@
export const redBankData: RedBankData = {
//@ts-ignore
rbwasmkey: {
OSMOMarket: {
denom: 'uosmo',
max_loan_to_value: '0.59',
liquidation_threshold: '0.61',
liquidation_bonus: '0.15',
reserve_factor: '0.2',
interest_rate_model: {
optimal_utilization_rate: '0.6',
base: '0',
slope_1: '0.15',
slope_2: '3',
},
borrow_index: '1.007594298500875037',
liquidity_index: '1.002246982368900673',
borrow_rate: '0.093460151948703559',
liquidity_rate: '0.027951360007279224',
indexes_last_updated: 1678369905,
collateral_total_scaled: '9114923539298761562',
debt_total_scaled: '3389444761162622058',
deposit_enabled: true,
borrow_enabled: true,
deposit_cap: '10000000000000',
},
OSMOMarketIncentive: {
denom: 'uosmo',
emission_per_second: '231482',
start_time: 1675793700,
duration: 2592000,
index: '0.000000139928505817',
last_updated: 1678369905,
},
ATOMMarket: {
denom: 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2',
max_loan_to_value: '0.68',
liquidation_threshold: '0.7',
liquidation_bonus: '0.15',
reserve_factor: '0.2',
interest_rate_model: {
optimal_utilization_rate: '0.6',
base: '0',
slope_1: '0.15',
slope_2: '3',
},
borrow_index: '1.007258580949970929',
liquidity_index: '1.002076728614660732',
borrow_rate: '0.09442052103774879',
liquidity_rate: '0.028528751337727877',
indexes_last_updated: 1678368967,
collateral_total_scaled: '349274630628750224',
debt_total_scaled: '131236134520805186',
deposit_enabled: true,
borrow_enabled: true,
deposit_cap: '350000000000',
},
ATOMMarketIncentive: {
denom: 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2',
emission_per_second: '106095',
start_time: 1675793700,
duration: 2592000,
index: '0.000001514531327288',
last_updated: 1678369638,
},
stATOMMarket: {
denom: 'ibc/C140AFD542AE77BD7DCC83F13FDD8C5E5BB8C4929785E6EC2F4C636F98F17901',
max_loan_to_value: '0.68',
liquidation_threshold: '0.7',
liquidation_bonus: '0.15',
reserve_factor: '0.2',
interest_rate_model: {
optimal_utilization_rate: '0.6',
base: '0',
slope_1: '0.15',
slope_2: '3',
},
borrow_index: '1.007258580949970929',
liquidity_index: '1.002076728614660732',
borrow_rate: '0.09442052103774879',
liquidity_rate: '0.028528751337727877',
indexes_last_updated: 1678368967,
collateral_total_scaled: '349274630628750224',
debt_total_scaled: '131236134520805186',
deposit_enabled: true,
borrow_enabled: false,
deposit_cap: '350000000000',
},
stATOMMarketIncentive: {
denom: 'ibc/C140AFD542AE77BD7DCC83F13FDD8C5E5BB8C4929785E6EC2F4C636F98F17901',
emission_per_second: '106095',
start_time: 1675793700,
duration: 2592000,
index: '0.000001514531327288',
last_updated: 1678369638,
},
axlUSDCMarket: {
denom: 'ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858',
max_loan_to_value: '0.74',
liquidation_threshold: '0.75',
liquidation_bonus: '0.1',
reserve_factor: '0.2',
interest_rate_model: {
optimal_utilization_rate: '0.8',
base: '0',
slope_1: '0.2',
slope_2: '2',
},
borrow_index: '1.010875731969102527',
liquidity_index: '1.00525118991986623',
borrow_rate: '0.193514001505802393',
liquidity_rate: '0.11983254009212061',
indexes_last_updated: 1678369620,
collateral_total_scaled: '1494717184985439254',
debt_total_scaled: '1158255523619905561',
deposit_enabled: true,
borrow_enabled: true,
deposit_cap: '1500000000000',
},
axlUSDCMarketIncentive: {
denom: 'ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858',
emission_per_second: '48225',
start_time: 1675793700,
duration: 2592000,
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',
},
}

View File

@ -65,6 +65,11 @@ const EditVault = (props: Props) => {
[prevPosition.values.total, position.values.total],
)
const borrowKey =
props.activeVault.position.borrowDenom === props.activeVault.denoms.primary
? 'borrowedPrimary'
: 'borrowedSecondary'
const { repayActions, repayFunds, repayFee } = useRepayPosition({
prevPosition,
repayPosition,
@ -131,7 +136,8 @@ const EditVault = (props: Props) => {
const actionButtons = useMemo(
() => (
<>
{prevPosition.amounts.borrowed > 0 && (
{(prevPosition.amounts.borrowedPrimary > 0 ||
prevPosition.amounts.borrowedSecondary > 0) && (
<Button
onClick={() => setIsRepay(!isRepay)}
text={isRepay ? t('fields.managePosition') : t('fields.repayDebt')}
@ -158,7 +164,8 @@ const EditVault = (props: Props) => {
setIsRepay,
lockupTimeAndUnit.time,
lockupTimeAndUnit.unit,
prevPosition.amounts.borrowed,
prevPosition.amounts.borrowedPrimary,
prevPosition.amounts.borrowedSecondary,
],
)
@ -200,9 +207,13 @@ const EditVault = (props: Props) => {
!isRepay &&
(prevPosition.amounts.primary > position.amounts.primary ||
prevPosition.amounts.secondary > position.amounts.secondary ||
prevPosition.amounts.borrowed > position.amounts.borrowed)
prevPosition.amounts.borrowedPrimary > position.amounts.borrowedPrimary ||
prevPosition.amounts.borrowedSecondary > position.amounts.borrowedSecondary)
const isNotRepaying = isRepay && repayPosition.amounts.borrowed >= prevPosition.amounts.borrowed
const isNotRepaying =
isRepay &&
repayPosition.amounts.borrowedPrimary >= prevPosition.amounts.borrowedPrimary &&
repayPosition.amounts.borrowedSecondary >= prevPosition.amounts.borrowedSecondary
const isWithoutFee = (!isRepay && !editFee) || (isRepay && !repayFee)
const isSameAmounts =
(isEqual(prevPosition.amounts, position.amounts) && !isRepay) ||
@ -280,7 +291,7 @@ const EditVault = (props: Props) => {
type={isRepay ? 'repay' : showUnlockBtn ? 'unlock' : 'edit'}
position={position}
prevPosition={prevPosition}
repayAmount={prevPosition.amounts.borrowed - repayPosition.amounts.borrowed}
repayAmount={prevPosition.amounts[borrowKey] - repayPosition.amounts[borrowKey]}
vault={props.activeVault}
className={styles.tooltip}
/>

View File

@ -56,6 +56,11 @@ const RepayVault = (props: Props) => {
const disableConfirmBtn = !repayFee || isSameAmounts
const borrowKey =
props.activeVault.position.borrowDenom === props.activeVault.denoms.primary
? 'borrowedPrimary'
: 'borrowedSecondary'
return (
<>
<Notification
@ -102,7 +107,7 @@ const RepayVault = (props: Props) => {
type={'repay'}
position={repayPosition}
prevPosition={prevPosition}
repayAmount={prevPosition.amounts.borrowed - repayPosition.amounts.borrowed}
repayAmount={prevPosition.amounts[borrowKey] - repayPosition.amounts[borrowKey]}
vault={props.activeVault}
className={styles.tooltip}
/>

View File

@ -115,6 +115,11 @@ const commonSlice = (
config.NETWORK_CONFIG.rpcUrl = serializeUrl(config.NETWORK_CONFIG.rpcUrl)
config.NETWORK_CONFIG.restUrl = serializeUrl(config.NETWORK_CONFIG.restUrl)
const storageDisplayCurrency = localStorage.getItem('displayCurrency')
if (storageDisplayCurrency) {
config.NETWORK_CONFIG.displayCurrency = JSON.parse(storageDisplayCurrency)
}
set({
otherAssets: config.NETWORK_CONFIG.assets.other,
whitelistedAssets: config.NETWORK_CONFIG.assets.whitelist,

View File

@ -25,9 +25,16 @@ const oraclesSlice = (set: NamedSet<Store>, get: GetState<Store>): OraclesSlice
const baseCurrency = get().baseCurrency
const networkConfig = get().networkConfig
const displayCurrency = networkConfig?.displayCurrency
const assets = [...whitelistedAssets, ...otherAssets]
const exchangeRatesState = get().exchangeRatesState
const assets: Asset[] = [...whitelistedAssets, ...otherAssets]
if (!coin || !exchangeRates || !assets.length || !displayCurrency) {
if (
!coin ||
exchangeRatesState === State.INITIALISING ||
!exchangeRates?.find((rate) => rate.denom === displayCurrency.denom) ||
!assets.length ||
!displayCurrency
) {
return 0
}
@ -88,13 +95,19 @@ const oraclesSlice = (set: NamedSet<Store>, get: GetState<Store>): OraclesSlice
const wasmQueryResults = data.prices
const exchangeRates: Coin[] = get().exchangeRates ?? []
const baseCurrency = get().baseCurrency
const networkConfig = get().networkConfig
const displayCurrency = networkConfig?.displayCurrency
get()
.whitelistedAssets?.filter((asset: Asset) => !!asset.denom)
.forEach((asset: Asset) => {
const denom = asset.denom
const hasBaseCurrency = exchangeRates?.find(
(ratesAsset) => ratesAsset.denom === baseCurrency.denom,
)
if (denom === get().baseCurrency.denom) {
if (denom === baseCurrency.denom && !hasBaseCurrency) {
exchangeRates.push({ denom, amount: '1' })
return
}
@ -106,6 +119,11 @@ const oraclesSlice = (set: NamedSet<Store>, get: GetState<Store>): OraclesSlice
denom,
amount: typeof exchangeRateResult === 'string' ? exchangeRateResult || '0.00' : '0.00',
}
if (asset.denom === displayCurrency.denom) {
set({
baseToDisplayCurrencyRatio: 1 / Number(exchangeRate.amount),
})
}
updateExchangeRate(exchangeRate, exchangeRates)
})
set({

View File

@ -119,6 +119,8 @@ const redBankSlice = (set: NamedSet<Store>, get: GetState<Store>): RedBankSlice
incentiveInfo,
isCollateral: true,
depositCap: depositCap,
borrowEnabled: marketInfo?.borrow_enabled ?? false,
depositEnabled: marketInfo?.deposit_enabled ?? false,
}
redBankAsset.subRows = [{ ...redBankAsset }]
redBankAssets.push(redBankAsset)

View File

@ -324,25 +324,50 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
(unlockTime) => unlockTime?.vaultAddress === curr.address,
)?.unlockAtTimestamp
let primarySupplyAmount = Number(
let primaryAmount = Number(
findByDenom(primaryAndSecondaryAmount.coins, curr.denoms.primary)?.amount || 0,
)
const secondaryAmount = Number(
let secondaryAmount = Number(
findByDenom(primaryAndSecondaryAmount.coins, curr.denoms.secondary)?.amount || 0,
)
const borrowedAmount = Number(creditAccountPosition.debts[0]?.amount || 0)
if (borrowedAmount > secondaryAmount) {
const swappedToPrimary = Math.round(
get().convertToBaseCurrency({
denom: curr.denoms.secondary,
amount: (borrowedAmount - secondaryAmount).toString(),
}),
)
primarySupplyAmount -= swappedToPrimary
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 secondarySupplyAmount = Math.max(secondaryAmount - borrowedAmount, 0)
const borrowedDenom: string = creditAccountPosition.debts[0]?.denom || ''
if (borrowedPrimaryAmount > primaryAmount) {
const swapped = Math.round(
get().convertToBaseCurrency({
denom: borrowedDenom,
amount: (borrowedPrimaryAmount - primaryAmount).toString(),
}),
)
secondaryAmount -= swapped
}
if (borrowedSecondaryAmount > secondaryAmount) {
const swapped = Math.round(
get().convertToBaseCurrency({
denom: borrowedDenom,
amount: (borrowedSecondaryAmount - secondaryAmount).toString(),
}),
)
primaryAmount -= swapped
}
const primarySupplyAmount = Math.max(primaryAmount - borrowedPrimaryAmount, 0)
const secondarySupplyAmount = Math.max(secondaryAmount - borrowedSecondaryAmount, 0)
const borrowedAmount = Math.max(borrowedPrimaryAmount, borrowedSecondaryAmount)
const convertToBaseCurrency = get().convertToBaseCurrency
const redBankAssets = get().redBankAssets
@ -357,14 +382,15 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
})
const borrowedValue = convertToBaseCurrency({
denom: curr.denoms.secondary,
denom: borrowedDenom,
amount: borrowedAmount.toString(),
})
const values = {
primary: primaryValue,
secondary: secondaryValue,
borrowed: borrowedValue,
borrowedPrimary: borrowedDenom === curr.denoms.primary ? borrowedValue : 0,
borrowedSecondary: borrowedDenom === curr.denoms.secondary ? borrowedValue : 0,
net: primaryValue + secondaryValue,
total: primaryValue + secondaryValue + borrowedValue,
}
@ -390,7 +416,8 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
amounts: {
primary: primarySupplyAmount,
secondary: secondarySupplyAmount,
borrowed: borrowedAmount,
borrowedPrimary: borrowedDenom === curr.denoms.primary ? borrowedAmount : 0,
borrowedSecondary: borrowedDenom === curr.denoms.secondary ? borrowedAmount : 0,
lp: {
amount: vaultTokenAmounts.unlocking,
primary: Number(
@ -416,6 +443,7 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
ltv: leverageToLtv(leverage),
...(unlockTime ? { unlockAtTimestamp: unlockTime } : {}),
status: getPositionStatus(unlockTime),
borrowDenom: borrowedDenom,
}
prev.activeVaults.push({ ...curr, position })

View File

@ -7,6 +7,7 @@
atom: $colorTokenATOM;
axlusdc: $colorTokenAxlUSDC;
juno: $colorTokenJUNO;
statom: $colorTokenStATOM;
/* COLORS */
success: $colorInfoProfit;

View File

@ -1,43 +1,45 @@
@use 'sass:math';
$rem-base: 15px;
$rem-base: 16px;
/* Colors */
$colorWhite: #ffffff;
$colorGrey: #3a3c49;
$colorGreyLight: #bfbfbf;
$colorGreyHighlight: #4c4c4c;
$colorGreyMedium: #5f697a;
$colorGreyDark: #1a1c25;
$colorWhite: #f5f5f5;
$colorGrey: #bdbdbd;
$colorGreyLight: #e0e0e0;
$colorGreyHighlight: #efefef;
$colorGreyMedium: #9e9e9e;
$colorGreyDark: #616161;
/* CI Colors */
$colorPrimary: #14a693;
$colorPrimaryHighlight: #15bfa9;
$colorPrimaryAlpha: rgba(20, 166, 147, 0.15);
$colorSecondary: #524bb1;
$colorSecondaryHighlight: #6962cc;
$colorSecondaryDark: #440b37;
$colorSecondaryAlpha: rgba(68, 11, 55, 0.7);
$colorAccent: #2c1b2f;
$colorAccentHighlight: #421f32;
$colorAccentDark: #341a2a;
$colorAccentInverted: #345dff;
$colorPrimary: #0000ff;
$colorPrimaryHighlight: #6962cc;
$colorPrimaryAlpha: rgba(0, 0, 255, 0.05);
$colorSecondary: #212121;
$colorSecondaryHighlight: #424242;
$colorSecondaryDark: #111111;
$colorSecondaryAlpha: rgba(17, 17, 17, 0.15);
$colorAccent: $colorGreyMedium;
$colorAccentHighlight: $colorGreyMedium;
$colorAccentDark: $colorGreyDark;
$colorAccentInverted: $colorGreyLight;
/* Info Colors */
$colorInfoProfit: #41a4a9;
$colorInfoLoss: #f96363;
$colorInfoWarning: #c83333;
$colorInfoVoteAgainst: #eb9e49;
$colorInfoProfit: #c4e7e9;
$colorInfoLoss: #c8aaaa;
$colorInfoWarning: #ffb5b5;
$colorInfoVoteAgainst: #6c5a46;
/* Token Colors */
$colorTokenMARS: #a03b45;
$colorTokenMARS: #dd5b65;
$colorTokenOSMO: #9f1ab9;
$colorTokenATOM: #6f7390;
$colorTokenAxlUSDC: #478edc;
$colorTokenJUNO: black;
$colorTokenStATOM: #e50571;
$colorGradientOSMO: linear-gradient(to bottom, #3a02e2, #e700ca);
$colorGradientATOM: linear-gradient(to bottom, #2e3148, #6f7390);
$colorGradientAxlUSDC: linear-gradient(to bottom, #1f5c9e, #478edc);
$colorGradientStATOM: linear-gradient(to bottom, #e50571, #fb5da9);
/* Alpha Colors */
$alphaWhite10: rgba(255, 255, 255, 0.1);
@ -60,32 +62,29 @@ $alphaBlack80: rgba(0, 0, 0, 0.8);
$alphaBlack90: rgba(0, 0, 0, 0.9);
/* Background Colors */
$backgroundBody: #562a3b;
$backgroundBodyDark: #141621;
$backgroundInTile: $alphaBlack30;
$backgroundFooter: $alphaBlack20;
$backgroundBody: $colorGrey;
$backgroundBodyDark: $backgroundBody;
$backgroundInTile: transparent;
$backgroundFooter: transparent;
/* Slider Colors */
$sliderThumb: $colorWhite;
$sliderMark: $colorGreyLight;
$sliderThumb: $colorGreyDark;
$sliderMark: $colorGreyDark;
/* Tooltip Colors */
$tooltipIconColor: $alphaWhite20;
$tableSort: $alphaWhite20;
$tableSortActive: $colorWhite;
$tableHeader: $alphaWhite50;
$tooltipIconColor: $alphaBlack60;
/* Table Colors */
$tableBorder: $alphaWhite10;
$tableBorderEnd: $alphaWhite80;
$tableSort: $alphaWhite20;
$tableSortActive: $colorWhite;
$tableHeader: $alphaWhite40;
$tableLabel: $alphaWhite60;
$tableBorder: $alphaBlack30;
$tableBorderEnd: $alphaBlack80;
$tableSort: $alphaBlack20;
$tableSortActive: $alphaBlack90;
$tableHeader: $alphaBlack40;
$tableLabel: $colorSecondaryDark;
/* Graph Colors */
$graphLiquidationsLine: $alphaWhite70;
$graphAxis: $alphaWhite40;
$graphLiquidationsLine: $alphaBlack70;
$graphAxis: $alphaBlack40;
/* Shadows */
$shadowInset: inset 0px 2px 2px rgba(0, 0, 0, 0.25);
@ -96,43 +95,40 @@ $shadowInset: inset 0px 2px 2px rgba(0, 0, 0, 0.25);
/* Devider */
@mixin devider10 {
border-bottom: 1px solid $alphaWhite10;
border-bottom: 1px solid $alphaBlack10;
}
@mixin devider20 {
border-bottom: 1px solid $alphaWhite20;
border-bottom: 1px solid $alphaBlack20;
}
@mixin devider40 {
border-bottom: 1px solid $alphaWhite40;
border-bottom: 1px solid $alphaBlack40;
}
@mixin devider60 {
border-bottom: 1px solid $alphaWhite60;
border-bottom: 1px solid $alphaBlack60;
}
/* Backgrounds */
@mixin bgBody {
background-color: $backgroundBody;
background-size: 100% auto;
background-image: url('../images/bg.svg');
background-position: center top;
}
@mixin bgTableHover {
background-color: transparent;
}
@mixin bgBodyDark {
background-color: $backgroundBodyDark;
}
@mixin bgTableHover {
background-color: rgba($colorPrimary, 0.2);
}
@mixin bgProposalActive {
background: linear-gradient(90deg, #10aa93 2.6%, #248aa9 97.92%);
}
@mixin bgProposalHover {
background-color: #05252f;
background-color: $colorGreyMedium;
}
@mixin bgTile($deg: 99.79) {
@ -144,7 +140,7 @@ $shadowInset: inset 0px 2px 2px rgba(0, 0, 0, 0.25);
}
@mixin bgInTile {
background: $alphaBlack30;
background: $backgroundInTile;
}
@mixin bgOverlay {
@ -161,11 +157,11 @@ $shadowInset: inset 0px 2px 2px rgba(0, 0, 0, 0.25);
}
@mixin bgTileDevider {
background-color: $alphaWhite60;
background-color: $alphaBlack60;
}
@mixin bgDevider {
background-color: $alphaWhite20;
background-color: $alphaBlack20;
}
@mixin bgInput {
@ -174,68 +170,60 @@ $shadowInset: inset 0px 2px 2px rgba(0, 0, 0, 0.25);
@mixin bgPrimary {
background-color: $colorPrimary;
color: $colorWhite;
}
@mixin bgSecondary {
background-color: $colorSecondary;
color: $colorWhite;
}
@mixin bgTertiary {
background-color: rgba(82, 75, 177, 0.5);
background-color: $alphaBlack60;
color: $colorWhite;
}
@mixin bgLimit {
background: linear-gradient(
to right,
#15bfa9 20.9%,
#5e4bb1 49.68%,
#382685 82.55%,
#c83333 100%
);
background: $colorPrimary;
}
@mixin bgLimitOpacity {
background: linear-gradient(
to right,
#15bfa830 20.9%,
#5e4bb130 49.68%,
#38268530 82.55%,
#c8333330 100%
);
background: $colorPrimary;
}
@mixin bgHatched {
background-image: linear-gradient(
135deg,
transparent 33.33%,
#826d6b 33.33%,
#826d6b 50%,
transparent 50%,
transparent 83.33%,
#826d6b 83.33%,
#826d6b 100%
#1a1c25 33.33%,
rgba(255, 255, 255, 0.2) 33.33%,
rgba(255, 255, 255, 0.2) 50%,
#1a1c25 50%,
#1a1c25 83.33%,
rgba(255, 255, 255, 0.2) 83.33%,
rgba(255, 255, 255, 0.2) 100%
);
background-size: 5px 5px;
}
/* GLOWS */
/* GLOWS */
@mixin glowXS {
filter: blur(1px);
display: none;
}
@mixin glowS {
filter: blur(3px);
display: none;
}
@mixin glowM {
filter: blur(4px);
display: none;
}
@mixin glowL {
filter: blur(5px);
display: none;
}
@mixin glowXL {
filter: blur(8px);
display: none;
}
@mixin glowXXL {
filter: blur(24px);
display: none;
}
/* Typography */
@ -243,11 +231,11 @@ $fontWeightLight: 300;
$fontWeightRegular: 400;
$fontWeightSemibold: 600;
$fontColorDarkPrimary: $colorSecondaryDark;
$fontColorDarkSecondary: rgba(68, 8, 55, 0.7);
$fontColorLightPrimary: $colorWhite;
$fontColorLightSecondary: $alphaWhite60;
$fontColorLightTertiary: rgba(255, 255, 255, 0.4);
$fontColorDarkPrimary: $colorWhite;
$fontColorDarkSecondary: $colorSecondaryDark;
$fontColorLightPrimary: $colorSecondaryDark;
$fontColorLightSecondary: $alphaBlack30;
$fontColorLightTertiary: $colorSecondaryDark;
$fontColorLtv: $colorWhite;
@mixin typoH1 {
@ -263,8 +251,6 @@ $fontColorLtv: $colorWhite;
@mixin typoH2caps {
@include typoH2;
text-transform: uppercase;
letter-spacing: rem-calc(9);
}
@mixin typoH3 {
@ -274,8 +260,6 @@ $fontColorLtv: $colorWhite;
@mixin typoH3caps {
font-size: rem-calc(30.42);
line-height: space(10);
text-transform: uppercase;
}
@mixin typoH4 {
@ -286,8 +270,6 @@ $fontColorLtv: $colorWhite;
@mixin typoH4caps {
@include typoH4;
text-transform: uppercase;
letter-spacing: rem-calc(3);
}
@mixin typoXXL {
@ -297,9 +279,7 @@ $fontColorLtv: $colorWhite;
@mixin typoXXLcaps {
@include typoXXL;
font-weight: $fontWeightRegular;
text-transform: uppercase;
letter-spacing: rem-calc(3);
font-weight: $fontWeightLight;
}
@mixin typoXL {
@ -309,8 +289,6 @@ $fontColorLtv: $colorWhite;
@mixin typoXLcaps {
@include typoXL;
letter-spacing: rem-calc(5);
text-transform: uppercase;
font-weight: $fontWeightLight;
}
@ -322,8 +300,6 @@ $fontColorLtv: $colorWhite;
@mixin typoLcaps {
@include typoL;
font-weight: $fontWeightSemibold;
text-transform: uppercase;
letter-spacing: rem-calc(3);
}
@mixin typoM {
@ -333,7 +309,6 @@ $fontColorLtv: $colorWhite;
@mixin typoMcaps {
@include typoM;
text-transform: uppercase;
}
@mixin typoS {
@ -344,8 +319,6 @@ $fontColorLtv: $colorWhite;
@mixin typoScaps {
@include typoS;
font-weight: $fontWeightSemibold;
text-transform: uppercase;
letter-spacing: rem-calc(3);
}
@mixin typoXS {
@ -356,8 +329,6 @@ $fontColorLtv: $colorWhite;
@mixin typoXScaps {
@include typoXS;
font-weight: $fontWeightSemibold;
text-transform: uppercase;
letter-spacing: rem-calc(3);
}
@mixin typoXXS {
@ -368,8 +339,6 @@ $fontColorLtv: $colorWhite;
@mixin typoXXScaps {
@include typoXXS;
font-weight: $fontWeightSemibold;
text-transform: uppercase;
letter-spacing: rem-calc(2);
}
@mixin typoXXXS {
@ -380,20 +349,15 @@ $fontColorLtv: $colorWhite;
@mixin typoXXXScaps {
@include typoXXXS;
font-weight: $fontWeightSemibold;
text-transform: uppercase;
letter-spacing: rem-calc(2);
}
@mixin typoButton {
font-family: Inter, sans-serif;
@include typoS;
font-weight: $fontWeightSemibold;
}
@mixin typoNav {
@include typoL;
text-transform: uppercase;
letter-spacing: rem-calc(5);
}
@mixin typoNetwork {
@ -467,100 +431,70 @@ $spacingBase: 4;
/* LAYOUTS */
@mixin layoutTile {
@include bgTile;
border: rem-calc(7) solid $colorAccentHighlight;
border-radius: $borderRadiusXL;
padding: space(1);
background: $colorGreyHighlight;
border: 2px solid $colorWhite;
box-shadow: 0 0 0 3px $colorGreyHighlight, 12px 12px 0 0 rgb(0 0 0 / 50%) !important;
height: fit-content;
}
@mixin layoutTooltip {
@include padding(2, 4);
@include bgTooltip;
@include typoS;
box-shadow: 0 rem-calc(3) rem-calc(4) rgba(0, 0, 0, 0.14),
0 rem-calc(3) rem-calc(3) rgba(0, 0, 0, 0.12), 0 rem-calc(1) rem-calc(8) rgba(0, 0, 0, 0.2);
border-radius: $borderRadiusL;
max-width: rem-calc(350);
padding: space(3);
background: $colorGreyLight;
border: 1px solid $colorSecondaryDark;
}
@mixin layoutPopover {
@include bgPopover;
box-shadow: 0 rem-calc(2) rem-calc(2) rgba(0, 0, 0, 0.14),
0 rem-calc(1) rem-calc(5) rgba(0, 0, 0, 0.2);
border-radius: $borderRadiusL;
padding: space(3);
background: $colorGreyLight;
border: 1px solid $colorSecondaryDark;
}
@mixin layoutIncentiveButton {
--border-width: 3px;
background-color: #946582;
position: relative;
border: none;
margin: rem-calc(3) rem-calc(11) 0 0;
height: rem-calc(28);
&:hover {
border: none;
background-color: darken(#946582, 10%);
}
&::after {
border-radius: $borderRadiusXXL;
position: absolute;
content: '';
top: calc(-1 * var(--border-width));
left: calc(-1 * var(--border-width));
z-index: -1;
width: calc(100% + var(--border-width) * 2);
height: calc(100% + var(--border-width) * 2);
background: linear-gradient(
90deg,
rgba(105, 98, 204, 0.8) 0%,
rgba(105, 98, 204, 1) 40%,
rgba(255, 255, 255, 1) 50%,
rgba(105, 98, 204, 1) 60%,
rgba(105, 98, 204, 0.8) 100%
);
background-size: 300% 300%;
background-position: 0 50%;
animation: moveGradient 6s alternate infinite;
}
}
@mixin layoutLogo {
> svg {
width: rem-calc(50);
height: rem-calc(50);
width: rem-calc(57);
height: rem-calc(57);
path {
stroke: $fontColorLightPrimary;
}
}
}
@mixin layoutGlobal {
opacity: 1 !important;
box-shadow: none !important;
}
/* Buttons */
$buttonBorder: $alphaWhite40;
$buttonBorderHover: $colorWhite;
$buttonBorder: $alphaBlack40;
$buttonBorderHover: $colorSecondaryDark;
@mixin buttonS {
@include typoS;
@include padding(1.5, 5);
min-height: rem-calc(32);
height: rem-calc(32);
}
@mixin buttonM {
@include typoM;
@include padding(2.5, 6);
min-height: rem-calc(40);
height: rem-calc(40);
}
@mixin buttonL {
@include typoL;
@include padding(2.5, 6);
min-height: rem-calc(56);
height: rem-calc(56);
}
@mixin buttonSolidPrimary {
&.primary {
background-color: $colorPrimary;
color: $colorWhite;
&:hover,
&:focus {
@ -576,6 +510,7 @@ $buttonBorderHover: $colorWhite;
@mixin buttonSolidSecondary {
&.secondary {
background-color: $colorSecondary;
color: $colorWhite;
&:hover,
&:focus {
@ -590,34 +525,33 @@ $buttonBorderHover: $colorWhite;
@mixin buttonSolidTertiary {
&.tertiary {
background-color: $colorSecondaryAlpha;
border: 1px solid $alphaWhite60;
background-color: $colorSecondaryDark;
color: $colorWhite;
border: 1px solid $alphaBlack30;
&:hover,
&:focus {
border: 1px solid $fontColorLightPrimary;
background-color: $colorSecondaryDark;
border: 1px solid $alphaBlack20;
}
&:active {
border: 1px solid $fontColorLightPrimary;
background-color: lighten($colorSecondaryDark, 10%);
}
}
}
/* Border Radius */
$borderRadiusXXXS: rem-calc(3);
$borderRadiusXXS: rem-calc(4);
$borderRadiusXS: rem-calc(5);
$borderRadiusS: rem-calc(8);
$borderRadiusM: rem-calc(9);
$borderRadiusL: rem-calc(12);
$borderRadiusXL: rem-calc(16);
$borderRadiusXXL: rem-calc(20);
$borderRadiusXXXL: rem-calc(30);
$borderRadiusXXXXL: rem-calc(100);
$borderRadiusRound: 50%;
$borderRadiusXXXS: 0;
$borderRadiusXXS: 0;
$borderRadiusXS: 0;
$borderRadiusS: 0;
$borderRadiusM: 0;
$borderRadiusL: 0;
$borderRadiusXL: 0;
$borderRadiusXXL: 0;
$borderRadiusXXXL: 0;
$borderRadiusXXXXL: 0;
$borderRadiusRound: 0;
/* Dimensions */
$headerHeight: rem-calc(86);

View File

@ -2,7 +2,7 @@ export enum QUERY_KEYS {
BLOCK_HEIGHT = 'blockHeight',
MARS_BALANCE = 'marsBalance',
MARS_ORACLE = 'marsOracle',
MARS_PRICE = 'marsPrice',
SPOT_PRICE = 'spotPrice',
REDBANK = 'redbank',
USER_BALANCE = 'userBalance',
USER_DEBT = 'userDebt',
@ -15,4 +15,5 @@ export enum QUERY_KEYS {
ESTIMATE_FARM_FEE = 'estimateFarmFee',
PROVIDE_LIQUIDITY = 'provideLiquidity',
UNLOCK_MESSAGE = 'unlockMessage',
USD_PRICE = 'usdPrice',
}

View File

@ -2,12 +2,12 @@ interface Asset {
color: string
name: string
denom: string
symbol: 'OSMO' | 'ATOM' | 'JUNO' | 'axlUSDC'
symbol: 'OSMO' | 'ATOM' | 'JUNO' | 'axlUSDC' | 'stATOM'
contract_addr?: string
logo: string
decimals: number
hasOraclePrice: boolean
poolId?: number
poolBase?: string
}
interface OtherAsset extends Omit<Asset, 'symbol'> {
@ -33,6 +33,8 @@ interface RedBankAsset extends Asset {
incentiveInfo?: IncentiveInfo
depositCap: number
depositLiquidity: numnber
borrowEnabled: boolean
depositEnabled: boolean
// This is a hack, subRows can only contain same data model
subRows?: DepositAsset[]
}

View File

@ -38,7 +38,8 @@ interface Position {
amounts: {
primary: number
secondary: number
borrowed: number
borrowedPrimary: number
borrowedSecondary: number
lp: {
amount: string // Need to be string as number can be extremely large
primary: number
@ -49,7 +50,8 @@ interface Position {
values: {
primary: number
secondary: number
borrowed: number
borrowedPrimary: number
borrowedSecondary: number
total: number
net: number
}
@ -61,6 +63,7 @@ interface Position {
ltv: number
currentLeverage: number
unlockAtTimestamp?: number
borrowDenom: string | null
}
interface PositionBarItem {

View File

@ -4,6 +4,7 @@ interface NetworkConfig {
rpcUrl: string
restUrl: string
apolloAprUrl: string
priceApiUrl: string
contracts: {
redBank: string
incentives: string
@ -16,12 +17,14 @@ interface NetworkConfig {
whitelist: Asset[]
other: OtherAsset[]
}
displayCurrency: {
denom: string
prefix: string
suffix: string
decimals: number
}
displayCurrency: displayCurrency
appUrl: string
wallets: import('@marsprotocol/wallet-connector').WalletID[]
}
interface DisplayCurrency {
denom: string
prefix: string
suffix: string
decimals: number
}

View File

@ -1,5 +1,5 @@
interface RedBankData {
balance: {
balance?: {
balance: import('@cosmjs/stargate').Coin[]
}
rbwasmkey: {
@ -11,6 +11,8 @@ interface RedBankData {
JUNOMarketIncentive: MarketIncentive
axlUSDCMarket: Market
axlUSDCMarketIncentive: MarketIncentive
stATOMMarket: Market
stATOMMarketIncentive: MarketIncentive
collateral: UserCollateral[]
unclaimedRewards: string
}
@ -44,7 +46,9 @@ interface InterestRateModel {
interface MarketIncentive {
denom: string
emission_per_second: number
index: number
emission_per_second: string
index: string
last_updated: number
start_time: number
duration: number
}