[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.local.mainnet
.env.production .env.production
.env .env
# IDE
.idea

View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
import debounce from 'lodash.debounce'
import { useCallback, useEffect, useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
import Divider from 'components/Divider' import Divider from 'components/Divider'
@ -21,6 +22,7 @@ import { BNCoin } from 'types/classes/BNCoin'
import estimateExactIn from 'api/swap/estimateExactIn' import estimateExactIn from 'api/swap/estimateExactIn'
import useHealthComputer from 'hooks/useHealthComputer' import useHealthComputer from 'hooks/useHealthComputer'
import useMarketBorrowings from 'hooks/useMarketBorrowings' import useMarketBorrowings from 'hooks/useMarketBorrowings'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
interface Props { interface Props {
buyAsset: Asset buyAsset: Asset
@ -45,6 +47,7 @@ export default function SwapForm(props: Props) {
const [estimatedFee, setEstimatedFee] = useState(defaultFee) const [estimatedFee, setEstimatedFee] = useState(defaultFee)
const throttledEstimateExactIn = useMemo(() => asyncThrottle(estimateExactIn, 250), []) const throttledEstimateExactIn = useMemo(() => asyncThrottle(estimateExactIn, 250), [])
const { removeDeposits, addDeposits, addDebt } = useUpdatedAccount(account)
const borrowAsset = useMemo( const borrowAsset = useMemo(
() => borrowAssets.find(byDenom(sellAsset.denom)), () => borrowAssets.find(byDenom(sellAsset.denom)),
@ -152,6 +155,37 @@ export default function SwapForm(props: Props) {
setSellAssetAmount(BN_ZERO) setSellAssetAmount(BN_ZERO)
}, [buyAsset.denom, sellAsset.denom]) }, [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(() => { useEffect(() => {
swapTx.estimateFee().then(setEstimatedFee) swapTx.estimateFee().then(setEstimatedFee)
}, [swapTx]) }, [swapTx])

View File

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

View File

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

View File

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

View File

@ -16,24 +16,36 @@ export const calculateAccountBalanceValue = (
const depositsValue = calculateAccountValue('deposits', account, prices) const depositsValue = calculateAccountValue('deposits', account, prices)
const lendsValue = calculateAccountValue('lends', account, prices) const lendsValue = calculateAccountValue('lends', account, prices)
const debtsValue = calculateAccountValue('debts', 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[]) => { export const getAccountPositionValues = (account: Account | AccountChange, prices: BNCoin[]) => {
const deposits = calculateAccountValue('deposits', account, prices) const deposits = calculateAccountValue('deposits', account, prices)
const lends = calculateAccountValue('lends', account, prices) const lends = calculateAccountValue('lends', account, prices)
const debts = calculateAccountValue('debts', account, prices) const debts = calculateAccountValue('debts', account, prices)
const vaults = calculateAccountValue('vaults', account, prices)
return [deposits, lends, debts] return [deposits, lends, debts, vaults]
} }
export const calculateAccountValue = ( export const calculateAccountValue = (
type: 'deposits' | 'lends' | 'debts', type: 'deposits' | 'lends' | 'debts' | 'vaults',
account: Account | AccountChange, account: Account | AccountChange,
prices: BNCoin[], prices: BNCoin[],
): BigNumber => { ): BigNumber => {
if (!account[type]) return BN_ZERO 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) => { return account[type]?.reduce((acc, position) => {
const asset = getAssetByDenom(position.denom) const asset = getAssetByDenom(position.denom)
if (!asset) return acc if (!asset) return acc
@ -65,6 +77,11 @@ export const calculateAccountBorrowRate = (
return BN_ZERO 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 { export function getAmount(denom: string, coins: Coin[]): BigNumber {
return BN(coins.find((asset) => asset.denom === denom)?.amount ?? 0) 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 return returnValue
} }
export function formatHealth(health: number) { export function formatHealth(health: number) {
return formatValue(health, { return formatValue(health, {
minDecimals: 0, minDecimals: 0,
@ -134,7 +135,7 @@ export function formatHealth(health: number) {
export function formatLeverage(leverage: number) { export function formatLeverage(leverage: number) {
return formatValue(leverage, { return formatValue(leverage, {
minDecimals: 0, minDecimals: 2,
suffix: 'x', suffix: 'x',
}) })
} }

View File

@ -3594,6 +3594,13 @@
dependencies: dependencies:
"@types/lodash" "*" "@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": "@types/lodash.values@^4.3.6":
version "4.3.7" version "4.3.7"
resolved "https://registry.npmjs.org/@types/lodash.values/-/lodash.values-4.3.7.tgz" resolved "https://registry.npmjs.org/@types/lodash.values/-/lodash.values-4.3.7.tgz"