release v1.4.4

This commit is contained in:
Linkie Link 2023-04-21 08:45:21 +08:00
parent b46a6b9461
commit 7fc6bea7f6
No known key found for this signature in database
GPG Key ID: 5318B0F2564D38EA
2 changed files with 543 additions and 242 deletions

View File

@ -1,280 +1,581 @@
import { TxBroadcastResult } from '@marsprotocol/wallet-connector' import 'chart.js/auto'
import { useQueryClient } from '@tanstack/react-query'
import { Action, Notification, TxResponse } from 'components/common' import classNames from 'classnames'
import { findByDenom } from 'functions'
import { import {
getRedbankBorrowMsgOptions, BorrowCapacity,
getRedbankDepositMsgOptions, Button,
getRedbankRepayMsgOptions, Card,
getRedbankWithdrawMsgOptions, ConnectButton,
} from 'functions/messages' DisplayCurrency,
import { useEstimateFee } from 'hooks/queries' ErrorMessage,
import { ltvWeightedDepositValue, maintainanceMarginWeightedDepositValue } from 'libs/assetInfo' InputSection,
import { lookup, lookupDecimals } from 'libs/parse' } from 'components/common'
import isEqual from 'lodash.isequal' import { findByDenom } from 'functions'
import { useRouter } from 'next/router' import { maxBorrowableAmount } from 'functions/redbank/maxBorrowableAmount'
import React, { useMemo, useState } from 'react' import { produceBarChartConfig } from 'functions/redbank/produceBarChartConfig'
import { produceUpdatedAssetData } from 'functions/redbank/produceUpdatedAssetData'
import { useUserBalance } from 'hooks/queries'
import {
balanceSum,
ltvWeightedDepositValue,
maintainanceMarginWeightedDepositValue,
producePercentData,
} from 'libs/assetInfo'
import { formatValue, lookup, lookupSymbol } from 'libs/parse'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Bar } from 'react-chartjs-2'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import useStore from 'store' import useStore from 'store'
import { NotificationType, ViewType } from 'types/enums' import colors from 'styles/_assets.module.scss'
import { QUERY_KEYS } from 'types/enums/queryKeys' import { ViewType } from 'types/enums'
import styles from './RedbankAction.module.scss' import styles from './Action.module.scss'
interface Props { interface Props {
amount: number
redBankAssets: RedBankAsset[]
depositAssets: RedBankAsset[]
borrowAssets: RedBankAsset[]
setIsMax: (isMax: boolean) => void
setCapHit: (capHit: boolean) => void
setAmountCallback: (amount: number) => void
mmScaledDepositAmount: number
ltvScaledDepositAmount: number
totalBorrowBaseCurrencyAmount: number
actionButtonSpec: ModalActionButton
submitted: boolean
feeError?: string
activeView: ViewType activeView: ViewType
id: string denom: string
decimals: number
handleClose: () => void
} }
export const RedbankAction = React.memo( export const Action = ({
({ activeView, id }: Props) => { amount,
// ------------------ redBankAssets,
// EXTERNAL HOOKS depositAssets,
// ------------------ borrowAssets,
const { t } = useTranslation() setIsMax,
const router = useRouter() setCapHit,
const queryClient = useQueryClient() setAmountCallback,
mmScaledDepositAmount,
ltvScaledDepositAmount,
totalBorrowBaseCurrencyAmount,
actionButtonSpec,
submitted,
feeError,
activeView,
denom,
decimals,
handleClose,
}: Props) => {
const { t } = useTranslation()
// ------------------
// STORE STATE
// ------------------
const baseCurrency = useStore((s) => s.baseCurrency)
const marketInfo = useStore((s) => s.marketInfo)
const marketAssetLiquidity = useStore((s) => s.marketAssetLiquidity)
const userCollateral = useStore((s) => s.userCollateral)
const userWalletAddress = useStore((s) => s.userWalletAddress)
const whitelistedAssets = useStore((s) => s.whitelistedAssets)
const convertToBaseCurrency = useStore((s) => s.convertToBaseCurrency)
const findUserDebt = useStore((s) => s.findUserDebt)
const enableAnimations = useStore((s) => s.enableAnimations)
const baseCurrencyDecimals = useStore((s) => s.baseCurrency.decimals)
// ------------------ // ------------------
// STORE STATE // LOCAL STATE
// ------------------ // ------------------
const client = useStore((s) => s.client) const [currentAssetPrice, setCurrentAssetPrice] = useState(0)
const marketInfo = useStore((s) => s.marketInfo) const [portfolioVisible, setPortfolioVisible] = useState(false)
const networkConfig = useStore((s) => s.networkConfig) const [chartsDataLoaded, setChartsDataLoaded] = useState(false)
const otherAssets = useStore((s) => s.otherAssets)
const redBankAssets = useStore((s) => s.redBankAssets)
const userBalances = useStore((s) => s.userBalances)
const userCollateral = useStore((s) => s.userCollateral)
const whitelistedAssets = useStore((s) => s.whitelistedAssets)
const executeMsg = useStore((s) => s.executeMsg)
// ------------------ const { data: userBalances } = useUserBalance()
// LOCAL STATE
// ------------------
const [amount, setAmount] = useState(0)
const [submitted, setSubmitted] = useState(false)
const [response, setResponse] = useState<TxBroadcastResult>()
const [error, setError] = useState<string>()
const [isMax, setIsMax] = useState<boolean>(false)
const [capHit, setCapHit] = useState<boolean>(false)
// ------------------ /// ------------------
// VARIABLES // VARIABLES
// ------------------ // ------------------
const assets = [...whitelistedAssets, ...otherAssets] const walletBalance = Number(findByDenom(userBalances || [], denom)?.amount.toString()) || 0
const denom = assets.find((asset) => asset.id === id)?.denom || '' const assetBorrowBalance = findUserDebt(denom)
const decimals = lookupDecimals(denom, whitelistedAssets || []) || 6 const availableBalanceBaseCurrency = Math.max(
const symbol = assets.find((asset) => asset.id === id)?.symbol || '' ltvScaledDepositAmount - totalBorrowBaseCurrencyAmount,
const walletBallance = Number(findByDenom(userBalances, denom)?.amount.toString()) 0,
)
const currentAsset = redBankAssets.find((asset) => asset.denom === denom)
// Read only states // -------------------------
const borrowAssetName = redBankAssets.find((asset) => asset.denom === denom) // calculate
const redBankContractAddress = networkConfig?.contracts.redBank // -------------------------
const totalScaledDepositbaseCurrencyBalance = useMemo(() => { const relevantAssetData = useMemo(
if (!userCollateral) return 0 () =>
return ltvWeightedDepositValue( activeView === ViewType.Deposit || activeView === ViewType.Withdraw
? depositAssets
: borrowAssets,
[depositAssets, borrowAssets, activeView],
)
const relevantBalanceKey = useMemo(
() =>
activeView === ViewType.Deposit || activeView === ViewType.Withdraw
? 'depositBalanceBaseCurrency'
: 'borrowBalanceBaseCurrency',
[activeView],
)
const amountAdjustedAssetData = useMemo(
() =>
produceUpdatedAssetData(
redBankAssets, redBankAssets,
marketInfo, [...relevantAssetData],
userCollateral, denom,
'depositBalanceBaseCurrency', amount * currentAssetPrice, // amount in display currency
) activeView,
}, [redBankAssets, marketInfo, userCollateral]) relevantBalanceKey,
baseCurrencyDecimals,
),
[
activeView,
amount,
relevantAssetData,
currentAssetPrice,
denom,
redBankAssets,
relevantBalanceKey,
baseCurrencyDecimals,
],
)
const totalMMScaledDepositbaseCurrencyBalance = useMemo(() => { const percentData = producePercentData(
if (!userCollateral) return 0 produceUpdatedAssetData(
return maintainanceMarginWeightedDepositValue( redBankAssets,
redBankAssets, [...relevantAssetData],
marketInfo, denom,
userCollateral, 0.0,
'depositBalanceBaseCurrency', activeView,
) relevantBalanceKey,
}, [redBankAssets, marketInfo, userCollateral]) baseCurrencyDecimals,
),
relevantBalanceKey,
)
const updatedData = producePercentData(amountAdjustedAssetData, relevantBalanceKey)
const totalBorrowBaseCurrencyAmount = redBankAssets.reduce( // ---------------------
(total, asset) => total + (Number(asset.borrowBalanceBaseCurrency) || 0), // logic
0, // ---------------------
) const newTotalMMScaledSupplyBalance = useMemo(
() =>
// For deposits and withdraws, we need to recalculate the loan limit
{
if (!userCollateral) return 0
// On first deposit of asset, SC does not hold state of collateral.enabled
// Therefore, we need to emulate this state
const isFirstDeposit =
!relevantAssetData.find((asset) => asset.denom === denom) &&
activeView === ViewType.Deposit
// -------------------------------- return activeView === ViewType.Deposit || activeView === ViewType.Withdraw
// Transaction objects ? maintainanceMarginWeightedDepositValue(
// -------------------------------- amountAdjustedAssetData,
marketInfo,
userCollateral,
relevantBalanceKey,
isFirstDeposit ? denom : '',
)
: mmScaledDepositAmount
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[activeView, amountAdjustedAssetData, mmScaledDepositAmount],
)
const txMsgOptions = useMemo(() => { const newTotalLTVScaledSupplyBalance = useMemo(
if (!redBankContractAddress || amount <= 0 || !denom) return () =>
// For deposits and withdraws, we need to recalculate the loan limit
{
if (!userCollateral) return 0
// On first deposit of asset, SC does not hold state of collateral.enabled
// Therefore, we need to emulate this state
const isFirstDeposit =
!relevantAssetData.find((asset) => asset.denom === denom) &&
activeView === ViewType.Deposit
switch (activeView) { return activeView === ViewType.Deposit || activeView === ViewType.Withdraw
case ViewType.Deposit: ? ltvWeightedDepositValue(
return getRedbankDepositMsgOptions(amount, denom) amountAdjustedAssetData,
case ViewType.Withdraw: marketInfo,
return getRedbankWithdrawMsgOptions(amount, denom) userCollateral,
case ViewType.Repay: relevantBalanceKey,
return getRedbankRepayMsgOptions( isFirstDeposit ? denom : '',
amount, )
denom, : ltvScaledDepositAmount
Number(findByDenom(userBalances, denom)?.amount) || 0, },
isMax, // eslint-disable-next-line react-hooks/exhaustive-deps
) [activeView, amountAdjustedAssetData, ltvScaledDepositAmount],
case ViewType.Borrow: )
return getRedbankBorrowMsgOptions(amount, denom)
default:
return undefined
}
}, [activeView, amount, redBankContractAddress, denom, isMax, userBalances])
const { data: fee, error: feeError } = useEstimateFee({ const debtValue =
msg: txMsgOptions?.msg, activeView === ViewType.Borrow || activeView === ViewType.Repay
funds: ? balanceSum(amountAdjustedAssetData, relevantBalanceKey)
activeView === ViewType.Deposit || activeView === ViewType.Repay : totalBorrowBaseCurrencyAmount
? [{ denom, amount: amount > 0 ? amount.toFixed(0) : '1' }]
: undefined,
contract: redBankContractAddress,
})
const produceActionButtonSpec = (): ModalActionButton => { const calculateMaxBorrowableAmount = useMemo((): number => {
return { const assetLiquidity = Number(findByDenom(marketAssetLiquidity, denom)?.amount || 0)
disabled: amount === 0 || capHit,
fetching: (amount > 0 && typeof fee === 'undefined') || submitted, return maxBorrowableAmount(assetLiquidity, availableBalanceBaseCurrency, currentAssetPrice)
text: t(`redbank.${activeView.toLowerCase()}`), }, [denom, availableBalanceBaseCurrency, currentAssetPrice, marketAssetLiquidity])
clickHandler: handleAction,
color: 'primary', const repayMax = useMemo((): number => {
} return Math.min(assetBorrowBalance, walletBalance)
}, [assetBorrowBalance, walletBalance, denom, baseCurrency.denom])
const maxWithdrawableAmount = useMemo((): number => {
const assetLtvRatio = findByDenom(marketInfo, denom)?.max_loan_to_value || 0
const assetLiquidity = Number(findByDenom(marketAssetLiquidity, denom)?.amount || 0)
const asset = depositAssets.find((asset) => asset.denom === denom)
const assetBalanceOrAvailableLiquidity = Math.min(Number(asset?.depositBalance), assetLiquidity)
if (totalBorrowBaseCurrencyAmount === 0) {
return assetBalanceOrAvailableLiquidity
} }
const handleAction = async () => { // If we did not receive a usable asset there is nothing more to do.
if (!redBankContractAddress || !client) { if (!asset || !asset.depositBalance || !asset.denom) return 0
alert('Uh oh, operation failed')
return const withdrawableAmountOfAsset =
availableBalanceBaseCurrency / (currentAssetPrice * assetLtvRatio)
return withdrawableAmountOfAsset < assetBalanceOrAvailableLiquidity
? withdrawableAmountOfAsset
: assetBalanceOrAvailableLiquidity
}, [
denom,
currentAssetPrice,
depositAssets,
availableBalanceBaseCurrency,
totalBorrowBaseCurrencyAmount,
marketInfo,
marketAssetLiquidity,
])
const maxUsableAmount = useMemo(() => {
if (!currentAsset) return 0
return activeView === ViewType.Deposit
? walletBalance
: activeView === ViewType.Withdraw
? maxWithdrawableAmount
: activeView === ViewType.Borrow
? calculateMaxBorrowableAmount
: repayMax
}, [
walletBalance,
maxWithdrawableAmount,
calculateMaxBorrowableAmount,
repayMax,
activeView,
currentAsset,
])
useEffect(() => {
setCurrentAssetPrice(convertToBaseCurrency({ denom: denom || '', amount: '1' }))
}, [denom, convertToBaseCurrency])
useEffect(() => {
if (!chartsDataLoaded && percentData[0] != 0) {
setChartsDataLoaded(true)
}
}, [percentData, chartsDataLoaded])
const chartRefBefore = useRef(null)
const chartRefAfter = useRef(null)
// -----------
// callbacks
// -----------
const handleInputAmount = useCallback(
(inputAmount: number) => {
if (inputAmount >= maxUsableAmount * 0.99) {
setIsMax(true)
} }
setSubmitted(true) setAmountCallback(Number(formatValue(inputAmount, 0, 0, false, false, false, false, false)))
},
[maxUsableAmount, setIsMax, setAmountCallback],
)
if (!fee || !txMsgOptions) { if (!currentAsset) return <></>
return
}
try { const amountUntilDepositCap = currentAsset.depositCap - Number(currentAsset.depositLiquidity)
const res = await executeMsg({
msg: txMsgOptions.msg, const onValueEntered = (microValue: number) => {
// @ts-ignore if (microValue >= maxUsableAmount) microValue = maxUsableAmount
funds: txMsgOptions.funds || [], setAmountCallback(Number(formatValue(microValue, 0, 0, false, false, false, false, false)))
contract: redBankContractAddress, setCapHit(amount > amountUntilDepositCap && activeView === ViewType.Deposit)
fee: fee, }
const produceTabActionButton = () => {
return (
<>
<Button
color='primary'
className={styles.submitButton}
disabled={actionButtonSpec.disabled}
onClick={() => actionButtonSpec.clickHandler()}
showProgressIndicator={actionButtonSpec.fetching}
text={actionButtonSpec.text}
/>
<ErrorMessage message={feeError} alignment='center' />
</>
)
}
const onEnterAction = () => {
if (!actionButtonSpec.disabled) actionButtonSpec.clickHandler()
}
const produceAvailableText = () => {
switch (activeView) {
case ViewType.Borrow:
return t('common.maxLimitAmountSymbol', {
amount: formatValue(
lookup(maxUsableAmount, denom, decimals),
0,
decimals,
true,
'',
'',
false,
false,
),
symbol: lookupSymbol(denom, whitelistedAssets || []),
}) })
if (res?.response.code !== 0) { case ViewType.Deposit:
setError(res?.rawLogs) return t('common.inWalletAmountSymbol', {
} else { amount: formatValue(
setResponse(res) lookup(walletBalance, denom, decimals),
} 0,
} catch (error) { decimals,
const e = error as { message: string } true,
setError(e.message as string) '',
} '',
false,
false,
),
symbol: lookupSymbol(denom, whitelistedAssets || []),
})
case ViewType.Withdraw:
// Find amount of asset deposited
const asset: RedBankAsset | undefined = depositAssets.find((asset) => asset.denom === denom)
return t('common.depositedAmountSymbol', {
amount: formatValue(
lookup(Number(asset?.depositBalance), denom, decimals),
0,
decimals,
true,
'',
'',
false,
false,
),
symbol: lookupSymbol(denom, whitelistedAssets || []),
})
case ViewType.Repay:
return t('redbank.borrowedAmountSymbol', {
amount: formatValue(
lookup(findUserDebt(denom), denom, decimals),
0,
decimals,
true,
'',
'',
false,
false,
),
symbol: lookupSymbol(denom, whitelistedAssets || []),
})
} }
const reset = () => { return ''
setAmount(0) }
setSubmitted(false)
setError(undefined)
setIsMax(false)
}
const handleClose = () => { // -------------
reset() // Presentation
// -------------
// path on redbank action will always be /redbank/deposit/<denom> etce const produceBarChartData = (percentData: Array<number>, labels: string[]) => {
router.push(`/${router.pathname.split('/')[1]}`) const barColors: string[] = []
} labels.forEach((label) => {
barColors.push(colors[label.toLowerCase()])
const removeZeroBalanceValues = ( })
assets: RedBankAsset[], return {
key: 'borrowBalance' | 'depositBalance', labels: labels,
) => { datasets: [
const finalisedArray: RedBankAsset[] = [] {
for (let i = 0; i < assets.length; i++) { axis: 'x',
if (Number(assets[i][key] ?? 0) > 0) { barPercentage: 0.8,
finalisedArray.push(assets[i]) maxBarThickness: 50,
} data: percentData,
} fill: true,
backgroundColor: barColors,
return finalisedArray borderWidth: 1,
} animation: {
duration: enableAnimations ? 800 : 0,
const { depositAssets, borrowAssets } = redBankAssets.reduce( },
(
prev: {
depositAssets: RedBankAsset[]
borrowAssets: RedBankAsset[]
}, },
curr, ],
) => { }
if (Number(curr.depositBalance) > 0) { }
prev.depositAssets.push(curr)
}
if (Number(curr.borrowBalance) > 0) {
prev.borrowAssets.push(curr)
}
return prev
},
{ depositAssets: [], borrowAssets: [] },
)
return ( const barChartHeight = 40 * percentData.length + 10
<div className={styles.cardContainer}>
<Notification
content={t('redbank.noFundsForRepay', {
symbol: borrowAssetName?.symbol || '',
})}
showNotification={
walletBallance === 0 && activeView === ViewType.Repay && !response && !error
}
type={NotificationType.Warning}
/>
{response || submitted || error ? ( const actionButton = !userWalletAddress ? (
<TxResponse <ConnectButton color={'secondary'} />
error={error} ) : (
handleClose={handleClose} produceTabActionButton()
onSuccess={() => { )
queryClient.invalidateQueries([QUERY_KEYS.USER_DEPOSIT])
queryClient.invalidateQueries([QUERY_KEYS.REDBANK]) const adjustedLabels = amountAdjustedAssetData.map((asset) =>
queryClient.invalidateQueries([QUERY_KEYS.USER_BALANCE]) lookupSymbol(asset.denom || '', whitelistedAssets || []),
queryClient.invalidateQueries([QUERY_KEYS.USER_DEBT]) )
}}
response={response} const getTooltip = (): string | undefined => {
title={t('common.summaryOfTheTransaction')} switch (activeView) {
actions={[ case ViewType.Borrow:
{ return t('redbank.tooltips.borrow.action')
label: activeView, case ViewType.Deposit:
values: [`${lookup(amount, denom, decimals).toString()} ${symbol}`], return t('redbank.tooltips.deposit.action')
}, case ViewType.Withdraw:
]} return t('redbank.tooltips.withdraw.action')
/> case ViewType.Repay:
) : ( return t('redbank.tooltips.repay.action')
<Action }
actionButtonSpec={produceActionButtonSpec()} }
feeError={!fee ? (feeError as string) : undefined}
activeView={activeView} const collapsableStyles = classNames(styles.collapsable, !portfolioVisible && styles.collapsed)
amount={Number(amount)}
borrowAssets={removeZeroBalanceValues(borrowAssets, 'borrowBalance')} return (
decimals={decimals} <Card onClick={handleClose} title={activeView} tooltip={getTooltip()}>
denom={denom} <InputSection
depositAssets={removeZeroBalanceValues(depositAssets, 'depositBalance')} actionButton={actionButton}
handleClose={handleClose} amount={amount}
ltvScaledDepositAmount={totalScaledDepositbaseCurrencyBalance} availableText={produceAvailableText()}
mmScaledDepositAmount={totalMMScaledDepositbaseCurrencyBalance} checkForMaxValue={activeView === ViewType.Deposit || activeView === ViewType.Repay}
redBankAssets={redBankAssets} asset={currentAsset}
setAmountCallback={setAmount} disabled={
setIsMax={setIsMax} submitted ||
submitted={submitted} (amountUntilDepositCap <= 0 && activeView === ViewType.Deposit) ||
totalBorrowBaseCurrencyAmount={totalBorrowBaseCurrencyAmount} maxUsableAmount < 1
setCapHit={setCapHit} }
/> inputCallback={onValueEntered}
maxUsableAmount={maxUsableAmount}
onEnterHandler={onEnterAction}
setAmountCallback={handleInputAmount}
amountUntilDepositCap={amountUntilDepositCap}
activeView={activeView}
walletBalance={walletBalance}
/>
{/* SITUATION COMPARISON */}
<div className={styles.newSituation}>
<div className={styles.borrowCapacityContainer}>
<div className={styles.borrowCapacity}>
<div className={styles.borrowCapacityTitle}>
<span className={`overline ${styles.title}`}>
{activeView === ViewType.Withdraw || activeView === ViewType.Deposit
? t('common.currentDepositBalance')
: t('common.currentBorrowBalance')}
</span>
<DisplayCurrency
className={styles.value}
coin={{
denom: baseCurrency.denom,
amount: balanceSum(relevantAssetData, relevantBalanceKey).toString(),
}}
prefixClass='sub2'
valueClass='h4'
/>
</div>
<BorrowCapacity
balance={totalBorrowBaseCurrencyAmount}
barHeight={'17px'}
limit={ltvScaledDepositAmount}
max={mmScaledDepositAmount}
showPercentageText
fadeTitle
/>
</div>
<div className={styles.borrowCapacity}>
<div className={styles.borrowCapacityTitle}>
<span className={`overline ${styles.title}`}>
{activeView === ViewType.Withdraw || activeView === ViewType.Deposit
? t('common.newDepositBalance')
: t('common.newBorrowBalance')}
</span>
<DisplayCurrency
className={styles.value}
coin={{
denom: baseCurrency.denom,
amount: balanceSum(amountAdjustedAssetData, relevantBalanceKey).toString(),
}}
prefixClass='sub2'
valueClass='h4'
/>
</div>
<BorrowCapacity
balance={debtValue}
barHeight={'17px'}
limit={newTotalLTVScaledSupplyBalance}
max={newTotalMMScaledSupplyBalance}
showPercentageText
fadeTitle
/>
</div>
</div>
{chartsDataLoaded && (
<div className={collapsableStyles}>
<div className={styles.portfolio}>
<div className={styles.portfolioWrapper}>
<span className={`overline ${styles.title}`}>
{t('redbank.currentComposition')}
</span>
<div className={styles.chartWrapper}>
<Bar
data={produceBarChartData(percentData, adjustedLabels)}
height={barChartHeight}
options={produceBarChartConfig(percentData)}
ref={chartRefBefore}
/>
</div>
</div>
<div className={styles.portfolioWrapper}>
<span className={`overline ${styles.title}`}>{t('redbank.newComposition')}</span>
<div className={styles.chartWrapper}>
<Bar
data={produceBarChartData(updatedData, adjustedLabels)}
height={barChartHeight}
options={produceBarChartConfig(updatedData)}
ref={chartRefAfter}
/>
</div>
</div>
</div>
</div>
)} )}
</div> </div>
)
},
(prev, next) => isEqual(prev, next),
)
RedbankAction.displayName = 'RedbankAction' {chartsDataLoaded && (
<div className={styles.showPortfolio}>
<Button
onClick={() => setPortfolioVisible(!portfolioVisible)}
size='medium'
text={!portfolioVisible ? t('common.showComposition') : t('common.closeComposition')}
variant='transparent'
/>
</div>
)}
</Card>
)
}

View File

@ -17,7 +17,7 @@ i18next
backend: { backend: {
crossDomain: true, crossDomain: true,
loadPath() { loadPath() {
return 'https://raw.githubusercontent.com/mars-protocol/translations/develop/{{lng}}.json' return 'https://raw.githubusercontent.com/mars-protocol/translations/master/{{lng}}.json'
}, },
}, },
react: { react: {