[CM details] support changes (#359)

* [CM details] support changes

* Update src/components/Trade/TradeModule/SwapForm/index.tsx

Co-authored-by: Yusuf Seyrek <yusufseyrek@users.noreply.github.com>

* Update src/components/Trade/TradeModule/SwapForm/index.tsx

Co-authored-by: Yusuf Seyrek <yusufseyrek@users.noreply.github.com>

* [HC] update to bncoin.from

---------

Co-authored-by: Yusuf Seyrek <yusufseyrek@users.noreply.github.com>
This commit is contained in:
Bob van der Helm 2023-08-14 09:55:53 -03:00 committed by GitHub
parent 5358b8c39e
commit f9318dfe6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 144 additions and 46 deletions

3
.gitignore vendored
View File

@ -44,3 +44,6 @@ next-env.d.ts
.env.local.mainnet
.env.production
.env
# IDE
.idea

View File

@ -27,6 +27,7 @@
"classnames": "^2.3.2",
"debounce-promise": "^3.1.2",
"lodash.throttle": "^4.1.1",
"lodash.debounce": "^4.0.8",
"moment": "^2.29.4",
"next": "13.4.9",
"react": "^18.2.0",
@ -49,6 +50,7 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^14.0.0",
"@types/debounce-promise": "^3.1.6",
"@types/lodash.debounce": "^4.0.7",
"@types/lodash.throttle": "^4.1.7",
"@types/node": "^20.4.8",
"@types/react": "18.2.19",

View File

@ -1,9 +1,10 @@
import classNames from 'classnames'
import { useMemo } from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import { Gauge } from 'components/Gauge'
import { Heart } from 'components/Icons'
import { ArrowRight, Heart } from 'components/Icons'
import Text from 'components/Text'
import { ORACLE_DENOM } from 'constants/oracle'
import useCurrentAccount from 'hooks/useCurrentAccount'
@ -11,13 +12,8 @@ import useHealthComputer from 'hooks/useHealthComputer'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { calculateAccountBalanceValue } from 'utils/accounts'
import { formatHealth } from 'utils/formatters'
import { BN } from 'utils/helpers'
interface Props {
account: Account
}
import { calculateAccountBalanceValue, calculateAccountLeverage } from 'utils/accounts'
import { formatLeverage } from 'utils/formatters'
export default function AccountDetailsController() {
const account = useCurrentAccount()
@ -29,37 +25,71 @@ export default function AccountDetailsController() {
return <AccountDetails account={account} />
}
interface Props {
account: Account
}
function AccountDetails(props: Props) {
const updatedAccount = useStore((s) => s.updatedAccount)
const { health } = useHealthComputer(props.account)
const { health: updatedHealth } = useHealthComputer(updatedAccount)
const { data: prices } = usePrices()
const accountBalanceValue = calculateAccountBalanceValue(props.account, prices)
const accountBalanceValue = calculateAccountBalanceValue(
updatedAccount ? updatedAccount : props.account,
prices,
)
const coin = BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, accountBalanceValue)
const healthFactor = BN(100).minus(formatHealth(health)).toNumber()
const leverage = useMemo(
() => calculateAccountLeverage(updatedAccount ? updatedAccount : props.account, prices),
[props.account, updatedAccount, prices],
)
return (
<div
data-testid='account-details'
className={classNames(
'hidden w-16 rounded-base border border-white/20 bg-white/5 backdrop-blur-sticky',
'md:block',
)}
className='w-16 rounded-base border border-white/20 bg-white/5 backdrop-blur-sticky'
>
<div className='flex flex-wrap justify-center w-full py-4'>
<Gauge tooltip='Health Factor' percentage={healthFactor} icon={<Heart />} />
<div className='flex w-full flex-wrap justify-center py-4'>
<Gauge tooltip='Health Factor' percentage={health} icon={<Heart />} />
<Text size='2xs' className='mb-0.5 mt-1 w-full text-center text-white/50'>
Health
</Text>
<FormattedNumber
className={'w-full text-center text-xs'}
amount={healthFactor}
options={{ maxDecimals: 0, minDecimals: 0 }}
animate
/>
<div className='flex'>
<FormattedNumber
className={'w-full text-center text-xs'}
amount={health}
options={{ maxDecimals: 1, minDecimals: 0 }}
animate
/>
{updatedHealth && health !== updatedHealth && (
<>
<ArrowRight
width={16}
className={classNames(health > updatedHealth ? 'text-loss' : 'text-success')}
/>
<FormattedNumber
className={'w-full text-center text-xs'}
amount={updatedHealth}
options={{ maxDecimals: 1, minDecimals: 0 }}
animate
/>
</>
)}
</div>
</div>
<div className='w-full py-4 border border-x-0 border-white/20'>
<div className='w-full border-t border-white/20 py-4'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
Balance
Leverage
</Text>
<DisplayCurrency coin={coin} className='w-full text-center truncate text-2xs ' />
<Text size='xs' className='text-center'>
{formatLeverage(leverage.toNumber())}
</Text>
</div>
<div className='w-full border-t border-white/20 py-4'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
Net worth
</Text>
<DisplayCurrency coin={coin} className='w-full truncate text-center text-2xs ' />
</div>
</div>
)

View File

@ -102,7 +102,7 @@ export default function VaultModalContent(props: Props) {
{
renderContent: () => (
<VaultBorrowings
updatedAccount={updatedAccount}
updatedAccount={updatedAccount || props.account}
borrowings={addedDebt}
deposits={removedDeposits}
primaryAsset={props.primaryAsset}

View File

@ -1,3 +1,4 @@
import debounce from 'lodash.debounce'
import { useCallback, useEffect, useMemo, useState } from 'react'
import Divider from 'components/Divider'
@ -21,6 +22,7 @@ import { BNCoin } from 'types/classes/BNCoin'
import estimateExactIn from 'api/swap/estimateExactIn'
import useHealthComputer from 'hooks/useHealthComputer'
import useMarketBorrowings from 'hooks/useMarketBorrowings'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
interface Props {
buyAsset: Asset
@ -45,6 +47,7 @@ export default function SwapForm(props: Props) {
const [estimatedFee, setEstimatedFee] = useState(defaultFee)
const throttledEstimateExactIn = useMemo(() => asyncThrottle(estimateExactIn, 250), [])
const { removeDeposits, addDeposits, addDebt } = useUpdatedAccount(account)
const borrowAsset = useMemo(
() => borrowAssets.find(byDenom(sellAsset.denom)),
@ -152,6 +155,37 @@ export default function SwapForm(props: Props) {
setSellAssetAmount(BN_ZERO)
}, [buyAsset.denom, sellAsset.denom])
const debouncedUpdateAccount = useMemo(
() =>
debounce((removeCoin: BNCoin, addCoin: BNCoin, debtCoin: BNCoin) => {
removeDeposits([removeCoin])
addDeposits([addCoin])
if (debtCoin.amount.isGreaterThan(BN_ZERO)) addDebt([debtCoin])
}, 1000),
[removeDeposits, addDeposits, addDebt],
)
useEffect(() => {
const removeDepositAmount = sellAssetAmount.isGreaterThanOrEqualTo(marginThreshold)
? marginThreshold
: sellAssetAmount
const addDebtAmount = sellAssetAmount.isGreaterThan(marginThreshold)
? sellAssetAmount.minus(marginThreshold)
: BN_ZERO
const removeCoin = BNCoin.fromDenomAndBigNumber(sellAsset.denom, removeDepositAmount)
const debtCoin = BNCoin.fromDenomAndBigNumber(sellAsset.denom, addDebtAmount)
const addCoin = BNCoin.fromDenomAndBigNumber(buyAsset.denom, buyAssetAmount)
debouncedUpdateAccount(removeCoin, addCoin, debtCoin)
}, [
sellAssetAmount,
buyAssetAmount,
marginThreshold,
buyAsset.denom,
sellAsset.denom,
debouncedUpdateAccount,
])
useEffect(() => {
swapTx.estimateFee().then(setEstimatedFee)
}, [swapTx])

View File

@ -3,14 +3,17 @@ import { useCallback, useEffect, useState } from 'react'
import { addCoins, addValueToVaults, removeCoins } from 'hooks/useUpdatedAccount/functions'
import { BNCoin } from 'types/classes/BNCoin'
import { cloneAccount } from 'utils/accounts'
import useStore from 'store'
export interface VaultValue {
address: string
value: BigNumber
}
export function useUpdatedAccount(account: Account) {
const [updatedAccount, setUpdatedAccount] = useState<Account>(cloneAccount(account))
export function useUpdatedAccount(account?: Account) {
const [updatedAccount, setUpdatedAccount] = useState<Account | undefined>(
account ? cloneAccount(account) : undefined,
)
const [addedDeposits, addDeposits] = useState<BNCoin[]>([])
const [removedDeposits, removeDeposits] = useState<BNCoin[]>([])
const [addedDebt, addDebt] = useState<BNCoin[]>([])
@ -19,6 +22,7 @@ export function useUpdatedAccount(account: Account) {
const removeDepositByDenom = useCallback(
(denom: string) => {
if (!account) return
const deposit = account.deposits.find((deposit) => deposit.denom === denom)
if (deposit) {
removeDeposits([...removedDeposits, deposit])
@ -28,17 +32,18 @@ export function useUpdatedAccount(account: Account) {
)
useEffect(() => {
async function updateAccount() {
const accountCopy = cloneAccount(account)
accountCopy.deposits = addCoins(addedDeposits, [...accountCopy.deposits])
accountCopy.debts = addCoins(addedDebt, [...accountCopy.debts])
accountCopy.vaults = addValueToVaults(addedVaultValues, [...accountCopy.vaults])
accountCopy.deposits = removeCoins(removedDeposits, [...accountCopy.deposits])
accountCopy.debts = removeCoins(removedDebt, [...accountCopy.debts])
setUpdatedAccount(accountCopy)
}
if (!account) return
updateAccount()
const accountCopy = cloneAccount(account)
accountCopy.deposits = addCoins(addedDeposits, [...accountCopy.deposits])
accountCopy.debts = addCoins(addedDebt, [...accountCopy.debts])
accountCopy.vaults = addValueToVaults(addedVaultValues, [...accountCopy.vaults])
accountCopy.deposits = removeCoins(removedDeposits, [...accountCopy.deposits])
accountCopy.debts = removeCoins(removedDebt, [...accountCopy.debts])
setUpdatedAccount(accountCopy)
useStore.setState({ updatedAccount: accountCopy })
return () => useStore.setState({ updatedAccount: undefined })
}, [account, addedDebt, removedDebt, addedDeposits, removedDeposits, addedVaultValues])
return {

View File

@ -1,7 +1,5 @@
import { GetState, SetState } from 'zustand'
import { ASSETS } from 'constants/assets'
export default function createCommonSlice(set: SetState<CommonSlice>, get: GetState<CommonSlice>) {
return {
accounts: null,

View File

@ -5,6 +5,7 @@ interface CommonSlice {
client?: WalletClient
isOpen: boolean
selectedAccount: string | null
updatedAccount?: Account
focusComponent: FocusComponent | null
}

View File

@ -16,24 +16,36 @@ export const calculateAccountBalanceValue = (
const depositsValue = calculateAccountValue('deposits', account, prices)
const lendsValue = calculateAccountValue('lends', account, prices)
const debtsValue = calculateAccountValue('debts', account, prices)
const vaultsValue = calculateAccountValue('vaults', account, prices)
return depositsValue.plus(lendsValue).minus(debtsValue)
return depositsValue.plus(lendsValue).plus(vaultsValue).minus(debtsValue)
}
export const getAccountPositionValues = (account: Account | AccountChange, prices: BNCoin[]) => {
const deposits = calculateAccountValue('deposits', account, prices)
const lends = calculateAccountValue('lends', account, prices)
const debts = calculateAccountValue('debts', account, prices)
return [deposits, lends, debts]
const vaults = calculateAccountValue('vaults', account, prices)
return [deposits, lends, debts, vaults]
}
export const calculateAccountValue = (
type: 'deposits' | 'lends' | 'debts',
type: 'deposits' | 'lends' | 'debts' | 'vaults',
account: Account | AccountChange,
prices: BNCoin[],
): BigNumber => {
if (!account[type]) return BN_ZERO
if (type === 'vaults') {
return (
account.vaults?.reduce(
(acc, vaultPosition) =>
acc.plus(vaultPosition.values.primary).plus(vaultPosition.values.secondary),
BN_ZERO,
) || BN_ZERO
).shiftedBy(-6)
}
return account[type]?.reduce((acc, position) => {
const asset = getAssetByDenom(position.denom)
if (!asset) return acc
@ -65,6 +77,11 @@ export const calculateAccountBorrowRate = (
return BN_ZERO
}
export function calculateAccountLeverage(account: Account, prices: BNCoin[]) {
const [deposits, lends, debts, vaults] = getAccountPositionValues(account, prices)
return debts.dividedBy(deposits.plus(lends).plus(vaults)).plus(1)
}
export function getAmount(denom: string, coins: Coin[]): BigNumber {
return BN(coins.find((asset) => asset.denom === denom)?.amount ?? 0)
}

View File

@ -125,6 +125,7 @@ export const formatValue = (amount: number | string, options?: FormatOptions): s
return returnValue
}
export function formatHealth(health: number) {
return formatValue(health, {
minDecimals: 0,
@ -134,7 +135,7 @@ export function formatHealth(health: number) {
export function formatLeverage(leverage: number) {
return formatValue(leverage, {
minDecimals: 0,
minDecimals: 2,
suffix: 'x',
})
}

View File

@ -3594,6 +3594,13 @@
dependencies:
"@types/lodash" "*"
"@types/lodash.debounce@^4.0.7":
version "4.0.7"
resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.7.tgz#0285879defb7cdb156ae633cecd62d5680eded9f"
integrity sha512-X1T4wMZ+gT000M2/91SYj0d/7JfeNZ9PeeOldSNoE/lunLeQXKvkmIumI29IaKMotU/ln/McOIvgzZcQ/3TrSA==
dependencies:
"@types/lodash" "*"
"@types/lodash.values@^4.3.6":
version "4.3.7"
resolved "https://registry.npmjs.org/@types/lodash.values/-/lodash.values-4.3.7.tgz"