Merge pull request #9 from mars-protocol/hotfix/ledger-support

fix: fixed ledger support and other minor improvements. LGTM
This commit is contained in:
dextermars 2023-02-10 18:20:57 +00:00 committed by GitHub
commit cf9fe7d5cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 274 additions and 158 deletions

View File

@ -22,7 +22,7 @@
"@cosmjs/launchpad": "^0.27.1", "@cosmjs/launchpad": "^0.27.1",
"@cosmjs/proto-signing": "^0.29.5", "@cosmjs/proto-signing": "^0.29.5",
"@cosmjs/stargate": "^0.29.5", "@cosmjs/stargate": "^0.29.5",
"@marsprotocol/wallet-connector": "^1.3.0", "@marsprotocol/wallet-connector": "^1.4.2",
"@material-ui/core": "^4.12.4", "@material-ui/core": "^4.12.4",
"@material-ui/icons": "^4.11.3", "@material-ui/icons": "^4.11.3",
"@ramonak/react-progress-bar": "^5.0.3", "@ramonak/react-progress-bar": "^5.0.3",

View File

@ -1,19 +0,0 @@
// This file configures the initialization of Sentry on the browser.
// The config you add here will be used whenever a page is visited.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from '@sentry/nextjs'
const SENTRY_DSN = process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN
Sentry.init({
environment: process.env.NEXT_PUBLIC_SENTRY_ENV,
dsn: SENTRY_DSN,
// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 0.5,
// ...
// Note: if you want to override the automatic release value, do not set a
// `release` value here - use the environment variable `SENTRY_RELEASE`, so
// that it will also get attached to your source maps
enabled: process.env.NODE_ENV !== 'development',
})

View File

@ -1,4 +0,0 @@
defaults.url=https://sentry.io/
defaults.org=delphi-mars
defaults.project=mars-dapp
cli.executable=../../../.npm/_npx/a8388072043b4cbc/node_modules/@sentry/cli/bin/sentry-cli

View File

@ -1,18 +0,0 @@
// This file configures the initialization of Sentry on the server.
// The config you add here will be used whenever the server handles a request.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from '@sentry/nextjs'
const SENTRY_DSN = process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN
Sentry.init({
dsn: SENTRY_DSN,
// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 0.5,
// ...
// Note: if you want to override the automatic release value, do not set a
// `release` value here - use the environment variable `SENTRY_RELEASE`, so
// that it will also get attached to your source maps
enabled: process.env.NODE_ENV !== 'development',
})

View File

@ -1,7 +1,6 @@
import classNames from 'classnames' import classNames from 'classnames'
import { CircularProgress } from 'components/common' import { CircularProgress } from 'components/common'
import React from 'react' import React, { ReactNode } from 'react'
import { ReactNode } from 'react'
import styles from './Button.module.scss' import styles from './Button.module.scss'
@ -46,7 +45,7 @@ export const Button = React.forwardRef<any, Props>(
styles[size], styles[size],
styles[color], styles[color],
styles[variant], styles[variant],
disabled && styles.disabled, (disabled || showProgressIndicator) && styles.disabled,
className, className,
) )
return ( return (

View File

@ -0,0 +1,18 @@
@import 'src/styles/master';
.errorMessage {
text-align: left;
@include margin(2, 0, -2);
display: block;
width: 100%;
@include typoXS;
color: $colorInfoWarning;
&.center {
text-align: center;
}
&.right {
text-align: right;
}
}

View File

@ -0,0 +1,14 @@
import classNames from 'classnames'
import styles from './ErrorMessage.module.scss'
interface Props {
errorMessage?: string
alignment?: 'left' | 'center' | 'right'
}
export const ErrorMessage = (props: Props) => {
const classes = classNames(styles.errorMessage, props.alignment && styles[props.alignment])
return props.errorMessage ? <p className={classes}>{props.errorMessage}</p> : null
}

View File

@ -220,15 +220,6 @@
button { button {
width: 100%; width: 100%;
} }
.error {
@include margin(2, 0, 0);
@include typoS;
width: 100%;
text-align: center;
font-weight: $fontWeightSemibold;
color: $colorInfoLoss;
}
} }
} }

View File

