From c4a2a7d9139f592b54c6d073bcbdde82556ebc83 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Tue, 30 Jan 2024 16:18:54 +0100 Subject: [PATCH] Perps account preview (#750) * fix: fixed the Liquidation Price inside the TradeSummary * feat: added account preview * tidy: refactor * feat: added HLS intro * fix: closing the wallet select focusComponent * fix: added perps position update to edit as well * fix: fix update perp * fix: fail catch * fix: implemented suggest changes * tidy: fix * fix: unfix * fix: created helper function * tidy: console.log --- src/components/Wallet/WalletConnecting.tsx | 47 +++++++++++++++++-- .../account/AccountBalancesTable/functions.ts | 1 - .../Columns/Asset.tsx | 3 +- src/components/account/AccountSummary.tsx | 4 +- src/components/header/DesktopHeader.tsx | 15 +++--- .../hls/Staking/HLSStakingIntro.tsx | 2 +- .../BalancesTable/Columns/TradeDirection.tsx | 1 + .../perps/Module/PerpsManageModule/index.tsx | 41 ++++++++++++++-- src/components/perps/Module/PerpsModule.tsx | 32 +++++++++++-- .../TradeModule/SwapForm/TradeSummary.tsx | 4 +- src/configs/chains/osmosis/devnet.ts | 3 +- src/hooks/useDisplayCurrencyPrice.ts | 9 ++-- src/hooks/useUpdatedAccount/functions.ts | 21 +++++++++ src/hooks/useUpdatedAccount/index.ts | 15 ++++++ src/types/enums/docURL.ts | 1 + src/utils/accounts.ts | 10 +++- src/utils/getPerpsPosition.ts | 23 +++++++++ 17 files changed, 200 insertions(+), 32 deletions(-) create mode 100644 src/utils/getPerpsPosition.ts diff --git a/src/components/Wallet/WalletConnecting.tsx b/src/components/Wallet/WalletConnecting.tsx index e9a3e5fa..156e01aa 100644 --- a/src/components/Wallet/WalletConnecting.tsx +++ b/src/components/Wallet/WalletConnecting.tsx @@ -26,8 +26,16 @@ const mapErrorMessages = (providerId: string, errorMessage: string, name: string } export default function WalletConnecting(props: Props) { - const { connect, mobileConnect, simulate, sign, broadcast, mobileProviders, extensionProviders } = - useShuttle() + const { + connect, + mobileConnect, + simulate, + sign, + broadcast, + mobileProviders, + extensionProviders, + disconnect, + } = useShuttle() const providers = useMemo( () => [...mobileProviders, ...extensionProviders], [mobileProviders, extensionProviders], @@ -85,6 +93,9 @@ export default function WalletConnecting(props: Props) { }} /> ), + onClose: () => { + disconnect({ chainId: chainConfig.id }) + }, }, }) } @@ -92,7 +103,17 @@ export default function WalletConnecting(props: Props) { } if (!isConnecting) handleConnectAsync() }, - [isConnecting, client, setIsConnecting, connect, chainConfig, broadcast, sign, simulate], + [ + isConnecting, + client, + setIsConnecting, + connect, + chainConfig, + broadcast, + sign, + simulate, + disconnect, + ], ) const handleMobileConnect = useCallback( @@ -105,6 +126,9 @@ export default function WalletConnecting(props: Props) { userDomain: undefined, focusComponent: { component: , + onClose: () => { + disconnect({ chainId: chainConfig.id }) + }, }, }) return @@ -144,6 +168,9 @@ export default function WalletConnecting(props: Props) { }} /> ), + onClose: () => { + disconnect({ chainId: chainConfig.id }) + }, }, }) } @@ -161,6 +188,7 @@ export default function WalletConnecting(props: Props) { broadcast, sign, simulate, + disconnect, ], ) @@ -172,6 +200,9 @@ export default function WalletConnecting(props: Props) { userDomain: undefined, focusComponent: { component: , + onClose: () => { + disconnect({ chainId: chainConfig.id }) + }, }, }) return @@ -188,7 +219,15 @@ export default function WalletConnecting(props: Props) { return } handleConnect(provider.id) - }, [handleConnect, isConnecting, providerId, providers, handleMobileConnect]) + }, [ + handleConnect, + isConnecting, + providerId, + providers, + handleMobileConnect, + disconnect, + chainConfig.id, + ]) return ( @@ -58,6 +58,7 @@ function TooltipContent(props: Props) { export default function Asset(props: Props) { const { row } = props + return ( } type='info'> diff --git a/src/components/account/AccountSummary.tsx b/src/components/account/AccountSummary.tsx index dcb5c1ff..0f7fc463 100644 --- a/src/components/account/AccountSummary.tsx +++ b/src/components/account/AccountSummary.tsx @@ -38,7 +38,6 @@ export default function AccountSummary(props: Props) { const { data: prices } = usePrices() const assets = useAllAssets() const updatedAccount = useStore((s) => s.updatedAccount) - const chainConfig = useStore((s) => s.chainConfig) const accountBalance = useMemo( () => props.account @@ -104,7 +103,7 @@ export default function AccountSummary(props: Props) { renderSubTitle: () => <>, }, ] - if (chainConfig.perps) + if (props.account.perps.length > 0) itemsArray.push({ title: 'Perp Positions', renderContent: () => @@ -122,7 +121,6 @@ export default function AccountSummary(props: Props) { borrowAssetsData, lendingAssetsData, props.isHls, - chainConfig.perps, handleToggle, accountSummaryTabs, ]) diff --git a/src/components/header/DesktopHeader.tsx b/src/components/header/DesktopHeader.tsx index 2a3448f0..e6b7b7f2 100644 --- a/src/components/header/DesktopHeader.tsx +++ b/src/components/header/DesktopHeader.tsx @@ -1,16 +1,16 @@ import classNames from 'classnames' -import { isDesktop } from 'react-device-detect' import { useMemo } from 'react' +import { isDesktop } from 'react-device-detect' +import Wallet from 'components/Wallet' import AccountMenu from 'components/account/AccountMenu' import EscButton from 'components/common/Button/EscButton' +import { Coins, CoinsSwap } from 'components/common/Icons' +import Settings from 'components/common/Settings' import ChainSelect from 'components/header/ChainSelect' import OracleResyncButton from 'components/header/OracleResyncButton' -import { Coins, CoinsSwap } from 'components/common/Icons' -import DesktopNavigation from 'components/header/navigation/DesktopNavigation' import RewardsCenter from 'components/header/RewardsCenter' -import Settings from 'components/common/Settings' -import Wallet from 'components/Wallet' +import DesktopNavigation from 'components/header/navigation/DesktopNavigation' import useAccountId from 'hooks/useAccountId' import useStore from 'store' import { WalletID } from 'types/enums/wallet' @@ -83,7 +83,10 @@ export default function DesktopHeader() { )} - +
+ {!address && } + +
) : (
diff --git a/src/components/hls/Staking/HLSStakingIntro.tsx b/src/components/hls/Staking/HLSStakingIntro.tsx index 7b04bc54..e4e11601 100644 --- a/src/components/hls/Staking/HLSStakingIntro.tsx +++ b/src/components/hls/Staking/HLSStakingIntro.tsx @@ -20,7 +20,7 @@ export default function HLSStakingIntro() { leftIcon={} onClick={(e) => { e.preventDefault() - window.open(DocURL.FARM_INTRO_URL, '_blank') + window.open(DocURL.HLS_INTRO_URL, '_blank') }} color='secondary' /> diff --git a/src/components/perps/BalancesTable/Columns/TradeDirection.tsx b/src/components/perps/BalancesTable/Columns/TradeDirection.tsx index 24223075..f2872036 100644 --- a/src/components/perps/BalancesTable/Columns/TradeDirection.tsx +++ b/src/components/perps/BalancesTable/Columns/TradeDirection.tsx @@ -5,6 +5,7 @@ export const PERP_TYPE_META = { accessorKey: 'tradeDirection', header: 'Side' } type Props = { tradeDirection: TradeDirection className?: string + directionChange?: boolean } export default function TradeDirection(props: Props) { diff --git a/src/components/perps/Module/PerpsManageModule/index.tsx b/src/components/perps/Module/PerpsManageModule/index.tsx index 0712df18..b1cbb2d8 100644 --- a/src/components/perps/Module/PerpsManageModule/index.tsx +++ b/src/components/perps/Module/PerpsManageModule/index.tsx @@ -1,5 +1,6 @@ import classNames from 'classnames' -import { useState } from 'react' +import debounce from 'lodash.debounce' +import { useEffect, useMemo, useState } from 'react' import { Cross } from 'components/common/Icons' import LeverageSlider from 'components/common/LeverageSlider' @@ -11,12 +12,16 @@ import { Or } from 'components/perps/Module/Or' import usePerpsManageModule from 'components/perps/Module/PerpsManageModule/usePerpsManageModule' import PerpsSummary from 'components/perps/Module/Summary' import AssetAmountInput from 'components/trade/TradeModule/SwapForm/AssetAmountInput' +import useCurrentAccount from 'hooks/accounts/useCurrentAccount' +import { useUpdatedAccount } from 'hooks/useUpdatedAccount' +import getPerpsPosition from 'utils/getPerpsPosition' import { BN } from 'utils/helpers' export function PerpsManageModule() { const [tradeDirection, setTradeDirection] = useState(null) const [amount, setAmount] = useState(null) - + const account = useCurrentAccount() + const { simulatePerps, addedPerps } = useUpdatedAccount(account) const { closeManagePerpModule, previousAmount, @@ -26,6 +31,36 @@ export function PerpsManageModule() { asset, } = usePerpsManageModule(amount) + const debouncedUpdateAccount = useMemo( + () => + debounce((perpsPosition: PerpsPosition) => { + if ( + addedPerps && + perpsPosition.amount === addedPerps.amount && + perpsPosition.tradeDirection === addedPerps.tradeDirection + ) + return + simulatePerps(perpsPosition) + }, 100), + [simulatePerps, addedPerps], + ) + + useEffect(() => { + const perpsPosition = getPerpsPosition( + asset, + amount ?? previousAmount, + tradeDirection ?? previousTradeDirection, + ) + debouncedUpdateAccount(perpsPosition) + }, [ + debouncedUpdateAccount, + asset, + amount, + previousAmount, + tradeDirection, + previousTradeDirection, + ]) + if (!asset) return null return ( @@ -47,7 +82,7 @@ export function PerpsManageModule() { /> ('long') const { perpsAsset } = usePerpsAsset() const [leverage, setLeverage] = useState(1) - + const account = useCurrentAccount() + const { simulatePerps, addedPerps } = useUpdatedAccount(account) const [amount, setAmount] = useState(BN_ZERO) + const debouncedUpdateAccount = useMemo( + () => + debounce((perpsPosition: PerpsPosition) => { + if ( + addedPerps && + perpsPosition.amount === addedPerps.amount && + perpsPosition.tradeDirection === addedPerps.tradeDirection + ) + return + simulatePerps(perpsPosition) + }, 100), + [simulatePerps, addedPerps], + ) + + useEffect(() => { + const perpsPosition = getPerpsPosition(perpsAsset, amount, tradeDirection) + debouncedUpdateAccount(perpsPosition) + }, [debouncedUpdateAccount, amount, perpsAsset, tradeDirection]) + if (!perpsAsset) return null return ( } - className='mb-4 h-full' + className='h-full mb-4' > )}
diff --git a/src/configs/chains/osmosis/devnet.ts b/src/configs/chains/osmosis/devnet.ts index 540a840e..bdf24af0 100644 --- a/src/configs/chains/osmosis/devnet.ts +++ b/src/configs/chains/osmosis/devnet.ts @@ -1,8 +1,7 @@ +import Osmosis1 from 'configs/chains/osmosis/osmosis-1' import { NETWORK } from 'types/enums/network' import { ChainInfoID } from 'types/enums/wallet' -import Osmosis1 from './osmosis-1' - const Devnet: ChainConfig = { ...Osmosis1, id: ChainInfoID.OsmosisDevnet, diff --git a/src/hooks/useDisplayCurrencyPrice.ts b/src/hooks/useDisplayCurrencyPrice.ts index 17bf08bb..bf72328f 100644 --- a/src/hooks/useDisplayCurrencyPrice.ts +++ b/src/hooks/useDisplayCurrencyPrice.ts @@ -33,9 +33,12 @@ function useDisplayCurrencyPrice() { ) const convertAmount = useCallback( - (asset: Asset, amount: string | number | BigNumber) => - getConversionRate(asset.denom)?.multipliedBy(BN(amount).shiftedBy(-asset.decimals)) ?? - BN_ZERO, + (asset: Asset, amount: string | number | BigNumber) => { + return ( + getConversionRate(asset.denom)?.multipliedBy(BN(amount).shiftedBy(-asset.decimals)) ?? + BN_ZERO + ) + }, [getConversionRate], ) diff --git a/src/hooks/useUpdatedAccount/functions.ts b/src/hooks/useUpdatedAccount/functions.ts index 5c7c9f0d..0e498b9e 100644 --- a/src/hooks/useUpdatedAccount/functions.ts +++ b/src/hooks/useUpdatedAccount/functions.ts @@ -36,6 +36,27 @@ export function removeCoins(coinsToRemove: BNCoin[], currentCoins: BNCoin[]) { return currentCoins } +export function updatePerpsPositions( + currentPositions: PerpsPosition[], + updatedPosition?: PerpsPosition, +): PerpsPosition[] { + if (!updatedPosition) { + return currentPositions ?? [] + } + const currentDenoms = currentPositions.map((position) => position.denom) + const index = currentDenoms.indexOf(updatedPosition.denom) + + if (index === -1) { + currentPositions.push(updatedPosition) + return currentPositions + } + + currentPositions[index].tradeDirection = updatedPosition.tradeDirection + currentPositions[index].amount = updatedPosition.amount + + return currentPositions +} + export function addValueToVaults( vaultValues: VaultValue[], vaults: DepositedVault[], diff --git a/src/hooks/useUpdatedAccount/index.ts b/src/hooks/useUpdatedAccount/index.ts index 1c44e492..519dd631 100644 --- a/src/hooks/useUpdatedAccount/index.ts +++ b/src/hooks/useUpdatedAccount/index.ts @@ -11,6 +11,7 @@ import { addValueToVaults, getDepositAndLendCoinsToSpend, removeCoins, + updatePerpsPositions, } from 'hooks/useUpdatedAccount/functions' import useVaults from 'hooks/useVaults' import useStore from 'store' @@ -43,6 +44,7 @@ export function useUpdatedAccount(account?: Account) { const [addedLends, addLends] = useState([]) const [removedLends, removeLends] = useState([]) const [addedTrades, addTrades] = useState([]) + const [addedPerps, addPerps] = useState() const [leverage, setLeverage] = useState(0) const removeDepositAndLendsByDenom = useCallback( @@ -241,6 +243,14 @@ export function useUpdatedAccount(account?: Account) { [account, assets, prices, slippage], ) + const simulatePerps = useCallback( + (position: PerpsPosition) => { + if (!account) return + addPerps(position) + }, + [account, addPerps], + ) + useEffect(() => { if (!account) return @@ -252,6 +262,7 @@ export function useUpdatedAccount(account?: Account) { [...accountCopy.vaults], availableVaults ?? [], ) + accountCopy.perps = updatePerpsPositions([...accountCopy.perps], addedPerps) accountCopy.deposits = removeCoins(removedDeposits, [...accountCopy.deposits]) accountCopy.debts = removeCoins(removedDebts, [...accountCopy.debts]) accountCopy.lends = addCoins(addedLends, [...accountCopy.lends]) @@ -274,6 +285,7 @@ export function useUpdatedAccount(account?: Account) { prices, addedTrades, assets, + addedPerps, ]) return { @@ -286,10 +298,12 @@ export function useUpdatedAccount(account?: Account) { addLends, removeLends, addVaultValues, + addPerps, addedDeposits, addedDebts, addedLends, addedTrades, + addedPerps, leverage, removedDeposits, removedDebts, @@ -303,5 +317,6 @@ export function useUpdatedAccount(account?: Account) { simulateTrade, simulateVaultDeposit, simulateWithdraw, + simulatePerps, } } diff --git a/src/types/enums/docURL.ts b/src/types/enums/docURL.ts index 44d7cc4d..93bd8b43 100644 --- a/src/types/enums/docURL.ts +++ b/src/types/enums/docURL.ts @@ -8,6 +8,7 @@ export enum DocURL { COUNCIL_KEPLR = 'https://wallet.keplr.app/chains/mars-hub?tab=governance', DOCS_URL = 'https://docs.marsprotocol.io/', FARM_INTRO_URL = 'https://docs.marsprotocol.io/docs/learn/tutorials/farming/farming-intro', + HLS_INTRO_URL = 'https://docs.marsprotocol.io/docs/learn/mars-v2/high-leveraged-strategies/high-leveraged-strategies-intro', MANAGE_ACCOUNT_URL = 'https://docs.marsprotocol.io/docs/learn/tutorials/credit-accounts/credit-accounts-intro', ROVER_INTRO_URL = 'https://docs.marsprotocol.io/docs/learn/mars-v2/credit-accounts', PRIVACY_POLICY_URL = 'https://docs.marsprotocol.io/docs/overview/legal/privacy-policy', diff --git a/src/utils/accounts.ts b/src/utils/accounts.ts index e3df2167..8b97d04e 100644 --- a/src/utils/accounts.ts +++ b/src/utils/accounts.ts @@ -242,8 +242,14 @@ export function cloneAccount(account: Account): Account { unlocked: vault.values.unlocked, }, })), - // TODO: 📈Add correct type mapping - perps: account.perps, + perps: account.perps.map((perpPosition) => ({ + ...perpPosition, + amount: perpPosition.amount, + closingFee: perpPosition.closingFee, + pnl: perpPosition.pnl, + entryPrice: perpPosition.entryPrice, + tradeDirection: perpPosition.tradeDirection, + })), } } diff --git a/src/utils/getPerpsPosition.ts b/src/utils/getPerpsPosition.ts new file mode 100644 index 00000000..ec7e2725 --- /dev/null +++ b/src/utils/getPerpsPosition.ts @@ -0,0 +1,23 @@ +import BigNumber from 'bignumber.js' + +import { BN_ONE } from 'constants/math' +import { BNCoin } from 'types/classes/BNCoin' + +export default function getPerpsPosition( + asset: Asset, + amount: BigNumber, + tradeDirection: TradeDirection, +) { + const perpsBaseDenom = 'ibc/F91EA2C0A23697A1048E08C2F787E3A58AC6F706A1CD2257A504925158CFC0F3' + const perpsPosition = { + amount, + closingFee: BNCoin.fromDenomAndBigNumber(perpsBaseDenom, BN_ONE), + pnl: BNCoin.fromDenomAndBigNumber(perpsBaseDenom, BN_ONE.negated()), + entryPrice: BN_ONE, + baseDenom: perpsBaseDenom, + denom: asset.denom, + tradeDirection, + } + + return perpsPosition +}