@ -1,7 +1,15 @@
import { ChainInfoID, SimpleChainInfoList, TxBroadcastResult } from '@marsprotocol/wallet-connector' import { ChainInfoID, SimpleChainInfoList, TxBroadcastResult } from '@marsprotocol/wallet-connector'
import { useQueryClient } from '@tanstack/react-query' import { useQueryClient } from '@tanstack/react-query'
import classNames from 'classnames' import classNames from 'classnames'
import { AnimatedNumber, Button, DisplayCurrency, SVG, Tooltip, TxLink } from 'components/common' import {
AnimatedNumber,
Button,
DisplayCurrency,
ErrorMessage,
SVG,
Tooltip,
TxLink,
} from 'components/common'
import { MARS_DECIMALS, MARS_SYMBOL } from 'constants/appConstants' import { MARS_DECIMALS, MARS_SYMBOL } from 'constants/appConstants'
import { getClaimUserRewardsMsgOptions } from 'functions/messages' import { getClaimUserRewardsMsgOptions } from 'functions/messages'
import { useEstimateFee } from 'hooks/queries' import { useEstimateFee } from 'hooks/queries'
@ -36,6 +44,7 @@ export const IncentivesButton = () => {
// --------------- // ---------------
const [showDetails, setShowDetails] = useState(false) const [showDetails, setShowDetails] = useState(false)
const [disabled, setDisabled] = useState(true) const [disabled, setDisabled] = useState(true)
const [fetching, setFetching] = useState(false)
const [submitted, setSubmitted] = useState(false) const [submitted, setSubmitted] = useState(false)
const [response, setResponse] = useState<TxBroadcastResult>() const [response, setResponse] = useState<TxBroadcastResult>()
const [error, setError] = useState<string>() const [error, setError] = useState<string>()
@ -53,6 +62,7 @@ export const IncentivesButton = () => {
const onClickAway = useCallback(() => { const onClickAway = useCallback(() => {
setShowDetails(false) setShowDetails(false)
setResponse(undefined) setResponse(undefined)
setError(undefined)
}, []) }, [])
useEffect(() => { useEffect(() => {
@ -64,12 +74,22 @@ export const IncentivesButton = () => {
return getClaimUserRewardsMsgOptions() return getClaimUserRewardsMsgOptions()
}, [hasUnclaimedRewards]) }, [hasUnclaimedRewards])
const { data: fee } = useEstimateFee({ const { data: fee, error: feeError } = useEstimateFee({
msg: txMsgOptions?.msg, msg: txMsgOptions?.msg,
funds: [], funds: [],
contract: incentivesContractAddress, contract: incentivesContractAddress,
}) })
if (feeError && error !== feeError && !fee) {
setError(feeError as string)
}
useEffect(() => {
const isFetching = submitted || (!fee && !response && hasUnclaimedRewards)
if (fetching === isFetching) return
setFetching(isFetching)
}, [submitted, fetching, fee, response, hasUnclaimedRewards])
useEffect(() => { useEffect(() => {
if (error) { if (error) {
setDisabled(!hasUnclaimedRewards) setDisabled(!hasUnclaimedRewards)
@ -186,8 +206,8 @@ export const IncentivesButton = () => {
)} )}
<div className={styles.claimButton}> <div className={styles.claimButton}>
<Button <Button
disabled={disabled || submitted || (!fee && !response && hasUnclaimedRewards)} disabled={disabled}
showProgressIndicator={(!fee && !response && hasUnclaimedRewards) || submitted} showProgressIndicator={fetching}
text={ text={
Number(unclaimedRewards) > 0 && !disabled Number(unclaimedRewards) > 0 && !disabled
? t('incentives.claimRewards') ? t('incentives.claimRewards')
@ -196,7 +216,7 @@ export const IncentivesButton = () => {
onClick={() => (submitted ? null : claimRewards())} onClick={() => (submitted ? null : claimRewards())}
color='primary' color='primary'
/> />
{error && <div className={styles.error}>{error}</div>} <ErrorMessage errorMessage={error} alignment='center' />
</div> </div>
</div> </div>
</div> </div>

View File

@ -153,6 +153,7 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
margin-bottom: space(3); margin-bottom: space(3);
flex-wrap: wrap;
} }
} }

View File

@ -18,6 +18,7 @@ interface Props {
disabled?: boolean disabled?: boolean
amountUntilDepositCap: number amountUntilDepositCap: number
activeView: ViewType activeView: ViewType
walletBalance: number
inputCallback: (value: number) => void inputCallback: (value: number) => void
onEnterHandler: () => void onEnterHandler: () => void
setAmountCallback: (value: number) => void setAmountCallback: (value: number) => void
@ -33,6 +34,7 @@ export const InputSection = ({
disabled, disabled,
amountUntilDepositCap, amountUntilDepositCap,
activeView, activeView,
walletBalance,
inputCallback, inputCallback,
onEnterHandler, onEnterHandler,
setAmountCallback, setAmountCallback,
@ -90,15 +92,14 @@ export const InputSection = ({
useEffect( useEffect(
() => { () => {
if (asset.denom !== baseCurrency.denom || !checkForMaxValue) return
if ( if (
amount >= maxUsableAmount && (activeView === ViewType.Repay && walletBalance <= amount) ||
asset.denom === baseCurrency.denom && (activeView === ViewType.Deposit && amount >= maxUsableAmount)
!depositWarning &&
checkForMaxValue
) { ) {
setDepositWarning(true) if (!depositWarning) setDepositWarning(true)
} else { } else {
setDepositWarning(false) if (depositWarning) setDepositWarning(false)
} }
}, // eslint-disable-next-line react-hooks/exhaustive-deps }, // eslint-disable-next-line react-hooks/exhaustive-deps
[sliderValue, amount, maxUsableAmount], [sliderValue, amount, maxUsableAmount],

View File

@ -27,7 +27,7 @@
opacity: 0.4; opacity: 0.4;
&.active { &.active {
filter: grayscale(0); filter: unset;
opacity: 1; opacity: 1;
} }
@ -37,9 +37,23 @@
@include typoXScaps; @include typoXScaps;
} }
svg { .icon {
width: rem-calc(50); width: rem-calc(50);
height: auto; height: rem-calc(50);
display: block;
svg {
width: 100%;
height: auto;
}
&.redBank {
background: url('../../../images/redbank.svg');
}
&.farm {
background: url('../../../images/farm.svg');
}
} }
} }
} }

View File

@ -20,11 +20,14 @@ export const MobileNav = () => {
passHref passHref
className={classNames(styles.nav, !router.pathname.includes('farm') && styles.active)} className={classNames(styles.nav, !router.pathname.includes('farm') && styles.active)}
> >
<SVG.RedBankIcon /> <div className={classNames(styles.icon, styles.redBank)} />
<span>{t('global.redBank')}</span> <span>{t('global.redBank')}</span>
</Link> </Link>
<a className={styles.nav} target='_blank' href={networkConfig?.councilUrl} rel='noreferrer'> <a className={styles.nav} target='_blank' href={networkConfig?.councilUrl} rel='noreferrer'>
<SVG.CouncilIcon /> <div className={styles.icon}>
<SVG.CouncilIcon />
</div>
<span>{t('global.council')}</span> <span>{t('global.council')}</span>
</a> </a>
{FIELDS_FEATURE && ( {FIELDS_FEATURE && (
@ -33,7 +36,7 @@ export const MobileNav = () => {
passHref passHref
className={classNames(styles.nav, router.pathname.includes('farm') && styles.active)} className={classNames(styles.nav, router.pathname.includes('farm') && styles.active)}
> >
<SVG.FieldsIcon /> <div className={classNames(styles.icon, styles.farm)} />
<span>{t('global.fields')}</span> <span>{t('global.fields')}</span>
</Link> </Link>
)} )}

View File

@ -52,11 +52,14 @@ export const Tutorial = (props: Props) => {
}, [tutorialStep, props.step]) }, [tutorialStep, props.step])
const hideTutorial = () => { const hideTutorial = () => {
localStorage.setItem( if (props.type === 'fields') {
props.type === 'fields' ? FIELDS_TUTORIAL_KEY : RED_BANK_TUTORIAL_KEY, localStorage.setItem(FIELDS_TUTORIAL_KEY, 'true')
'true', return
) }
localStorage.setItem(RED_BANK_TUTORIAL_KEY, 'true')
useStore.setState({ showRedBankTutorial: false })
} }
const handleButtonClick = () => { const handleButtonClick = () => {
if (props.step === 3) hideTutorial() if (props.step === 3) hideTutorial()
setTutorialStep(props.type) setTutorialStep(props.type)

View File

@ -72,12 +72,6 @@
opacity: 0.4; opacity: 0.4;
margin-bottom: space(9); margin-bottom: space(9);
} }
.actionButton {
display: flex;
justify-content: center;
margin-bottom: space(3);
}
} }
.feeTooltipContent { .feeTooltipContent {

View File

@ -8,6 +8,7 @@ import {
Card, Card,
ConnectButton, ConnectButton,
DisplayCurrency, DisplayCurrency,
ErrorMessage,
InputSection, InputSection,
} from 'components/common' } from 'components/common'
import { findByDenom } from 'functions' import { findByDenom } from 'functions'
@ -44,6 +45,7 @@ interface Props {
totalBorrowBaseCurrencyAmount: number totalBorrowBaseCurrencyAmount: number
actionButtonSpec: ModalActionButton actionButtonSpec: ModalActionButton
submitted: boolean submitted: boolean
feeError?: string
txFee?: Coin txFee?: Coin
activeView: ViewType activeView: ViewType
denom: string denom: string
@ -64,6 +66,7 @@ export const Action = ({
totalBorrowBaseCurrencyAmount, totalBorrowBaseCurrencyAmount,
actionButtonSpec, actionButtonSpec,
submitted, submitted,
feeError,
txFee, txFee,
activeView, activeView,
denom, denom,
@ -313,22 +316,22 @@ export const Action = ({
if (microValue >= maxUsableAmount) microValue = maxUsableAmount if (microValue >= maxUsableAmount) microValue = maxUsableAmount
setAmountCallback(Number(formatValue(microValue, 0, 0, false, false, false, false, false))) setAmountCallback(Number(formatValue(microValue, 0, 0, false, false, false, false, false)))
setCapHit(amount > amountUntilDepositCap) setCapHit(amount > amountUntilDepositCap && activeView === ViewType.Deposit)
} }
const produceTabActionButton = () => { const produceTabActionButton = () => {
return ( return (
<Button <>
color='primary' <Button
className={styles.submitButton} color='primary'
disabled={ className={styles.submitButton}
actionButtonSpec.disabled || disabled={actionButtonSpec.disabled}
(amount > amountUntilDepositCap && activeView === ViewType.Deposit) onClick={() => actionButtonSpec.clickHandler()}
} showProgressIndicator={actionButtonSpec.fetching}
onClick={() => actionButtonSpec.clickHandler()} text={actionButtonSpec.text}
showProgressIndicator={actionButtonSpec.fetching} />
text={actionButtonSpec.text} <ErrorMessage errorMessage={feeError} alignment='center' />
/> </>
) )
} }
@ -465,7 +468,7 @@ export const Action = ({
actionButton={actionButton} actionButton={actionButton}
amount={amount} amount={amount}
availableText={produceAvailableText()} availableText={produceAvailableText()}
checkForMaxValue={activeView === ViewType.Deposit} checkForMaxValue={activeView === ViewType.Deposit || activeView === ViewType.Repay}
asset={currentAsset} asset={currentAsset}
disabled={ disabled={
submitted || submitted ||
@ -478,6 +481,7 @@ export const Action = ({
setAmountCallback={handleInputAmount} setAmountCallback={handleInputAmount}
amountUntilDepositCap={amountUntilDepositCap} amountUntilDepositCap={amountUntilDepositCap}
activeView={activeView} activeView={activeView}
walletBalance={walletBalance}
/> />
{/* SITUATION COMPARISON */} {/* SITUATION COMPARISON */}

View File

@ -13,6 +13,7 @@ export { CommonContainer } from './Containers/CommonContainer'
export { FieldsContainer } from './Containers/FieldsContainer' export { FieldsContainer } from './Containers/FieldsContainer'
export { CosmosWalletConnectProvider } from './CosmosWalletConnectProvider/CosmosWalletConnectProvider' export { CosmosWalletConnectProvider } from './CosmosWalletConnectProvider/CosmosWalletConnectProvider'
export { DisplayCurrency } from './DisplayCurrency/DisplayCurrency' export { DisplayCurrency } from './DisplayCurrency/DisplayCurrency'
export { ErrorMessage } from './ErrorMessage/ErrorMessage'
export { Footer } from './Footer/Footer' export { Footer } from './Footer/Footer'
export { Connect } from './Header/Connect' export { Connect } from './Header/Connect'
export { ConnectButton } from './Header/ConnectButton' export { ConnectButton } from './Header/ConnectButton'

View File

@ -237,7 +237,7 @@ export const useActiveVaultsColumns = () => {
</> </>
) )
case 'active': case 'active':
const apy = new BigNumber(row.original.position.apy.net).decimalPlaces(2).toNumber() const apy = new BigNumber(row.original.position.apy.net).toNumber()
const apyData = { const apyData = {
total: row.original.apy || 0, total: row.original.apy || 0,

View File

@ -91,7 +91,7 @@ export const useAvailableVaultsColumns = () => {
(asset) => asset.denom === row.original.denoms.secondary, (asset) => asset.denom === row.original.denoms.secondary,
) )
const maxBorrowRate = Number(borrowAsset?.borrowRate ?? 0) * row.original.ltv.max const maxBorrowRate = Number(borrowAsset?.borrowRate ?? 0) * row.original.ltv.max
const minAPY = new BigNumber(row.original.apy).decimalPlaces(2).toNumber() const minAPY = new BigNumber(row.original.apy).toNumber()
const maxAPY = new BigNumber(minAPY).times(maxLeverage).toNumber() - maxBorrowRate const maxAPY = new BigNumber(minAPY).times(maxLeverage).toNumber() - maxBorrowRate
const minDailyAPY = new BigNumber(convertApyToDailyApy(row.original.apy)) const minDailyAPY = new BigNumber(convertApyToDailyApy(row.original.apy))
@ -108,13 +108,22 @@ export const useAvailableVaultsColumns = () => {
<> <>
<TextTooltip <TextTooltip
hideStyling hideStyling
text={<AnimatedNumber amount={minAPY} />} text={
<AnimatedNumber
amount={Number(formatValue(minAPY, 2, 2, true, false, false, true))}
/>
}
tooltip={<Apy apyData={apyDataNoLev} leverage={1} />} tooltip={<Apy apyData={apyDataNoLev} leverage={1} />}
/> />
<span>-</span> <span>-</span>
<TextTooltip <TextTooltip
hideStyling hideStyling
text={<AnimatedNumber amount={maxAPY} suffix='%' />} text={
<AnimatedNumber
amount={Number(formatValue(maxAPY, 2, 2, true, false, false, true))}
suffix='%'
/>
}
tooltip={ tooltip={
<Apy apyData={apyDataLev} leverage={ltvToLeverage(row.original.ltv.max)} /> <Apy apyData={apyDataLev} leverage={ltvToLeverage(row.original.ltv.max)} />
} }

View File

@ -216,7 +216,13 @@ export const BreakdownTable = (props: Props) => {
<span className='faded'>{t('common.apy')}: </span> <span className='faded'>{t('common.apy')}: </span>
<TextTooltip <TextTooltip
hideStyling hideStyling
text={<AnimatedNumber amount={apy} suffix='%' abbreviated={false} />} text={
<AnimatedNumber
amount={Number(formatValue(apy, 2, 2, true, false, false, true))}
suffix='%'
abbreviated={false}
/>
}
tooltip={<Apy apyData={apyData} leverage={currentLeverage} />} tooltip={<Apy apyData={apyData} leverage={currentLeverage} />}
/> />
</div> </div>

View File

@ -120,7 +120,7 @@ export const RedbankAction = React.memo(
} }
}, [activeView, amount, redBankContractAddress, denom, isMax, userBalances]) }, [activeView, amount, redBankContractAddress, denom, isMax, userBalances])
const { data: fee } = useEstimateFee({ const { data: fee, error: feeError } = useEstimateFee({
msg: txMsgOptions?.msg, msg: txMsgOptions?.msg,
funds: funds:
activeView === ViewType.Deposit || activeView === ViewType.Repay activeView === ViewType.Deposit || activeView === ViewType.Repay
@ -131,8 +131,8 @@ export const RedbankAction = React.memo(
const produceActionButtonSpec = (): ModalActionButton => { const produceActionButtonSpec = (): ModalActionButton => {
return { return {
disabled: !Number(amount) || amount === 0 || typeof fee === 'undefined' || submitted, disabled: amount === 0 || capHit,
fetching: !!Number(amount) && amount > 0 && typeof fee === 'undefined' && !capHit, fetching: (amount > 0 && typeof fee === 'undefined') || submitted,
text: t(`redbank.${activeView.toLowerCase()}`), text: t(`redbank.${activeView.toLowerCase()}`),
clickHandler: handleAction, clickHandler: handleAction,
color: 'primary', color: 'primary',
@ -157,10 +157,14 @@ export const RedbankAction = React.memo(
// @ts-ignore // @ts-ignore
funds: txMsgOptions.funds || [], funds: txMsgOptions.funds || [],
contract: redBankContractAddress, contract: redBankContractAddress,
fee, fee: fee,
}) })
setResponse(res) if (res?.response.code !== 0) {
setError(res?.rawLogs)
} else {
setResponse(res)
}
} catch (error) { } catch (error) {
const e = error as { message: string } const e = error as { message: string }
setError(e.message as string) setError(e.message as string)
@ -248,6 +252,7 @@ export const RedbankAction = React.memo(
) : ( ) : (
<Action <Action
actionButtonSpec={produceActionButtonSpec()} actionButtonSpec={produceActionButtonSpec()}
feeError={!fee ? (feeError as string) : undefined}
activeView={activeView} activeView={activeView}
amount={Number(amount)} amount={Number(amount)}
borrowAssets={removeZeroBalanceValues(borrowAssets, 'borrowBalance')} borrowAssets={removeZeroBalanceValues(borrowAssets, 'borrowBalance')}

View File

@ -15,15 +15,6 @@ export const ASSETS: { [denom: string]: Asset } = {
hasOraclePrice: true, hasOraclePrice: true,
logo: osmo, logo: osmo,
}, },
atom: {
symbol: 'ATOM',
name: 'Atom',
denom: 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2',
color: colors.atom,
logo: atom,
decimals: 6,
hasOraclePrice: true,
},
axlusdc: { axlusdc: {
symbol: 'axlUSDC', symbol: 'axlUSDC',
name: 'Axelar USDC', name: 'Axelar USDC',
@ -34,6 +25,15 @@ export const ASSETS: { [denom: string]: Asset } = {
hasOraclePrice: true, hasOraclePrice: true,
poolId: 678, poolId: 678,
}, },
atom: {
symbol: 'ATOM',
name: 'Atom',
denom: 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2',
color: colors.atom,
logo: atom,
decimals: 6,
hasOraclePrice: true,
},
} }
const OTHER_ASSETS: { [denom: string]: OtherAsset } = { const OTHER_ASSETS: { [denom: string]: OtherAsset } = {
@ -82,9 +82,9 @@ export const NETWORK_CONFIG: NetworkConfig = {
councilUrl: 'https://council.marsprotocol.io', councilUrl: 'https://council.marsprotocol.io',
wallets: [ wallets: [
WalletID.Keplr, WalletID.Keplr,
WalletID.StationWallet,
WalletID.Leap, WalletID.Leap,
WalletID.Cosmostation, WalletID.Cosmostation,
WalletID.Falcon,
WalletID.KeplrMobile, WalletID.KeplrMobile,
], ],
} }

View File

@ -42,16 +42,18 @@ export const useEstimateFee = (props: Props) => {
} }
const result = await client.simulate(simulateOptions) const result = await client.simulate(simulateOptions)
return result.success
? { if (result.success) {
amount: result.fee ? result.fee.amount : [], return {
gas: new BigNumber(result.fee ? result.fee.gas : 0) amount: result.fee ? result.fee.amount : [],
.multipliedBy(gasAdjustment) gas: new BigNumber(result.fee ? result.fee.gas : 0)
.toFixed(0), .multipliedBy(gasAdjustment)
} .toFixed(0),
: null }
} catch { }
return null throw result.error
} catch (e) {
throw e
} }
}, },
{ {

1
src/images/farm.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

1
src/images/redbank.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -18,7 +18,7 @@ export default function Document() {
<meta content='@mars_protocol' name='twitter:site' /> <meta content='@mars_protocol' name='twitter:site' />
<meta content='@mars_protocol' name='twitter:creator' /> <meta content='@mars_protocol' name='twitter:creator' />
<meta content='https://osmosis.marsprotocol.io' property='og:url' /> <meta content='https://osmosis.marsprotocol.io' property='og:url' />
<meta content='Mars Protocol Application - Powered by Osmosis' property='og:title' /> <meta content='Mars Protocol - Osmosis Outpost' property='og:title' />
<meta <meta
content="Lend, borrow and earn on the galaxy's most powerful credit protocol or enter the Fields of Mars for advanced DeFi strategies." content="Lend, borrow and earn on the galaxy's most powerful credit protocol or enter the Fields of Mars for advanced DeFi strategies."
property='og:description' property='og:description'

View File

@ -1,5 +1,6 @@
import { Button, Card } from 'components/common' import { Button, Card } from 'components/common'
import { ActionsTooltip, Breakdown, PositionInput } from 'components/fields' import { ActionsTooltip, Breakdown, PositionInput } from 'components/fields'
import { VAULT_DEPOSIT_BUFFER } from 'constants/appConstants'
import { DEFAULT_POSITION } from 'constants/defaults' import { DEFAULT_POSITION } from 'constants/defaults'
import { useAvailableVault } from 'hooks/data' import { useAvailableVault } from 'hooks/data'
import cloneDeep from 'lodash.clonedeep' import cloneDeep from 'lodash.clonedeep'
@ -27,11 +28,24 @@ const Create = () => {
return return
} }
const vaultCap = (availableVault.vaultCap?.max || 0) * VAULT_DEPOSIT_BUFFER
const isVaultCapReached = availableVault.vaultCap
? availableVault.vaultCap.used + position.values.total > vaultCap
: false
const isDisabled = position.values.total === 0 || isVaultCapReached
return ( return (
<Card <Card
title={availableVault.name} title={availableVault.name}
onClick={() => router.replace('/farm')} onClick={() => router.replace('/farm')}
tooltip={'placeholder'} tooltip={
<>
{t('fields.tooltips.editPosition')}
<br />
<br />
{t('fields.tooltips.apy.available')}
</>
}
> >
<PositionInput vault={availableVault} position={position} setPosition={setPosition} /> <PositionInput vault={availableVault} position={position} setPosition={setPosition} />
<Breakdown vault={availableVault} newPosition={position} isSetUp /> <Breakdown vault={availableVault} newPosition={position} isSetUp />
@ -41,11 +55,11 @@ const Create = () => {
<Button <Button
text={t('common.setup')} text={t('common.setup')}
onClick={() => setPositionInStore(position)} onClick={() => setPositionInStore(position)}
disabled={position.values.total === 0} disabled={isDisabled}
className={styles.button} className={styles.button}
/> />
</Link> </Link>
{position.values.total !== 0 && ( {!isDisabled && (
<ActionsTooltip <ActionsTooltip
type='edit' type='edit'
vault={availableVault} vault={availableVault}

View File

@ -80,7 +80,7 @@ const SetupPosition = (props: Props) => {
<Button <Button
prefix={accountId && <SVG.Check />} prefix={accountId && <SVG.Check />}
text={t('fields.setup.step1.button')} text={t('fields.setup.step1.button')}
disabled={!!accountId || isLoadingCreate || !createFee} disabled={!!accountId || !createFee}
showProgressIndicator={isLoadingCreate} showProgressIndicator={isLoadingCreate}
onClick={handleCreateCreditAccountClick} onClick={handleCreateCreditAccountClick}
className={styles.mintBtn} className={styles.mintBtn}

View File

@ -212,7 +212,14 @@ const EditVault = (props: Props) => {
<Card <Card
title={props.activeVault.name} title={props.activeVault.name}
onClick={() => router.back()} onClick={() => router.back()}
tooltip={t('fields.tooltips.editPosition')} tooltip={
<>
{t('fields.tooltips.editPosition')}
<br />
<br />
{t('fields.tooltips.apy.available')}
</>
}
actionButtons={actionButtons} actionButtons={actionButtons}
> >
{isRepay ? ( {isRepay ? (

View File

@ -67,7 +67,14 @@ const RepayVault = (props: Props) => {
<Card <Card
title={props.activeVault.name} title={props.activeVault.name}
onClick={() => router.back()} onClick={() => router.back()}
tooltip={t('fields.tooltips.editPosition')} tooltip={
<>
{t('fields.tooltips.editPosition')}
<br />
<br />
{t('fields.tooltips.apy.available')}
</>
}
> >
<RepayInput <RepayInput
vault={props.activeVault} vault={props.activeVault}

View File

@ -10,7 +10,7 @@ import {
import { useEffect, useMemo } from 'react' import { useEffect, useMemo } from 'react'
import { Trans, useTranslation } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
import useStore from 'store' import useStore from 'store'
import { NotificationType } from 'types/enums' import { NotificationType, State } from 'types/enums'
import { DocURL } from 'types/enums/docURL' import { DocURL } from 'types/enums/docURL'
import styles from './Redbank.module.scss' import styles from './Redbank.module.scss'
@ -28,8 +28,10 @@ const RedBank = () => {
// STORE STATE // STORE STATE
// ------------------ // ------------------
const marketInfo = useStore((s) => s.marketInfo) const marketInfo = useStore((s) => s.marketInfo)
const userCollateral = useStore((s) => s.userCollateral) const showTutorial = useStore((s) => s.showRedBankTutorial)
const tutorialStep = useStore((s) => s.tutorialSteps['redbank']) const tutorialStep = useStore((s) => s.tutorialSteps['redbank'])
const userBalancesState = useStore((s) => s.userBalancesState)
const userCollateral = useStore((s) => s.userCollateral)
const maxBorrowLimit = useMemo(() => { const maxBorrowLimit = useMemo(() => {
if (!userCollateral || !redBankAssets) return 0 if (!userCollateral || !redBankAssets) return 0
@ -69,8 +71,6 @@ const RedBank = () => {
const showLiquidationWarning = borrowBalance >= maxBorrowLimit && borrowBalance > 0 const showLiquidationWarning = borrowBalance >= maxBorrowLimit && borrowBalance > 0
const showTutorial = !localStorage.getItem(RED_BANK_TUTORIAL_KEY)
const depositCard = ( const depositCard = (
<Card <Card
hideHeaderBorder hideHeaderBorder
@ -92,10 +92,19 @@ const RedBank = () => {
) )
useEffect(() => { useEffect(() => {
if (userBalancesState !== State.READY) return
if (localStorage.getItem(RED_BANK_TUTORIAL_KEY)) {
return useStore.setState({ showRedBankTutorial: false })
}
if (Number(balanceSum(redBankAssets, 'depositBalanceBaseCurrency')) > 0) { if (Number(balanceSum(redBankAssets, 'depositBalanceBaseCurrency')) > 0) {
localStorage.setItem(RED_BANK_TUTORIAL_KEY, 'true') localStorage.setItem(RED_BANK_TUTORIAL_KEY, 'true')
return useStore.setState({ showRedBankTutorial: false })
} }
}, [redBankAssets])
useStore.setState({ showRedBankTutorial: true })
}, [redBankAssets, userBalancesState])
return ( return (
<div className={styles.markets}> <div className={styles.markets}>

View File

@ -12,6 +12,7 @@ export interface RedBankSlice {
marketInfo: Market[] marketInfo: Market[]
redBankAssets: RedBankAsset[] redBankAssets: RedBankAsset[]
redBankState: State redBankState: State
showRedBankTutorial: boolean
userBalancesState: State userBalancesState: State
userCollateral?: UserCollateral[] userCollateral?: UserCollateral[]
userDebts?: Coin[] userDebts?: Coin[]

View File

@ -12,6 +12,7 @@ import { DepositAndDebtData } from 'hooks/queries/useDepositAndDebt'
import { SafetyFundBalanceData } from 'hooks/queries/useSafetyFundBalance' import { SafetyFundBalanceData } from 'hooks/queries/useSafetyFundBalance'
import { UserBalanceData } from 'hooks/queries/useUserBalance' import { UserBalanceData } from 'hooks/queries/useUserBalance'
import isEqual from 'lodash.isequal' import isEqual from 'lodash.isequal'
import { isMobile } from 'react-device-detect'
import { CommonSlice } from 'store/interfaces/common.interface' import { CommonSlice } from 'store/interfaces/common.interface'
import { OraclesSlice } from 'store/interfaces/oracles.interface' import { OraclesSlice } from 'store/interfaces/oracles.interface'
import { Network } from 'types/enums/network' import { Network } from 'types/enums/network'
@ -97,11 +98,14 @@ const commonSlice = (
gasLimit: options.fee.gas, gasLimit: options.fee.gas,
memo: undefined, memo: undefined,
wallet: client.recentWallet, wallet: client.recentWallet,
mobile: isMobile,
} }
try { try {
return client.broadcast(broadcastOptions) return client.broadcast(broadcastOptions)
} catch (e) {} } catch (e) {
console.error('transaction', e)
}
}, },
loadNetworkConfig: async () => { loadNetworkConfig: async () => {
try { try {

View File

@ -19,6 +19,7 @@ const redBankSlice = (set: NamedSet<Store>, get: GetState<Store>): RedBankSlice
marketInfo: [], marketInfo: [],
redBankAssets: [], redBankAssets: [],
redBankState: State.INITIALISING, redBankState: State.INITIALISING,
showRedBankTutorial: false,
userBalancesState: State.INITIALISING, userBalancesState: State.INITIALISING,
// ------------------ // ------------------
// GENERAL FUNCTIONS // GENERAL FUNCTIONS

View File

@ -364,7 +364,7 @@
"@cosmjs/math" "^0.29.5" "@cosmjs/math" "^0.29.5"
"@cosmjs/utils" "^0.29.5" "@cosmjs/utils" "^0.29.5"
"@cosmjs/cosmwasm-stargate@^0.29.2", "@cosmjs/cosmwasm-stargate@^0.29.5": "@cosmjs/cosmwasm-stargate@^0.29.5":
version "0.29.5" version "0.29.5"
resolved "https://registry.yarnpkg.com/@cosmjs/cosmwasm-stargate/-/cosmwasm-stargate-0.29.5.tgz#3f257da682658833e0f4eb9e8ff758e4d927663a" resolved "https://registry.yarnpkg.com/@cosmjs/cosmwasm-stargate/-/cosmwasm-stargate-0.29.5.tgz#3f257da682658833e0f4eb9e8ff758e4d927663a"
integrity sha512-TNdSvm2tEE3XMCuxHxquzls56t40hC8qnLeYJWHsY2ECZmRK3KrnpRReEr7N7bLtODToK7X/riYrV0JaYxjrYA== integrity sha512-TNdSvm2tEE3XMCuxHxquzls56t40hC8qnLeYJWHsY2ECZmRK3KrnpRReEr7N7bLtODToK7X/riYrV0JaYxjrYA==
@ -419,7 +419,7 @@
bech32 "^1.1.4" bech32 "^1.1.4"
readonly-date "^1.0.0" readonly-date "^1.0.0"
"@cosmjs/encoding@^0.29.2", "@cosmjs/encoding@^0.29.5": "@cosmjs/encoding@^0.29.5":
version "0.29.5" version "0.29.5"
resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.29.5.tgz#009a4b1c596cdfd326f30ccfa79f5e56daa264f2" resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.29.5.tgz#009a4b1c596cdfd326f30ccfa79f5e56daa264f2"
integrity sha512-G4rGl/Jg4dMCw5u6PEZHZcoHnUBlukZODHbm/wcL4Uu91fkn5jVo5cXXZcvs4VCkArVGrEj/52eUgTZCmOBGWQ== integrity sha512-G4rGl/Jg4dMCw5u6PEZHZcoHnUBlukZODHbm/wcL4Uu91fkn5jVo5cXXZcvs4VCkArVGrEj/52eUgTZCmOBGWQ==
@ -486,7 +486,7 @@
ws "^7" ws "^7"
xstream "^11.14.0" xstream "^11.14.0"
"@cosmjs/stargate@^0.29.2", "@cosmjs/stargate@^0.29.5": "@cosmjs/stargate@^0.29.5":
version "0.29.5" version "0.29.5"
resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.29.5.tgz#d597af1c85a3c2af7b5bdbec34d5d40692cc09e4" resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.29.5.tgz#d597af1c85a3c2af7b5bdbec34d5d40692cc09e4"
integrity sha512-hjEv8UUlJruLrYGJcUZXM/CziaINOKwfVm2BoSdUnNTMxGvY/jC1ABHKeZUYt9oXHxEJ1n9+pDqzbKc8pT0nBw== integrity sha512-hjEv8UUlJruLrYGJcUZXM/CziaINOKwfVm2BoSdUnNTMxGvY/jC1ABHKeZUYt9oXHxEJ1n9+pDqzbKc8pT0nBw==
@ -537,21 +537,21 @@
resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.29.5.tgz#3fed1b3528ae8c5f1eb5d29b68755bebfd3294ee" resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.29.5.tgz#3fed1b3528ae8c5f1eb5d29b68755bebfd3294ee"
integrity sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ== integrity sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ==
"@delphi-labs/shuttle@^2.3.2": "@delphi-labs/shuttle@^2.3.9":
version "2.3.2" version "2.3.10"
resolved "https://registry.yarnpkg.com/@delphi-labs/shuttle/-/shuttle-2.3.2.tgz#d05890bca91b3b18fc451bde3cb03d83072c1c42" resolved "https://registry.yarnpkg.com/@delphi-labs/shuttle/-/shuttle-2.3.10.tgz#bd4f0fc48bb0ff13fd3a7e004246e469c5315d6e"
integrity sha512-xgebNiS761IPiqQ5q3no1CBNGD/Q5vznV7ydUqismzR/Vw4GIEAqJv6QFIe7HbjHPXkIPeL/enimEGwAnNB1Ag== integrity sha512-fS7vN7nFkeynbf7p01E8OTF3L+pKPnW2yuTWQTzHLzCSpzSaLkgIPnXxg4/C5/abr4FZ5ZytxpS9TKyj7hP4wA==
dependencies: dependencies:
"@cosmjs/amino" "^0.29.5" "@cosmjs/amino" "^0.29.5"
"@cosmjs/cosmwasm-stargate" "^0.29.2" "@cosmjs/cosmwasm-stargate" "^0.29.5"
"@cosmjs/encoding" "^0.29.2" "@cosmjs/encoding" "^0.29.5"
"@cosmjs/launchpad" "^0.27.1" "@cosmjs/launchpad" "^0.27.1"
"@cosmjs/proto-signing" "^0.29.5" "@cosmjs/proto-signing" "^0.29.5"
"@cosmjs/stargate" "^0.29.2" "@cosmjs/stargate" "^0.29.5"
"@injectivelabs/sdk-ts" "^1.0.360" "@injectivelabs/sdk-ts" "^1.0.360"
"@injectivelabs/ts-types" "^1.0.28" "@injectivelabs/ts-types" "^1.0.28"
"@injectivelabs/utils" "^1.0.60" "@injectivelabs/utils" "^1.0.60"
"@keplr-wallet/cosmos" "^0.11.34" "@keplr-wallet/cosmos" "^0.11.38"
"@keplr-wallet/proto-types" "^0.11.38" "@keplr-wallet/proto-types" "^0.11.38"
"@metamask/eth-sig-util" "^5.0.2" "@metamask/eth-sig-util" "^5.0.2"
"@walletconnect/client" "^1.8.0" "@walletconnect/client" "^1.8.0"
@ -560,9 +560,12 @@
"@walletconnect/utils" "^1.8.0" "@walletconnect/utils" "^1.8.0"
cosmjs-types "^0.6.1" cosmjs-types "^0.6.1"
ethereumjs-util "^7.1.5" ethereumjs-util "^7.1.5"
isomorphic-ws "^5.0.0"
long "^5.2.1" long "^5.2.1"
secp256k1 "^5.0.0" secp256k1 "^5.0.0"
tslib "^2.4.0" tslib "^2.4.0"
use-local-storage-state "^18.1.2"
ws "^8.12.0"
zustand "^4.3.1" zustand "^4.3.1"
"@discoveryjs/json-ext@^0.5.0": "@discoveryjs/json-ext@^0.5.0":
@ -1434,7 +1437,7 @@
buffer "^6.0.3" buffer "^6.0.3"
delay "^4.4.0" delay "^4.4.0"
"@keplr-wallet/cosmos@^0.11.34", "@keplr-wallet/cosmos@^0.11.38": "@keplr-wallet/cosmos@^0.11.38":
version "0.11.38" version "0.11.38"
resolved "https://registry.yarnpkg.com/@keplr-wallet/cosmos/-/cosmos-0.11.38.tgz#5657e3fed10c9a9f75d29f01a137913403196348" resolved "https://registry.yarnpkg.com/@keplr-wallet/cosmos/-/cosmos-0.11.38.tgz#5657e3fed10c9a9f75d29f01a137913403196348"
integrity sha512-0N7pf77cSPp/tNzsCp9dhbmEStXZJv+7KZv3FICRpgSp2w6vKwMPwJ5GIt0T0X4RuiQfgkabnDX77DkBSHo9ng== integrity sha512-0N7pf77cSPp/tNzsCp9dhbmEStXZJv+7KZv3FICRpgSp2w6vKwMPwJ5GIt0T0X4RuiQfgkabnDX77DkBSHo9ng==
@ -1495,16 +1498,18 @@
resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f" resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f"
integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw== integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==
"@marsprotocol/wallet-connector@^1.3.0": "@marsprotocol/wallet-connector@^1.4.2":
version "1.3.0" version "1.4.2"
resolved "https://registry.yarnpkg.com/@marsprotocol/wallet-connector/-/wallet-connector-1.3.0.tgz#1d7a47c5a2ee94e23f7b3703638a7526b35fd8ba" resolved "https://registry.yarnpkg.com/@marsprotocol/wallet-connector/-/wallet-connector-1.4.2.tgz#9dfeaf82242e821d2beef276c23e7f60313ae079"
integrity sha512-O9zyUtWNrggRE1LeilofR8BunhwP6lCY5g5+WU1BDI/V2AqzV9tdNdjYg5CGEO2Z8iXb21Y+o4RoiZ7mknezzQ== integrity sha512-rHVCbKHvAXHzmU5tZo9mV81Y4WclY6oDtO35nQqlh4X+DtIF/8j71hJRXCpGF+IuN1xwp3I8hcKAIqb08KOa5w==
dependencies: dependencies:
"@cosmjs/cosmwasm-stargate" "^0.29.5" "@cosmjs/cosmwasm-stargate" "^0.29.5"
"@delphi-labs/shuttle" "^2.3.2" "@cosmjs/encoding" "^0.29.5"
"@cosmjs/stargate" "^0.29.5"
"@delphi-labs/shuttle" "^2.3.9"
"@keplr-wallet/cosmos" "^0.11.38" "@keplr-wallet/cosmos" "^0.11.38"
axios "^1.3.2" axios "^1.3.2"
react-device-detect "^2.2.2" react-device-detect "^2.2.3"
react-modal "^3.16.1" react-modal "^3.16.1"
react-qr-code "^2.0.11" react-qr-code "^2.0.11"
webpack "^5.75.0" webpack "^5.75.0"
@ -2218,7 +2223,7 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
"@types/node@*", "@types/node@>=13.7.0", "@types/node@^18.11.18": "@types/node@*", "@types/node@>=13.7.0":
version "18.11.18" version "18.11.18"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f"
integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==
@ -2233,6 +2238,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a" resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a"
integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ== integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==
"@types/node@^18.13.0":
version "18.13.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.13.0.tgz#0400d1e6ce87e9d3032c19eb6c58205b0d3f7850"
integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==
"@types/numeral@^2.0.2": "@types/numeral@^2.0.2":
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/@types/numeral/-/numeral-2.0.2.tgz#8ea2c4f4e64c0cc948ad7da375f6f827778a7912" resolved "https://registry.yarnpkg.com/@types/numeral/-/numeral-2.0.2.tgz#8ea2c4f4e64c0cc948ad7da375f6f827778a7912"
@ -5177,6 +5187,11 @@ isomorphic-ws@^4.0.1:
resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc"
integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==
isomorphic-ws@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf"
integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==
istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0:
version "3.2.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3"
@ -6694,6 +6709,13 @@ react-device-detect@^2.2.2:
dependencies: dependencies:
ua-parser-js "^1.0.2" ua-parser-js "^1.0.2"
react-device-detect@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/react-device-detect/-/react-device-detect-2.2.3.tgz#97a7ae767cdd004e7c3578260f48cf70c036e7ca"
integrity sha512-buYY3qrCnQVlIFHrC5UcUoAj7iANs/+srdkwsnNjI7anr3Tt7UY6MqNxtMLlr0tMBied0O49UZVK8XKs3ZIiPw==
dependencies:
ua-parser-js "^1.0.33"
react-dom@^18.2.0: react-dom@^18.2.0:
version "18.2.0" version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
@ -7627,7 +7649,7 @@ typescript@^4.9.5:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a"
integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==
ua-parser-js@^1.0.2: ua-parser-js@^1.0.2, ua-parser-js@^1.0.33:
version "1.0.33" version "1.0.33"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.33.tgz#f21f01233e90e7ed0f059ceab46eb190ff17f8f4" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.33.tgz#f21f01233e90e7ed0f059ceab46eb190ff17f8f4"
integrity sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ== integrity sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ==
@ -7675,6 +7697,11 @@ url-parse@^1.5.3:
querystringify "^2.1.1" querystringify "^2.1.1"
requires-port "^1.0.0" requires-port "^1.0.0"
use-local-storage-state@^18.1.2:
version "18.1.2"
resolved "https://registry.yarnpkg.com/use-local-storage-state/-/use-local-storage-state-18.1.2.tgz#f131c0aa3803742ca261c547cdfd9d61e848581d"
integrity sha512-V+kYQNC5R0N/JDpsg6b4ED5UaItKJcSvbne68DwJDZWHxGMQBiF41ATktFIOyet3PIq30d2qtzVp/2aB6hQ8Bg==
use-sync-external-store@1.2.0, use-sync-external-store@^1.2.0: use-sync-external-store@1.2.0, use-sync-external-store@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
@ -7944,7 +7971,7 @@ ws@^7:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
ws@^8.11.0: ws@^8.11.0, ws@^8.12.0:
version "8.12.0" version "8.12.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8" resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8"
integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig== integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==