From 442b7a3a8c8cf1f89b23581eed9ed03f07dac5f6 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Wed, 14 Feb 2024 11:11:34 +0100 Subject: [PATCH 01/15] feat: implement v1 tables into v2 with data fetching --- src/api/cache.ts | 9 +++- src/api/cosmwasm-client.ts | 20 ++++++++ src/api/v1/getV1Positions.ts | 41 ++++++++++++++++ src/components/borrow/Borrowings.tsx | 8 ++-- src/components/borrow/Table/Columns/Debt.tsx | 36 -------------- .../borrow/Table/Columns/DebtValue.tsx | 30 ++++++++++++ .../Table/Columns/useDepositedColumns.tsx | 11 +++-- .../borrow/Table/DepositedBorrowingsTable.tsx | 13 ++++- .../Table/useBorrowMarketAssetsTableData.ts | 14 ++++-- src/components/common/Intro.tsx | 2 +- .../earn/lend/Table/Columns/DepositValue.tsx | 1 + .../earn/lend/Table/DepositedLendsTable.tsx | 19 ++++++-- src/components/header/DesktopHeader.tsx | 1 + src/components/header/navigation/Routes.tsx | 3 ++ src/components/v1/Borrowings.tsx | 47 ++++++++++++++++++ src/components/v1/Deposits.tsx | 48 +++++++++++++++++++ .../v1/Table/useV1BorrowingsTableData.ts | 38 +++++++++++++++ .../v1/Table/useV1DepositsTableData.ts | 39 +++++++++++++++ src/components/v1/V1Intro.tsx | 24 ++++++++++ src/hooks/v1/useV1Positions.tsx | 17 +++++++ src/pages/V1Page.tsx | 15 ++++++ src/types/interfaces/market.d.ts | 3 +- src/types/interfaces/route.d.ts | 1 + src/types/interfaces/v1.d.ts | 4 ++ 24 files changed, 387 insertions(+), 57 deletions(-) create mode 100644 src/api/v1/getV1Positions.ts delete mode 100644 src/components/borrow/Table/Columns/Debt.tsx create mode 100644 src/components/borrow/Table/Columns/DebtValue.tsx create mode 100644 src/components/v1/Borrowings.tsx create mode 100644 src/components/v1/Deposits.tsx create mode 100644 src/components/v1/Table/useV1BorrowingsTableData.ts create mode 100644 src/components/v1/Table/useV1DepositsTableData.ts create mode 100644 src/components/v1/V1Intro.tsx create mode 100644 src/hooks/v1/useV1Positions.tsx create mode 100644 src/pages/V1Page.tsx create mode 100644 src/types/interfaces/v1.d.ts diff --git a/src/api/cache.ts b/src/api/cache.ts index 3ce93c94..f6051db3 100644 --- a/src/api/cache.ts +++ b/src/api/cache.ts @@ -10,7 +10,12 @@ import { TotalDepositResponse, VaultConfigBaseForAddr, } from 'types/generated/mars-params/MarsParams.types' -import { ArrayOfMarket } from 'types/generated/mars-red-bank/MarsRedBank.types' +import { + ArrayOfMarket, + ArrayOfUserCollateralResponse, + ArrayOfUserDebtResponse, + UserCollateralResponse, +} from 'types/generated/mars-red-bank/MarsRedBank.types' interface Cache extends Map {} @@ -62,3 +67,5 @@ export const underlyingDebtCache: Cache = new Map() export const previewDepositCache: Cache<{ vaultAddress: string; amount: string }> = new Map() export const stakingAprCache: Cache = new Map() export const assetParamsCache: Cache = new Map() +export const userCollateralCache: Cache = new Map() +export const userDebtCache: Cache = new Map() diff --git a/src/api/cosmwasm-client.ts b/src/api/cosmwasm-client.ts index 357b6167..f6a0ab0a 100644 --- a/src/api/cosmwasm-client.ts +++ b/src/api/cosmwasm-client.ts @@ -6,6 +6,7 @@ import { MarsMockVaultQueryClient } from 'types/generated/mars-mock-vault/MarsMo import { MarsOracleOsmosisQueryClient } from 'types/generated/mars-oracle-osmosis/MarsOracleOsmosis.client' import { MarsParamsQueryClient } from 'types/generated/mars-params/MarsParams.client' import { MarsPerpsQueryClient } from 'types/generated/mars-perps/MarsPerps.client' +import { MarsRedBankQueryClient } from 'types/generated/mars-red-bank/MarsRedBank.client' import { MarsSwapperOsmosisQueryClient } from 'types/generated/mars-swapper-osmosis/MarsSwapperOsmosis.client' let _cosmWasmClient: Map = new Map() @@ -15,6 +16,7 @@ let _paramsQueryClient: Map = new Map() let _incentivesQueryClient: Map = new Map() let _swapperOsmosisClient: Map = new Map() let _perpsClient: Map = new Map() +let _redBankQueryClient: Map = new Map() const getClient = async (rpc: string) => { try { @@ -137,6 +139,23 @@ const getPerpsQueryClient = async (chainConfig: ChainConfig) => { } } +const getRedBankQueryClient = async (chainConfig: ChainConfig) => { + try { + const contract = chainConfig.contracts.redBank + const rpc = chainConfig.endpoints.rpc + const key = rpc + contract + + if (!_redBankQueryClient.get(key)) { + const client = await getClient(rpc) + _redBankQueryClient.set(key, new MarsRedBankQueryClient(client, contract)) + } + + return _redBankQueryClient.get(key)! + } catch (error) { + throw error + } +} + export { getClient, getCreditManagerQueryClient, @@ -146,4 +165,5 @@ export { getSwapperQueryClient, getVaultQueryClient, getPerpsQueryClient, + getRedBankQueryClient, } diff --git a/src/api/v1/getV1Positions.ts b/src/api/v1/getV1Positions.ts new file mode 100644 index 00000000..33032dfa --- /dev/null +++ b/src/api/v1/getV1Positions.ts @@ -0,0 +1,41 @@ +import { cacheFn, userCollateralCache, userDebtCache } from 'api/cache' +import { getRedBankQueryClient } from 'api/cosmwasm-client' +import { BNCoin } from 'types/classes/BNCoin' +import { + ArrayOfUserCollateralResponse, + ArrayOfUserDebtResponse, +} from 'types/generated/mars-red-bank/MarsRedBank.types' + +export default async function getV1Positions( + chainConfig: ChainConfig, + user?: string, +): Promise { + if (!user) return new Promise((_, reject) => reject('No account Wallet ID found')) + + const redBankQueryClient = await getRedBankQueryClient(chainConfig) + + const userCollateral: ArrayOfUserCollateralResponse = await cacheFn( + () => redBankQueryClient.userCollaterals({ user: user, limit: 100 }), + userCollateralCache, + `${chainConfig.id}/v1/deposits/${user}`, + ) + const userDebt: ArrayOfUserDebtResponse = await cacheFn( + () => redBankQueryClient.userDebts({ user: user, limit: 100 }), + userDebtCache, + `${chainConfig.id}/v1/debts/${user}`, + ) + + if (userCollateral && userDebt) { + return { + id: user, + debts: userDebt.map((debt) => new BNCoin(debt)), + lends: userCollateral.map((lend) => new BNCoin(lend)), + deposits: [], + vaults: [], + perps: [], + kind: 'default', + } + } + + return new Promise((_, reject) => reject('No account found')) +} diff --git a/src/components/borrow/Borrowings.tsx b/src/components/borrow/Borrowings.tsx index a30cc99e..4f197270 100644 --- a/src/components/borrow/Borrowings.tsx +++ b/src/components/borrow/Borrowings.tsx @@ -5,15 +5,15 @@ import { BN_ZERO } from 'constants/math' import useBorrowEnabledAssets from 'hooks/assets/useBorrowEnabledAssets' export default function Borrowings() { - const data = useBorrowMarketAssetsTableData() + const { accountBorrowedAssets, availableAssets, allAssets } = useBorrowMarketAssetsTableData() - if (!data?.allAssets?.length) { + if (!allAssets?.length) { return } return ( <> - - + + ) } diff --git a/src/components/borrow/Table/Columns/Debt.tsx b/src/components/borrow/Table/Columns/Debt.tsx deleted file mode 100644 index 794822c6..00000000 --- a/src/components/borrow/Table/Columns/Debt.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Row } from '@tanstack/react-table' - -import AmountAndValue from 'components/common/AmountAndValue' -import { BN_ZERO } from 'constants/math' -import useMarketEnabledAssets from 'hooks/assets/useMarketEnabledAssets' -import { byDenom } from 'utils/array' - -export const DEBT_META = { - accessorKey: 'debt', - header: 'Debt', -} - -export const debtSortingFn = ( - a: Row, - b: Row, -): number => { - const assetA = a.original.asset - const assetB = b.original.asset - if (!a.original.accountDebt || !b.original.accountDebt) return 0 - const debtA = a.original.accountDebt.shiftedBy(-assetA.decimals) - const debtB = b.original.accountDebt.shiftedBy(-assetB.decimals) - return debtA.minus(debtB).toNumber() -} - -interface Props { - data: BorrowMarketTableData -} - -export default function Debt(props: Props) { - const marketAssets = useMarketEnabledAssets() - const asset = marketAssets.find(byDenom(props.data.asset.denom)) - - if (!asset) return null - - return -} diff --git a/src/components/borrow/Table/Columns/DebtValue.tsx b/src/components/borrow/Table/Columns/DebtValue.tsx new file mode 100644 index 00000000..ea5a4d4d --- /dev/null +++ b/src/components/borrow/Table/Columns/DebtValue.tsx @@ -0,0 +1,30 @@ +import { Row } from '@tanstack/react-table' + +import AmountAndValue from 'components/common/AmountAndValue' +import { BN_ZERO } from 'constants/math' +import { BN } from 'utils/helpers' + +export const DEBT_VALUE_META = { + id: 'accountDebtValue', + accessorKey: 'accountDebtValue', + header: 'Debt', +} + +export const debtSortingFn = ( + a: Row, + b: Row, +): number => { + const debtValueA = BN(a.original?.accountDebtValue ?? 0) + const debtValueB = BN(b.original?.accountDebtValue ?? 0) + return debtValueA.minus(debtValueB).toNumber() +} + +interface Props { + asset: Asset + debtAmount?: BigNumber +} +export default function DebtValue(props: Props) { + return ( + + ) +} diff --git a/src/components/borrow/Table/Columns/useDepositedColumns.tsx b/src/components/borrow/Table/Columns/useDepositedColumns.tsx index fa657241..1a78f892 100644 --- a/src/components/borrow/Table/Columns/useDepositedColumns.tsx +++ b/src/components/borrow/Table/Columns/useDepositedColumns.tsx @@ -2,7 +2,10 @@ import { ColumnDef } from '@tanstack/react-table' import { useMemo } from 'react' import BorrowRate, { BORROW_RATE_META } from 'components/borrow/Table/Columns/BorrowRate' -import Debt, { DEBT_META, debtSortingFn } from 'components/borrow/Table/Columns/Debt' +import DebtValue, { + DEBT_VALUE_META, + debtSortingFn, +} from 'components/borrow/Table/Columns/DebtValue' import Liquidity, { LIQUIDITY_META, liquiditySortingFn, @@ -18,8 +21,10 @@ export default function useDepositedColumns() { cell: ({ row }) => , }, { - ...DEBT_META, - cell: ({ row }) => , + ...DEBT_VALUE_META, + cell: ({ row }) => ( + + ), sortingFn: debtSortingFn, }, { diff --git a/src/components/borrow/Table/DepositedBorrowingsTable.tsx b/src/components/borrow/Table/DepositedBorrowingsTable.tsx index 85b6fa96..78c12867 100644 --- a/src/components/borrow/Table/DepositedBorrowingsTable.tsx +++ b/src/components/borrow/Table/DepositedBorrowingsTable.tsx @@ -2,6 +2,7 @@ import { Row } from '@tanstack/react-table' import { useCallback } from 'react' import BorrowActionButtons from 'components/borrow/BorrowActionButtons' +import { DEBT_VALUE_META } from 'components/borrow/Table/Columns/DebtValue' import { NAME_META } from 'components/borrow/Table/Columns/Name' import useDepositedColumns from 'components/borrow/Table/Columns/useDepositedColumns' import MarketDetails from 'components/common/MarketDetails' @@ -11,6 +12,7 @@ import ActionButtonRow from 'components/common/Table/ActionButtonRow' type Props = { data: BorrowMarketTableData[] isLoading: boolean + v1?: boolean } export default function DepositedBorrowingsTable(props: Props) { @@ -32,10 +34,17 @@ export default function DepositedBorrowingsTable(props: Props) { return ( ) diff --git a/src/components/borrow/Table/useBorrowMarketAssetsTableData.ts b/src/components/borrow/Table/useBorrowMarketAssetsTableData.ts index d5650cc2..7ca53f0f 100644 --- a/src/components/borrow/Table/useBorrowMarketAssetsTableData.ts +++ b/src/components/borrow/Table/useBorrowMarketAssetsTableData.ts @@ -1,11 +1,14 @@ import { useMemo } from 'react' +import { BN_ZERO } from 'constants/math' import useCurrentAccount from 'hooks/accounts/useCurrentAccount' import useMarkets from 'hooks/markets/useMarkets' +import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice' export default function useBorrowMarketAssetsTableData() { const account = useCurrentAccount() const markets = useMarkets() + const { convertAmount } = useDisplayCurrencyPrice() return useMemo((): { accountBorrowedAssets: BorrowMarketTableData[] @@ -18,13 +21,16 @@ export default function useBorrowMarketAssetsTableData() { markets .filter((market) => market.borrowEnabled) .forEach((market) => { - const debt = account?.debts?.find((debt) => debt.denom === market.asset.denom) + const amount = + account?.debts?.find((debt) => debt.denom === market.asset.denom)?.amount ?? BN_ZERO + const value = amount ? convertAmount(market.asset, amount) : undefined const borrowMarketAsset: BorrowMarketTableData = { ...market, - accountDebt: debt?.amount, + accountDebtAmount: amount, + accountDebtValue: value, } - ;(borrowMarketAsset.accountDebt ? accountBorrowedAssets : availableAssets).push( + ;(borrowMarketAsset.accountDebtAmount ? accountBorrowedAssets : availableAssets).push( borrowMarketAsset, ) }) @@ -34,5 +40,5 @@ export default function useBorrowMarketAssetsTableData() { availableAssets, allAssets: [...accountBorrowedAssets, ...availableAssets], } - }, [account?.debts, markets]) + }, [account?.debts, markets, convertAmount]) } diff --git a/src/components/common/Intro.tsx b/src/components/common/Intro.tsx index 0a2d3bdc..41fdc893 100644 --- a/src/components/common/Intro.tsx +++ b/src/components/common/Intro.tsx @@ -17,7 +17,7 @@ import useStore from 'store' interface Props { text: string | ReactNode children?: ReactNode - bg: 'borrow' | 'lend' | 'farm' | 'portfolio' | 'hls-farm' | 'hls-staking' + bg: Page } function IntroBackground(props: { bg: Props['bg'] }) { diff --git a/src/components/earn/lend/Table/Columns/DepositValue.tsx b/src/components/earn/lend/Table/Columns/DepositValue.tsx index 54f5d33b..cfd2c504 100644 --- a/src/components/earn/lend/Table/Columns/DepositValue.tsx +++ b/src/components/earn/lend/Table/Columns/DepositValue.tsx @@ -5,6 +5,7 @@ import { BN_ZERO } from 'constants/math' import { BN } from 'utils/helpers' export const DEPOSIT_VALUE_META = { + id: 'accountLentValue', accessorKey: 'accountLentValue', header: 'Deposited', } diff --git a/src/components/earn/lend/Table/DepositedLendsTable.tsx b/src/components/earn/lend/Table/DepositedLendsTable.tsx index 42d37ade..7309ddc6 100644 --- a/src/components/earn/lend/Table/DepositedLendsTable.tsx +++ b/src/components/earn/lend/Table/DepositedLendsTable.tsx @@ -1,16 +1,18 @@ import { Row } from '@tanstack/react-table' import { useCallback } from 'react' -import LendingActionButtons from 'components/earn/lend/LendingActionButtons' -import { NAME_META } from 'components/earn/lend/Table/Columns/Name' -import useDepositedColumns from 'components/earn/lend/Table/Columns/useDepositedColumns' import MarketDetails from 'components/common/MarketDetails' import Table from 'components/common/Table' import ActionButtonRow from 'components/common/Table/ActionButtonRow' +import LendingActionButtons from 'components/earn/lend/LendingActionButtons' +import { DEPOSIT_VALUE_META } from 'components/earn/lend/Table/Columns/DepositValue' +import { NAME_META } from 'components/earn/lend/Table/Columns/Name' +import useDepositedColumns from 'components/earn/lend/Table/Columns/useDepositedColumns' type Props = { data: LendingMarketTableData[] isLoading: boolean + v1?: boolean } export default function DepositedLendsTable(props: Props) { @@ -32,10 +34,17 @@ export default function DepositedLendsTable(props: Props) { return (
) diff --git a/src/components/header/DesktopHeader.tsx b/src/components/header/DesktopHeader.tsx index e6b7b7f2..77ade9f4 100644 --- a/src/components/header/DesktopHeader.tsx +++ b/src/components/header/DesktopHeader.tsx @@ -40,6 +40,7 @@ export const menuTree = (walletId: WalletID, chainConfig: ChainConfig): MenuTree { pages: ['borrow'], label: 'Borrow' }, ...(chainConfig.hls ? [{ pages: ['hls-staking'] as Page[], label: 'High Leverage' }] : []), { pages: ['portfolio'], label: 'Portfolio' }, + { pages: ['v1'], label: 'V1' }, { pages: ['governance'], label: 'Governance', externalUrl: getGovernanceUrl(walletId) }, ] diff --git a/src/components/header/navigation/Routes.tsx b/src/components/header/navigation/Routes.tsx index 4c2eb851..901a7e08 100644 --- a/src/components/header/navigation/Routes.tsx +++ b/src/components/header/navigation/Routes.tsx @@ -12,6 +12,7 @@ import PerpsPage from 'pages/PerpsPage' import PortfolioAccountPage from 'pages/PortfolioAccountPage' import PortfolioPage from 'pages/PortfolioPage' import TradePage from 'pages/TradePage' +import V1Page from 'pages/V1Page' import Layout from 'pages/_layout' export default function Routes() { @@ -32,6 +33,7 @@ export default function Routes() { } /> } /> } /> + } /> } /> {chainConfig.hls && } />} {chainConfig.hls && } />} @@ -47,6 +49,7 @@ export default function Routes() { } /> {chainConfig.hls && } />} {chainConfig.hls && } />} + } /> } /> diff --git a/src/components/v1/Borrowings.tsx b/src/components/v1/Borrowings.tsx new file mode 100644 index 00000000..75f159b2 --- /dev/null +++ b/src/components/v1/Borrowings.tsx @@ -0,0 +1,47 @@ +import BorrowingsTable from 'components/borrow/Table/DepositedBorrowingsTable' +import useV1BorrowingsTableData from 'components/v1/Table/useV1BorrowingsTableData' +import { BN_ZERO } from 'constants/math' +import useBorrowEnabledAssets from 'hooks/assets/useBorrowEnabledAssets' + +export default function Borrowings() { + const { debtAssets } = useV1BorrowingsTableData() + + if (!debtAssets?.length) { + return + } + + return ( + <> + + + ) +} + +function Fallback() { + const assets = useBorrowEnabledAssets() + const data: BorrowMarketTableData[] = assets.map((asset) => ({ + asset, + apy: { + borrow: 0, + deposit: 0, + }, + ltv: { + max: 0, + liq: 0, + }, + liquidity: BN_ZERO, + marketLiquidityRate: 0, + cap: { + denom: asset.denom, + max: BN_ZERO, + used: BN_ZERO, + }, + debt: BN_ZERO, + borrowEnabled: true, + depositEnabled: true, + deposits: BN_ZERO, + accountDebt: BN_ZERO, + })) + + return +} diff --git a/src/components/v1/Deposits.tsx b/src/components/v1/Deposits.tsx new file mode 100644 index 00000000..997243bc --- /dev/null +++ b/src/components/v1/Deposits.tsx @@ -0,0 +1,48 @@ +import DepositsTable from 'components/earn/lend/Table/DepositedLendsTable' +import useV1DepositsTableData from 'components/v1/Table/useV1DepositsTableData' +import { BN_ZERO } from 'constants/math' +import useMarketEnabledAssets from 'hooks/assets/useMarketEnabledAssets' + +export default function Deposits() { + const { depositAssets } = useV1DepositsTableData() + + if (!depositAssets?.length) { + return + } + + console.log(depositAssets) + + return ( + <> + + + ) +} + +function Fallback() { + const assets = useMarketEnabledAssets() + + const data: LendingMarketTableData[] = assets.map((asset) => ({ + asset, + borrowEnabled: true, + depositEnabled: true, + debt: BN_ZERO, + deposits: BN_ZERO, + liquidity: BN_ZERO, + cap: { + max: BN_ZERO, + used: BN_ZERO, + denom: asset.denom, + }, + apy: { + borrow: 0, + deposit: 0, + }, + ltv: { + max: 0, + liq: 0, + }, + })) + + return +} diff --git a/src/components/v1/Table/useV1BorrowingsTableData.ts b/src/components/v1/Table/useV1BorrowingsTableData.ts new file mode 100644 index 00000000..01059116 --- /dev/null +++ b/src/components/v1/Table/useV1BorrowingsTableData.ts @@ -0,0 +1,38 @@ +import { useMemo } from 'react' + +import { BN_ZERO } from 'constants/math' +import useMarkets from 'hooks/markets/useMarkets' +import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice' +import useV1Positions from 'hooks/v1/useV1Positions' +import useStore from 'store' + +export default function useV1BorrowingsTableData() { + const address = useStore((s) => s.address) + const markets = useMarkets() + const { data: v1Positions } = useV1Positions(address) + const userDebts = v1Positions?.debts ?? [] + const { convertAmount } = useDisplayCurrencyPrice() + + return useMemo((): { + debtAssets: BorrowMarketTableData[] + } => { + const debtAssets: BorrowMarketTableData[] = [] + + markets + .filter((market) => market.borrowEnabled) + .forEach((market) => { + const amount = + userDebts.find((debt) => debt.denom === market.asset.denom)?.amount ?? BN_ZERO + const value = amount ? convertAmount(market.asset, amount) : undefined + + const borrowMarketAsset: BorrowMarketTableData = { + ...market, + accountDebtAmount: amount, + accountDebtValue: value, + } + debtAssets.push(borrowMarketAsset) + }) + + return { debtAssets } + }, [userDebts, markets, convertAmount]) +} diff --git a/src/components/v1/Table/useV1DepositsTableData.ts b/src/components/v1/Table/useV1DepositsTableData.ts new file mode 100644 index 00000000..720922b5 --- /dev/null +++ b/src/components/v1/Table/useV1DepositsTableData.ts @@ -0,0 +1,39 @@ +import { useMemo } from 'react' + +import { BN_ZERO } from 'constants/math' +import useMarkets from 'hooks/markets/useMarkets' +import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice' +import useV1Positions from 'hooks/v1/useV1Positions' +import useStore from 'store' +import { byDenom } from 'utils/array' + +export default function useV1DepositsTableData(): { + depositAssets: LendingMarketTableData[] +} { + const address = useStore((s) => s.address) + const markets = useMarkets() + const { data: v1Positions } = useV1Positions(address) + const userCollateral = v1Positions?.lends ?? [] + const { convertAmount } = useDisplayCurrencyPrice() + + return useMemo(() => { + const depositAssets: LendingMarketTableData[] = [] + + markets.forEach((market) => { + const amount = userCollateral.find(byDenom(market.asset.denom))?.amount ?? BN_ZERO + const value = amount ? convertAmount(market.asset, amount) : undefined + + const lendingMarketAsset: LendingMarketTableData = { + ...market, + accountLentValue: value, + accountLentAmount: amount, + } + + depositAssets.push(lendingMarketAsset) + }) + + return { + depositAssets, + } + }, [markets, userCollateral, convertAmount]) +} diff --git a/src/components/v1/V1Intro.tsx b/src/components/v1/V1Intro.tsx new file mode 100644 index 00000000..b14e9eec --- /dev/null +++ b/src/components/v1/V1Intro.tsx @@ -0,0 +1,24 @@ +import WalletConnectButton from 'components/Wallet/WalletConnectButton' +import Intro from 'components/common/Intro' +import useStore from 'store' + +export default function V1Intro() { + const address = useStore((state) => state.address) + return ( + + Welcome to the Red Bank! +
+ This is the first version (v1) of the Red Bank. It provides simple lending and borrowing, + without the use of Credit Accounts. Funds are{' '} + not cross-collateralized and can't be used on v2 as + collateral. + + } + bg='v1' + > + {!address && } +
+ ) +} diff --git a/src/hooks/v1/useV1Positions.tsx b/src/hooks/v1/useV1Positions.tsx new file mode 100644 index 00000000..70510ec6 --- /dev/null +++ b/src/hooks/v1/useV1Positions.tsx @@ -0,0 +1,17 @@ +import useSWR from 'swr' + +import getV1Positions from 'api/v1/getV1Positions' +import useChainConfig from 'hooks/useChainConfig' + +export default function useV1Positions(user?: string, suspense?: boolean) { + const chainConfig = useChainConfig() + + return useSWR( + user && `chains/${chainConfig.id}/v1/${user}`, + () => getV1Positions(chainConfig, user), + { + suspense: suspense, + revalidateOnFocus: false, + }, + ) +} diff --git a/src/pages/V1Page.tsx b/src/pages/V1Page.tsx new file mode 100644 index 00000000..f20b87cf --- /dev/null +++ b/src/pages/V1Page.tsx @@ -0,0 +1,15 @@ +import MigrationBanner from 'components/common/MigrationBanner' +import Borrowings from 'components/v1/Borrowings' +import Deposits from 'components/v1/Deposits' +import V1Intro from 'components/v1/V1Intro' + +export default function V1Page() { + return ( +
+ + + + +
+ ) +} diff --git a/src/types/interfaces/market.d.ts b/src/types/interfaces/market.d.ts index f96d2b47..9858f4b0 100644 --- a/src/types/interfaces/market.d.ts +++ b/src/types/interfaces/market.d.ts @@ -17,7 +17,8 @@ interface Market { } interface BorrowMarketTableData extends Market { - accountDebt?: BigNumber + accountDebtAmount?: BigNumber + accountDebtValue?: BigNumber } interface LendingMarketTableData extends Market { diff --git a/src/types/interfaces/route.d.ts b/src/types/interfaces/route.d.ts index 59fff097..cd4f5fca 100644 --- a/src/types/interfaces/route.d.ts +++ b/src/types/interfaces/route.d.ts @@ -11,6 +11,7 @@ type Page = | 'hls-staking' | 'governance' | 'execute' + | 'v1' type OsmosisRouteResponse = { amount_in: { diff --git a/src/types/interfaces/v1.d.ts b/src/types/interfaces/v1.d.ts new file mode 100644 index 00000000..e40c3902 --- /dev/null +++ b/src/types/interfaces/v1.d.ts @@ -0,0 +1,4 @@ +interface V1Positions { + deposits: BNCoin[] + debts: BNCoin[] +} From 34db8aea1a7a5335124c7fd3b7732b5cbfe3f8d4 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Wed, 14 Feb 2024 11:14:40 +0100 Subject: [PATCH 02/15] tidy: streamline borrow --- .../borrow/Table/useBorrowMarketAssetsTableData.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/borrow/Table/useBorrowMarketAssetsTableData.ts b/src/components/borrow/Table/useBorrowMarketAssetsTableData.ts index 7ca53f0f..e786a0c6 100644 --- a/src/components/borrow/Table/useBorrowMarketAssetsTableData.ts +++ b/src/components/borrow/Table/useBorrowMarketAssetsTableData.ts @@ -30,9 +30,12 @@ export default function useBorrowMarketAssetsTableData() { accountDebtAmount: amount, accountDebtValue: value, } - ;(borrowMarketAsset.accountDebtAmount ? accountBorrowedAssets : availableAssets).push( - borrowMarketAsset, - ) + + if (borrowMarketAsset.accountDebtAmount?.isZero()) { + availableAssets.push(borrowMarketAsset) + } else { + accountBorrowedAssets.push(borrowMarketAsset) + } }) return { From 106de661a8a569d5565ac139769fcb2c57482d41 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Thu, 15 Feb 2024 16:08:04 +0100 Subject: [PATCH 03/15] feat: added v1 based buttons and a portfolio summary --- src/components/borrow/Borrowings.tsx | 4 +- ...ngsTable.tsx => ActiveBorrowingsTable.tsx} | 6 +-- .../borrow/Table/Columns/BorrowButton.tsx | 1 + .../borrow/Table/Columns/Manage.tsx | 3 +- ...ositedColumns.tsx => useActiveColumns.tsx} | 12 +++-- src/components/common/MarketDetails.tsx | 2 +- .../Table/Columns/useAvailableColumns.tsx | 3 +- .../Table/Columns/useDepositedColumns.tsx | 7 ++- .../earn/lend/Table/DepositedLendsTable.tsx | 2 +- src/components/portfolio/Account/Summary.tsx | 7 +-- src/components/v1/Borrowings.tsx | 2 +- .../v1/Table/borrowings/Columns/Action.tsx | 20 ++++++++ .../v1/Table/deposits/Columns/Action.tsx | 19 ++++++++ .../Table/deposits/Columns/DepositButton.tsx | 48 +++++++++++++++++++ .../v1/Table/deposits/Columns/Manage.tsx | 45 +++++++++++++++++ .../v1/Table/useV1BorrowingsTableData.ts | 8 ++-- .../v1/Table/useV1DepositsTableData.ts | 8 ++-- src/components/v1/V1Intro.tsx | 4 +- src/hooks/accounts/useAccount.tsx | 10 +++- src/hooks/v1/useV1Positions.tsx | 17 ------- src/pages/V1Page.tsx | 4 ++ 21 files changed, 184 insertions(+), 48 deletions(-) rename src/components/borrow/Table/{DepositedBorrowingsTable.tsx => ActiveBorrowingsTable.tsx} (83%) rename src/components/borrow/Table/Columns/{useDepositedColumns.tsx => useActiveColumns.tsx} (83%) create mode 100644 src/components/v1/Table/borrowings/Columns/Action.tsx create mode 100644 src/components/v1/Table/deposits/Columns/Action.tsx create mode 100644 src/components/v1/Table/deposits/Columns/DepositButton.tsx create mode 100644 src/components/v1/Table/deposits/Columns/Manage.tsx delete mode 100644 src/hooks/v1/useV1Positions.tsx diff --git a/src/components/borrow/Borrowings.tsx b/src/components/borrow/Borrowings.tsx index 4f197270..5cb6d42c 100644 --- a/src/components/borrow/Borrowings.tsx +++ b/src/components/borrow/Borrowings.tsx @@ -1,5 +1,5 @@ +import ActiveBorrowingsTable from 'components/borrow/Table/ActiveBorrowingsTable' import AvailableBorrowingsTable from 'components/borrow/Table/AvailableBorrowingsTable' -import DepositedBorrowingsTable from 'components/borrow/Table/DepositedBorrowingsTable' import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData' import { BN_ZERO } from 'constants/math' import useBorrowEnabledAssets from 'hooks/assets/useBorrowEnabledAssets' @@ -12,7 +12,7 @@ export default function Borrowings() { } return ( <> - + ) diff --git a/src/components/borrow/Table/DepositedBorrowingsTable.tsx b/src/components/borrow/Table/ActiveBorrowingsTable.tsx similarity index 83% rename from src/components/borrow/Table/DepositedBorrowingsTable.tsx rename to src/components/borrow/Table/ActiveBorrowingsTable.tsx index a90261a1..605fb8fa 100644 --- a/src/components/borrow/Table/DepositedBorrowingsTable.tsx +++ b/src/components/borrow/Table/ActiveBorrowingsTable.tsx @@ -3,7 +3,7 @@ import { useCallback } from 'react' import { DEBT_VALUE_META } from 'components/borrow/Table/Columns/DebtValue' import { NAME_META } from 'components/borrow/Table/Columns/Name' -import useDepositedColumns from 'components/borrow/Table/Columns/useDepositedColumns' +import useBorrowingsColumns from 'components/borrow/Table/Columns/useActiveColumns' import MarketDetails from 'components/common/MarketDetails' import Table from 'components/common/Table' @@ -13,8 +13,8 @@ type Props = { v1?: boolean } -export default function DepositedBorrowingsTable(props: Props) { - const columns = useDepositedColumns() +export default function ActiveBorrowingsTable(props: Props) { + const columns = useBorrowingsColumns({ v1: props.v1 }) const renderExpanded = useCallback((row: Row) => { return diff --git a/src/components/borrow/Table/Columns/BorrowButton.tsx b/src/components/borrow/Table/Columns/BorrowButton.tsx index 421a277d..8368bd83 100644 --- a/src/components/borrow/Table/Columns/BorrowButton.tsx +++ b/src/components/borrow/Table/Columns/BorrowButton.tsx @@ -16,6 +16,7 @@ export const BORROW_BUTTON_META = { interface Props { data: LendingMarketTableData + v1?: boolean } export default function BorrowButton(props: Props) { const account = useCurrentAccount() diff --git a/src/components/borrow/Table/Columns/Manage.tsx b/src/components/borrow/Table/Columns/Manage.tsx index c7112d06..59adbec6 100644 --- a/src/components/borrow/Table/Columns/Manage.tsx +++ b/src/components/borrow/Table/Columns/Manage.tsx @@ -12,6 +12,7 @@ export const MANAGE_META = { interface Props { data: BorrowMarketTableData + v1?: boolean } export default function Manage(props: Props) { @@ -48,7 +49,7 @@ export default function Manage(props: Props) { if (!address) return null return ( -
+
) diff --git a/src/components/borrow/Table/Columns/useDepositedColumns.tsx b/src/components/borrow/Table/Columns/useActiveColumns.tsx similarity index 83% rename from src/components/borrow/Table/Columns/useDepositedColumns.tsx rename to src/components/borrow/Table/Columns/useActiveColumns.tsx index 4fcc49c9..5cc6633f 100644 --- a/src/components/borrow/Table/Columns/useDepositedColumns.tsx +++ b/src/components/borrow/Table/Columns/useActiveColumns.tsx @@ -13,8 +13,13 @@ import Liquidity, { } from 'components/borrow/Table/Columns/Liquidity' import Manage, { MANAGE_META } from 'components/borrow/Table/Columns/Manage' import Name, { NAME_META } from 'components/borrow/Table/Columns/Name' +import Action from 'components/v1/Table/borrowings/Columns/Action' -export default function useDepositedColumns() { +interface Props { + v1?: boolean +} + +export default function useActiveColumns(props: Props) { return useMemo[]>(() => { return [ { @@ -39,12 +44,13 @@ export default function useDepositedColumns() { }, { ...MANAGE_META, - cell: ({ row }) => , + cell: ({ row }) => + props.v1 ? : , }, { ...CHEVRON_META, cell: ({ row }) => , }, ] - }, []) + }, [props.v1]) } diff --git a/src/components/common/MarketDetails.tsx b/src/components/common/MarketDetails.tsx index 545c3492..f06114fc 100644 --- a/src/components/common/MarketDetails.tsx +++ b/src/components/common/MarketDetails.tsx @@ -23,7 +23,7 @@ export default function MarketDetails({ row, type }: Props) { symbol: displayCurrencySymbol, } = useDisplayCurrencyPrice() - const { asset, ltv, cap, liquidity, deposits, debt } = row.original + const { asset, ltv, deposits, debt } = row.original const details: Detail[] = useMemo(() => { const isDollar = displayCurrencySymbol === '$' diff --git a/src/components/earn/farm/Table/Columns/useAvailableColumns.tsx b/src/components/earn/farm/Table/Columns/useAvailableColumns.tsx index 8269d7d1..ce5560ee 100644 --- a/src/components/earn/farm/Table/Columns/useAvailableColumns.tsx +++ b/src/components/earn/farm/Table/Columns/useAvailableColumns.tsx @@ -2,6 +2,7 @@ import { ColumnDef } from '@tanstack/react-table' import { useMemo } from 'react' import Apy, { APY_META } from 'components/earn/farm/Table/Columns/Apy' +import { Deposit, DEPOSIT_META } from 'components/earn/farm/Table/Columns/Deposit' import DepositCap, { DEPOSIT_CAP_META, depositCapSortingFn, @@ -10,8 +11,6 @@ import MaxLTV, { LTV_MAX_META } from 'components/earn/farm/Table/Columns/MaxLTV' import Name, { NAME_META } from 'components/earn/farm/Table/Columns/Name' import TVL, { TVL_META } from 'components/earn/farm/Table/Columns/TVL' -import { Deposit, DEPOSIT_META } from './Deposit' - interface Props { isLoading: boolean } diff --git a/src/components/earn/lend/Table/Columns/useDepositedColumns.tsx b/src/components/earn/lend/Table/Columns/useDepositedColumns.tsx index d39702fe..638888fe 100644 --- a/src/components/earn/lend/Table/Columns/useDepositedColumns.tsx +++ b/src/components/earn/lend/Table/Columns/useDepositedColumns.tsx @@ -13,9 +13,11 @@ import DepositValue, { } from 'components/earn/lend/Table/Columns/DepositValue' import Manage, { MANAGE_META } from 'components/earn/lend/Table/Columns/Manage' import Name, { NAME_META } from 'components/earn/lend/Table/Columns/Name' +import Action from 'components/v1/Table/deposits/Columns/Action' interface Props { isLoading: boolean + v1?: boolean } export default function useDepositedColumns(props: Props) { @@ -49,12 +51,13 @@ export default function useDepositedColumns(props: Props) { }, { ...MANAGE_META, - cell: ({ row }) => , + cell: ({ row }) => + props.v1 ? : , }, { ...CHEVRON_META, cell: ({ row }) => , }, ] - }, [props.isLoading]) + }, [props.isLoading, props.v1]) } diff --git a/src/components/earn/lend/Table/DepositedLendsTable.tsx b/src/components/earn/lend/Table/DepositedLendsTable.tsx index 23aaa2b4..d9ce9b68 100644 --- a/src/components/earn/lend/Table/DepositedLendsTable.tsx +++ b/src/components/earn/lend/Table/DepositedLendsTable.tsx @@ -14,7 +14,7 @@ type Props = { } export default function DepositedLendsTable(props: Props) { - const columns = useDepositedColumns({ isLoading: props.isLoading }) + const columns = useDepositedColumns({ isLoading: props.isLoading, v1: props.v1 }) const renderExpanded = useCallback( (row: Row) => , diff --git a/src/components/portfolio/Account/Summary.tsx b/src/components/portfolio/Account/Summary.tsx index 3818afa6..e6f5fdf2 100644 --- a/src/components/portfolio/Account/Summary.tsx +++ b/src/components/portfolio/Account/Summary.tsx @@ -17,6 +17,7 @@ import { DEFAULT_PORTFOLIO_STATS } from 'utils/constants' interface Props { accountId: string + v1?: boolean } function Content(props: Props) { @@ -78,17 +79,17 @@ function Content(props: Props) { options={{ suffix: 'x' }} /> ), - sub: DEFAULT_PORTFOLIO_STATS[4].sub, + sub: props.v1 ? 'Total Leverage' : DEFAULT_PORTFOLIO_STATS[4].sub, }, ] - }, [account, assets, borrowAssets, hlsStrategies, lendingAssets, prices, vaultAprs]) + }, [account, assets, borrowAssets, hlsStrategies, lendingAssets, prices, vaultAprs, props.v1]) return ( ) diff --git a/src/components/v1/Borrowings.tsx b/src/components/v1/Borrowings.tsx index 75f159b2..b480f899 100644 --- a/src/components/v1/Borrowings.tsx +++ b/src/components/v1/Borrowings.tsx @@ -1,4 +1,4 @@ -import BorrowingsTable from 'components/borrow/Table/DepositedBorrowingsTable' +import BorrowingsTable from 'components/borrow/Table/ActiveBorrowingsTable' import useV1BorrowingsTableData from 'components/v1/Table/useV1BorrowingsTableData' import { BN_ZERO } from 'constants/math' import useBorrowEnabledAssets from 'hooks/assets/useBorrowEnabledAssets' diff --git a/src/components/v1/Table/borrowings/Columns/Action.tsx b/src/components/v1/Table/borrowings/Columns/Action.tsx new file mode 100644 index 00000000..2de24f32 --- /dev/null +++ b/src/components/v1/Table/borrowings/Columns/Action.tsx @@ -0,0 +1,20 @@ +import BorrowButton from 'components/borrow/Table/Columns/BorrowButton' +import Manage from 'components/borrow/Table/Columns/Manage' + +export const MANAGE_META = { + accessorKey: 'manage', + enableSorting: false, + header: '', +} + +interface Props { + data: BorrowMarketTableData +} + +export default function Action(props: Props) { + const hasDebt = !props.data.accountDebtAmount?.isZero() ?? false + + if (hasDebt) return + + return +} diff --git a/src/components/v1/Table/deposits/Columns/Action.tsx b/src/components/v1/Table/deposits/Columns/Action.tsx new file mode 100644 index 00000000..b08f2c52 --- /dev/null +++ b/src/components/v1/Table/deposits/Columns/Action.tsx @@ -0,0 +1,19 @@ +import DepositButton from 'components/v1/Table/deposits/Columns/DepositButton' +import Manage from 'components/v1/Table/deposits/Columns/Manage' + +export const MANAGE_META = { + accessorKey: 'manage', + enableSorting: false, + header: '', +} + +interface Props { + data: LendingMarketTableData +} +export default function Action(props: Props) { + const hasDeposits = !props.data.accountLentAmount?.isZero() ?? false + + if (hasDeposits) return + + return +} diff --git a/src/components/v1/Table/deposits/Columns/DepositButton.tsx b/src/components/v1/Table/deposits/Columns/DepositButton.tsx new file mode 100644 index 00000000..26c9e8d9 --- /dev/null +++ b/src/components/v1/Table/deposits/Columns/DepositButton.tsx @@ -0,0 +1,48 @@ +import ActionButton from 'components/common/Button/ActionButton' +import { ArrowUpLine } from 'components/common/Icons' +import Text from 'components/common/Text' +import { Tooltip } from 'components/common/Tooltip' +import ConditionalWrapper from 'hocs/ConditionalWrapper' +import useWalletBalances from 'hooks/useWalletBalances' +import useStore from 'store' +import { byDenom } from 'utils/array' + +interface Props { + data: LendingMarketTableData +} +export default function DepositButton(props: Props) { + const address = useStore((s) => s.address) + const { data: balances } = useWalletBalances(address) + const hasBalance = !!balances.find(byDenom(props.data.asset.denom)) + + return ( +
+ ( + {`You don’t have any ${props.data.asset.symbol} in your Wallet.`} + } + contentClassName='max-w-[200px]' + className='ml-auto' + > + {children} + + )} + > + } + disabled={!hasBalance} + color='tertiary' + onClick={(e) => { + useStore.setState({ fundAndWithdrawModal: 'fund' }) + e.stopPropagation() + }} + text='Deposit' + /> + +
+ ) +} diff --git a/src/components/v1/Table/deposits/Columns/Manage.tsx b/src/components/v1/Table/deposits/Columns/Manage.tsx new file mode 100644 index 00000000..28ca2bef --- /dev/null +++ b/src/components/v1/Table/deposits/Columns/Manage.tsx @@ -0,0 +1,45 @@ +import { useMemo } from 'react' + +import DropDownButton from 'components/common/Button/DropDownButton' +import { ArrowDownLine, ArrowUpLine } from 'components/common/Icons' +import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal' +import useWalletBalances from 'hooks/useWalletBalances' +import useStore from 'store' +import { byDenom } from 'utils/array' + +interface Props { + data: LendingMarketTableData +} + +export default function Manage(props: Props) { + const { openLend, openReclaim } = useLendAndReclaimModal() + const address = useStore((s) => s.address) + const { data: balances } = useWalletBalances(address) + const hasBalance = !!balances.find(byDenom(props.data.asset.denom)) + + console.log(balances) + + const ITEMS: DropDownItem[] = useMemo( + () => [ + { + icon: , + text: 'Deposit more', + onClick: () => openLend(props.data), + disabled: !hasBalance, + disabledTooltip: `You don’t have any ${props.data.asset.symbol} in your Wallet.`, + }, + { + icon: , + text: 'Withdraw', + onClick: () => openReclaim(props.data), + }, + ], + [hasBalance, openLend, openReclaim, props.data], + ) + + return ( +
+ +
+ ) +} diff --git a/src/components/v1/Table/useV1BorrowingsTableData.ts b/src/components/v1/Table/useV1BorrowingsTableData.ts index 01059116..62b544d3 100644 --- a/src/components/v1/Table/useV1BorrowingsTableData.ts +++ b/src/components/v1/Table/useV1BorrowingsTableData.ts @@ -1,21 +1,21 @@ import { useMemo } from 'react' import { BN_ZERO } from 'constants/math' +import useAccount from 'hooks/accounts/useAccount' import useMarkets from 'hooks/markets/useMarkets' import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice' -import useV1Positions from 'hooks/v1/useV1Positions' import useStore from 'store' export default function useV1BorrowingsTableData() { const address = useStore((s) => s.address) const markets = useMarkets() - const { data: v1Positions } = useV1Positions(address) - const userDebts = v1Positions?.debts ?? [] + const { data: v1Positions } = useAccount(address) const { convertAmount } = useDisplayCurrencyPrice() return useMemo((): { debtAssets: BorrowMarketTableData[] } => { + const userDebts = v1Positions?.debts ?? [] const debtAssets: BorrowMarketTableData[] = [] markets @@ -34,5 +34,5 @@ export default function useV1BorrowingsTableData() { }) return { debtAssets } - }, [userDebts, markets, convertAmount]) + }, [v1Positions, markets, convertAmount]) } diff --git a/src/components/v1/Table/useV1DepositsTableData.ts b/src/components/v1/Table/useV1DepositsTableData.ts index 720922b5..3224a224 100644 --- a/src/components/v1/Table/useV1DepositsTableData.ts +++ b/src/components/v1/Table/useV1DepositsTableData.ts @@ -1,9 +1,9 @@ import { useMemo } from 'react' import { BN_ZERO } from 'constants/math' +import useAccount from 'hooks/accounts/useAccount' import useMarkets from 'hooks/markets/useMarkets' import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice' -import useV1Positions from 'hooks/v1/useV1Positions' import useStore from 'store' import { byDenom } from 'utils/array' @@ -12,12 +12,12 @@ export default function useV1DepositsTableData(): { } { const address = useStore((s) => s.address) const markets = useMarkets() - const { data: v1Positions } = useV1Positions(address) - const userCollateral = v1Positions?.lends ?? [] + const { data: v1Positions } = useAccount(address) const { convertAmount } = useDisplayCurrencyPrice() return useMemo(() => { const depositAssets: LendingMarketTableData[] = [] + const userCollateral = v1Positions?.lends ?? [] markets.forEach((market) => { const amount = userCollateral.find(byDenom(market.asset.denom))?.amount ?? BN_ZERO @@ -35,5 +35,5 @@ export default function useV1DepositsTableData(): { return { depositAssets, } - }, [markets, userCollateral, convertAmount]) + }, [markets, v1Positions, convertAmount]) } diff --git a/src/components/v1/V1Intro.tsx b/src/components/v1/V1Intro.tsx index b14e9eec..f8212b8a 100644 --- a/src/components/v1/V1Intro.tsx +++ b/src/components/v1/V1Intro.tsx @@ -12,8 +12,8 @@ export default function V1Intro() {
This is the first version (v1) of the Red Bank. It provides simple lending and borrowing, without the use of Credit Accounts. Funds are{' '} - not cross-collateralized and can't be used on v2 as - collateral. + not cross-collateralized and can‘t be used on v2 + as collateral. } bg='v1' diff --git a/src/hooks/accounts/useAccount.tsx b/src/hooks/accounts/useAccount.tsx index 980cc590..2973d98e 100644 --- a/src/hooks/accounts/useAccount.tsx +++ b/src/hooks/accounts/useAccount.tsx @@ -1,14 +1,20 @@ import useSWR from 'swr' import getAccount from 'api/accounts/getAccount' +import getV1Positions from 'api/v1/getV1Positions' import useChainConfig from 'hooks/useChainConfig' export default function useAccount(accountId?: string, suspense?: boolean) { const chainConfig = useChainConfig() + const isV1 = isNaN(parseInt(accountId || '')) + + const cacheKey = isV1 + ? `chains/${chainConfig.id}/v1/user/${accountId}` + : `chains/${chainConfig.id}/accounts/${accountId}` return useSWR( - accountId && `chains/${chainConfig.id}/accounts/${accountId}`, - () => getAccount(chainConfig, accountId), + accountId && cacheKey, + () => (isV1 ? getV1Positions(chainConfig, accountId) : getAccount(chainConfig, accountId)), { suspense: suspense, revalidateOnFocus: false, diff --git a/src/hooks/v1/useV1Positions.tsx b/src/hooks/v1/useV1Positions.tsx deleted file mode 100644 index 70510ec6..00000000 --- a/src/hooks/v1/useV1Positions.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import useSWR from 'swr' - -import getV1Positions from 'api/v1/getV1Positions' -import useChainConfig from 'hooks/useChainConfig' - -export default function useV1Positions(user?: string, suspense?: boolean) { - const chainConfig = useChainConfig() - - return useSWR( - user && `chains/${chainConfig.id}/v1/${user}`, - () => getV1Positions(chainConfig, user), - { - suspense: suspense, - revalidateOnFocus: false, - }, - ) -} diff --git a/src/pages/V1Page.tsx b/src/pages/V1Page.tsx index f20b87cf..f6fb5330 100644 --- a/src/pages/V1Page.tsx +++ b/src/pages/V1Page.tsx @@ -1,13 +1,17 @@ import MigrationBanner from 'components/common/MigrationBanner' +import Summary from 'components/portfolio/Account/Summary' import Borrowings from 'components/v1/Borrowings' import Deposits from 'components/v1/Deposits' import V1Intro from 'components/v1/V1Intro' +import useStore from 'store' export default function V1Page() { + const address = useStore((s) => s.address) return (
+ {address && }
From f609540809c789adfa07e9c2a7138fb2b22d66a6 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Thu, 15 Feb 2024 16:22:20 +0100 Subject: [PATCH 04/15] feat: prepared everything for modal interaction --- .../account/AccountDetails/index.tsx | 8 +++--- src/components/common/Background.tsx | 26 ++++++++++++++----- src/components/header/DesktopHeader.tsx | 4 ++- src/components/v1/Deposits.tsx | 2 -- .../v1/Table/deposits/Columns/Manage.tsx | 2 -- src/store/slices/common.ts | 1 + src/types/interfaces/store/common.d.ts | 1 + src/utils/route.ts | 1 + tailwind.config.js | 4 +++ 9 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/components/account/AccountDetails/index.tsx b/src/components/account/AccountDetails/index.tsx index 1ac488f9..e0ad160b 100644 --- a/src/components/account/AccountDetails/index.tsx +++ b/src/components/account/AccountDetails/index.tsx @@ -37,6 +37,7 @@ import { export default function AccountDetailsController() { const address = useStore((s) => s.address) const isHLS = useStore((s) => s.isHLS) + const isV1 = useStore((s) => s.isV1) const { data: _, isLoading } = useAccounts('default', address) const { data: accountIds } = useAccountIds(address, false, true) @@ -45,10 +46,11 @@ export default function AccountDetailsController() { const account = useCurrentAccount() const focusComponent = useStore((s) => s.focusComponent) const isOwnAccount = accountId && accountIds?.includes(accountId) + const hideAccountDetails = !address || focusComponent || !isOwnAccount || isHLS || isV1 + const isLoadingAccountDetails = (isLoading && accountId && !focusComponent) || !account - if (!address || focusComponent || !isOwnAccount || isHLS) return null - - if ((isLoading && accountId && !focusComponent) || !account) return + if (hideAccountDetails) return null + if (isLoadingAccountDetails) return return } diff --git a/src/components/common/Background.tsx b/src/components/common/Background.tsx index 54ef2924..3bdca489 100644 --- a/src/components/common/Background.tsx +++ b/src/components/common/Background.tsx @@ -15,11 +15,23 @@ export default function Background() { ) const { pathname } = useLocation() const page = getPage(pathname) - const isHLS = useMemo(() => page.split('-')[0] === 'hls', [page]) + const [isHLS, isV1] = useMemo(() => [page.split('-')[0] === 'hls', page === 'v1'], [page]) useEffect(() => { - useStore.setState({ isHLS }) - }, [isHLS]) + useStore.setState({ isHLS: isHLS, isV1: isV1 }) + }, [isHLS, isV1]) + + const [primaryOrbClassName, secondaryOrbClassName, tertiaryOrbClassName, bodyClassName] = + useMemo(() => { + if (isHLS) { + return ['bg-orb-primary-hls', 'bg-orb-secondary-hls', 'bg-orb-tertiary-hls', 'bg-body-hls'] + } + if (isV1) { + return ['bg-orb-primary-v1', 'bg-orb-secondary-v1', 'bg-orb-tertiary-v1', 'bg-body-v1'] + } + + return ['bg-orb-primary', 'bg-orb-secondary', 'bg-orb-tertiary', 'bg-body'] + }, [isHLS, isV1]) return (
@@ -39,7 +51,7 @@ export default function Background() { 'max-h-[500px] max-w-[500px]', 'left-[-10vw] top-[-10vw]', 'blur-orb-primary', - isHLS ? ' bg-orb-primary-hls' : 'bg-orb-primary', + primaryOrbClassName, 'translate-x-0 translate-y-0 rounded-full opacity-20', !reduceMotion && 'animate-[float_120s_ease-in-out_infinite_2s]', !reduceMotion && 'transition-bg duration-1000 delay-300', @@ -53,7 +65,7 @@ export default function Background() { 'max-h-[1000px] max-w-[1000px]', 'bottom-[-20vw] right-[-10vw]', 'blur-orb-secondary', - isHLS ? ' bg-orb-secondary-hls' : 'bg-orb-secondary', + secondaryOrbClassName, 'translate-x-0 translate-y-0 rounded-full opacity-30', !reduceMotion && 'transition-bg duration-1000 delay-300', )} @@ -66,7 +78,7 @@ export default function Background() { 'max-h-[600px] max-w-[600px]', 'right-[-4vw] top-[-10vw]', 'blur-orb-tertiary ', - isHLS ? ' bg-orb-tertiary-hls' : 'bg-orb-tertiary', + tertiaryOrbClassName, 'translate-x-0 translate-y-0 rounded-full opacity-20', !reduceMotion && 'animate-[float_180s_ease-in_infinite]', !reduceMotion && 'transition-bg duration-1000 delay-300', diff --git a/src/components/header/DesktopHeader.tsx b/src/components/header/DesktopHeader.tsx index 77ade9f4..493e6f73 100644 --- a/src/components/header/DesktopHeader.tsx +++ b/src/components/header/DesktopHeader.tsx @@ -49,7 +49,9 @@ export default function DesktopHeader() { const focusComponent = useStore((s) => s.focusComponent) const isOracleStale = useStore((s) => s.isOracleStale) const isHLS = useStore((s) => s.isHLS) + const isV1 = useStore((s) => s.isV1) const accountId = useAccountId() + const showAccountMenu = address && !isHLS && !isV1 function handleCloseFocusMode() { if (focusComponent && focusComponent.onClose) focusComponent.onClose() @@ -93,7 +95,7 @@ export default function DesktopHeader() {
{showStaleOracle && } {accountId && } - {address && !isHLS && } + {showAccountMenu && } diff --git a/src/components/v1/Deposits.tsx b/src/components/v1/Deposits.tsx index 997243bc..4fc025af 100644 --- a/src/components/v1/Deposits.tsx +++ b/src/components/v1/Deposits.tsx @@ -10,8 +10,6 @@ export default function Deposits() { return } - console.log(depositAssets) - return ( <> diff --git a/src/components/v1/Table/deposits/Columns/Manage.tsx b/src/components/v1/Table/deposits/Columns/Manage.tsx index 28ca2bef..51c447a7 100644 --- a/src/components/v1/Table/deposits/Columns/Manage.tsx +++ b/src/components/v1/Table/deposits/Columns/Manage.tsx @@ -17,8 +17,6 @@ export default function Manage(props: Props) { const { data: balances } = useWalletBalances(address) const hasBalance = !!balances.find(byDenom(props.data.asset.denom)) - console.log(balances) - const ITEMS: DropDownItem[] = useMemo( () => [ { diff --git a/src/store/slices/common.ts b/src/store/slices/common.ts index eee116cc..f8ffb448 100644 --- a/src/store/slices/common.ts +++ b/src/store/slices/common.ts @@ -19,5 +19,6 @@ export default function createCommonSlice(set: SetState, get: GetSt useAutoRepay: true, isOracleStale: false, isHLS: false, + isV1: false, } } diff --git a/src/types/interfaces/store/common.d.ts b/src/types/interfaces/store/common.d.ts index 93d61291..9c9122cd 100644 --- a/src/types/interfaces/store/common.d.ts +++ b/src/types/interfaces/store/common.d.ts @@ -18,6 +18,7 @@ interface CommonSlice { useAutoRepay: boolean isOracleStale: boolean isHLS: boolean + isV1: boolean } interface FocusComponent { diff --git a/src/utils/route.ts b/src/utils/route.ts index aae1b19e..723cc922 100644 --- a/src/utils/route.ts +++ b/src/utils/route.ts @@ -40,6 +40,7 @@ export function getPage(pathname: string): Page { 'portfolio', 'hls-farm', 'hls-staking', + 'v1', ] const segments = pathname.split('/') diff --git a/tailwind.config.js b/tailwind.config.js index 321717d1..1bc2f416 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -102,6 +102,7 @@ module.exports = { axlusdc: '#478edc', body: '#0D0012', 'body-hls': '#090000', + 'body-v1': '#10000a', 'body-dark': '#141621', chart: '#220e1d', error: '#F04438', @@ -123,10 +124,13 @@ module.exports = { osmo: '#9f1ab9', 'orb-primary': '#b12f25', 'orb-primary-hls': '#FF645F', + 'orb-primary-v1': '#612e4d', 'orb-secondary': '#530781', 'orb-secondary-hls': '#a03b45', + 'orb-secondary-v1': '#692f55', 'orb-tertiary': '#ff00c7', 'orb-tertiary-hls': '#FB9562', + 'orb-tertiary-v1': '#993878', profit: '#4CA30D', primary: '#FF625E', secondary: '#FB9562', From 37be3240ebe940c92fadfe9ade69444887dc327d Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Thu, 15 Feb 2024 20:40:22 +0100 Subject: [PATCH 05/15] feat: finished deposit and withdraw --- .../Modals/AssetAmountSelectActionModal.tsx | 5 +- .../Modals/LendAndReclaim/index.tsx | 4 + src/components/Modals/ModalsContainer.tsx | 2 + .../Modals/V1DepositAndWithdraw/Deposit.tsx | 83 +++++++++++++++++++ .../Modals/V1DepositAndWithdraw/Withdraw.tsx | 67 +++++++++++++++ .../Modals/V1DepositAndWithdraw/index.tsx | 16 ++++ src/components/Modals/index.tsx | 5 +- src/components/Wallet/RecentTransactions.tsx | 6 +- src/components/common/Toaster/index.tsx | 11 ++- .../Table/deposits/Columns/DepositButton.tsx | 4 +- .../v1/Table/deposits/Columns/Manage.tsx | 12 ++- src/hooks/accounts/useAccount.tsx | 4 +- src/store/slices/broadcast.ts | 67 ++++++++++++++- src/store/slices/modal.ts | 1 + src/types/interfaces/store/broadcast.d.ts | 3 + src/types/interfaces/store/modals.d.ts | 6 ++ 16 files changed, 281 insertions(+), 15 deletions(-) create mode 100644 src/components/Modals/V1DepositAndWithdraw/Deposit.tsx create mode 100644 src/components/Modals/V1DepositAndWithdraw/Withdraw.tsx create mode 100644 src/components/Modals/V1DepositAndWithdraw/index.tsx diff --git a/src/components/Modals/AssetAmountSelectActionModal.tsx b/src/components/Modals/AssetAmountSelectActionModal.tsx index e0b2f05a..6dbdd83c 100644 --- a/src/components/Modals/AssetAmountSelectActionModal.tsx +++ b/src/components/Modals/AssetAmountSelectActionModal.tsx @@ -10,12 +10,12 @@ import Text from 'components/common/Text' import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider' import AssetImage from 'components/common/assets/AssetImage' import { BN_ZERO } from 'constants/math' -import useCurrentAccount from 'hooks/accounts/useCurrentAccount' import { BNCoin } from 'types/classes/BNCoin' import { byDenom } from 'utils/array' import { BN } from 'utils/helpers' interface Props { + account: Account asset: Asset title: string coinBalances: BNCoin[] @@ -29,6 +29,7 @@ interface Props { export default function AssetAmountSelectActionModal(props: Props) { const { + account, asset, title, coinBalances, @@ -41,7 +42,6 @@ export default function AssetAmountSelectActionModal(props: Props) { } = props const [amount, setAmount] = useState(BN_ZERO) const maxAmount = BN(coinBalances.find(byDenom(asset.denom))?.amount ?? 0) - const account = useCurrentAccount() const handleAmountChange = useCallback( (value: BigNumber) => { setAmount(value) @@ -54,7 +54,6 @@ export default function AssetAmountSelectActionModal(props: Props) { onAction(amount, amount.isEqualTo(maxAmount)) }, [amount, maxAmount, onAction]) - if (!account) return return ( s.lend) const reclaim = useStore((s) => s.reclaim) const { close } = useLendAndReclaimModal() @@ -65,8 +66,11 @@ function LendAndReclaimModal({ currentAccount, config }: Props) { }, [asset.denom, close, currentAccount.id, isLendAction, lend, reclaim], ) + if (!account) return null + return ( } coinBalances={coinBalances} diff --git a/src/components/Modals/ModalsContainer.tsx b/src/components/Modals/ModalsContainer.tsx index 01471b9d..ae749fa7 100644 --- a/src/components/Modals/ModalsContainer.tsx +++ b/src/components/Modals/ModalsContainer.tsx @@ -10,6 +10,7 @@ import { LendAndReclaimModalController, SettingsModal, UnlockModal, + V1DepositAndWithdraw, VaultModal, WalletAssets, WithdrawFromVaultsModal, @@ -32,6 +33,7 @@ export default function ModalsContainer() { + ) } diff --git a/src/components/Modals/V1DepositAndWithdraw/Deposit.tsx b/src/components/Modals/V1DepositAndWithdraw/Deposit.tsx new file mode 100644 index 00000000..b2849729 --- /dev/null +++ b/src/components/Modals/V1DepositAndWithdraw/Deposit.tsx @@ -0,0 +1,83 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' + +import WalletBridges from 'components/Wallet/WalletBridges' +import { BN_ZERO } from 'constants/math' +import useBaseAsset from 'hooks/assets/useBasetAsset' +import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance' +import { useUpdatedAccount } from 'hooks/useUpdatedAccount' +import useWalletBalances from 'hooks/useWalletBalances' +import useStore from 'store' +import { BNCoin } from 'types/classes/BNCoin' +import { byDenom } from 'utils/array' +import { defaultFee } from 'utils/constants' +import { BN } from 'utils/helpers' + +import AssetAmountSelectActionModal from '../AssetAmountSelectActionModal' +import DetailsHeader from '../LendAndReclaim/DetailsHeader' + +interface Props { + account: Account +} + +export default function Deposit(props: Props) { + const { account } = props + const baseAsset = useBaseAsset() + const modal = useStore((s) => s.v1DepositAndWithdrawModal) + const address = useStore((s) => s.address) + const asset = modal?.data.asset ?? baseAsset + const [fundingAsset, setFundingAsset] = useState( + BNCoin.fromDenomAndBigNumber(modal?.data.asset.denom ?? baseAsset.denom, BN_ZERO), + ) + const { data: walletBalances } = useWalletBalances(address) + const { simulateDeposits } = useUpdatedAccount(account) + const balance = useCurrentWalletBalance(asset.denom) + const v1Action = useStore((s) => s.v1Action) + + const baseBalance = useMemo( + () => walletBalances.find(byDenom(baseAsset.denom))?.amount ?? '0', + [walletBalances, baseAsset], + ) + + const close = useCallback(() => { + useStore.setState({ v1DepositAndWithdrawModal: null }) + }, []) + + const handleClick = useCallback(async () => { + v1Action('deposit', fundingAsset) + close() + }, [v1Action, fundingAsset, close]) + + useEffect(() => { + if (BN(baseBalance).isLessThan(defaultFee.amount[0].amount)) { + useStore.setState({ focusComponent: { component: } }) + } + }, [baseBalance]) + + const onDebounce = useCallback(() => { + simulateDeposits('lend', [fundingAsset]) + }, [fundingAsset, simulateDeposits]) + + const handleAmountChange = useCallback( + (value: BigNumber) => { + setFundingAsset(BNCoin.fromDenomAndBigNumber(asset.denom, value)) + }, + [asset.denom], + ) + + if (!modal) return + + return ( + } + coinBalances={balance ? [BNCoin.fromCoin(balance)] : []} + actionButtonText={`Deposit ${asset.symbol}`} + title={`Deposit ${asset.symbol} into the Red Bank`} + onClose={close} + onAction={handleClick} + onChange={handleAmountChange} + onDebounce={onDebounce} + /> + ) +} diff --git a/src/components/Modals/V1DepositAndWithdraw/Withdraw.tsx b/src/components/Modals/V1DepositAndWithdraw/Withdraw.tsx new file mode 100644 index 00000000..7cc27f62 --- /dev/null +++ b/src/components/Modals/V1DepositAndWithdraw/Withdraw.tsx @@ -0,0 +1,67 @@ +import { useCallback, useState } from 'react' + +import { BN_ZERO } from 'constants/math' +import useBaseAsset from 'hooks/assets/useBasetAsset' +import useHealthComputer from 'hooks/useHealthComputer' +import { useUpdatedAccount } from 'hooks/useUpdatedAccount' +import useStore from 'store' +import { BNCoin } from 'types/classes/BNCoin' + +import AssetAmountSelectActionModal from '../AssetAmountSelectActionModal' +import DetailsHeader from '../LendAndReclaim/DetailsHeader' + +interface Props { + account: Account +} + +export default function Withdraw(props: Props) { + const { account } = props + const baseAsset = useBaseAsset() + const modal = useStore((s) => s.v1DepositAndWithdrawModal) + const asset = modal?.data.asset ?? baseAsset + const [withdrawAsset, setWithdrawAsset] = useState( + BNCoin.fromDenomAndBigNumber(modal?.data.asset.denom ?? baseAsset.denom, BN_ZERO), + ) + const { computeMaxWithdrawAmount } = useHealthComputer(account) + const maxWithdrawAmount = computeMaxWithdrawAmount(asset.denom) + const { simulateWithdraw } = useUpdatedAccount(account) + const balance = BNCoin.fromDenomAndBigNumber(asset.denom, maxWithdrawAmount) + const v1Action = useStore((s) => s.v1Action) + + const close = useCallback(() => { + useStore.setState({ v1DepositAndWithdrawModal: null }) + }, []) + + const handleClick = useCallback(async () => { + v1Action('withdraw', withdrawAsset) + close() + }, [v1Action, withdrawAsset, close]) + + const onDebounce = useCallback(() => { + simulateWithdraw(false, withdrawAsset) + }, [withdrawAsset, simulateWithdraw]) + + const handleAmountChange = useCallback( + (value: BigNumber) => { + setWithdrawAsset(BNCoin.fromDenomAndBigNumber(asset.denom, value)) + }, + [asset.denom], + ) + + if (!modal) return + + return ( + } + coinBalances={[balance]} + actionButtonText={`Withdraw ${asset.symbol}`} + title={`Withdraw ${asset.symbol} from the Red Bank`} + onClose={close} + onAction={handleClick} + onChange={handleAmountChange} + onDebounce={onDebounce} + /> + ) +} diff --git a/src/components/Modals/V1DepositAndWithdraw/index.tsx b/src/components/Modals/V1DepositAndWithdraw/index.tsx new file mode 100644 index 00000000..698db31d --- /dev/null +++ b/src/components/Modals/V1DepositAndWithdraw/index.tsx @@ -0,0 +1,16 @@ +import useAccount from 'hooks/accounts/useAccount' +import useStore from 'store' + +import Deposit from './Deposit' +import Withdraw from './Withdraw' + +export default function V1DepositAndWithdraw() { + const address = useStore((s) => s.address) + const { data: account } = useAccount(address) + const modal = useStore((s) => s.v1DepositAndWithdrawModal) + const isDeposit = modal?.type === 'deposit' + + if (!modal || !account) return null + if (isDeposit) return + return +} diff --git a/src/components/Modals/index.tsx b/src/components/Modals/index.tsx index e71ffa31..c8b5b890 100644 --- a/src/components/Modals/index.tsx +++ b/src/components/Modals/index.tsx @@ -4,11 +4,12 @@ export { default as AlertDialogController } from 'components/Modals/AlertDialog' export { default as BorrowModal } from 'components/Modals/BorrowModal' export { default as FundAndWithdrawModal } from 'components/Modals/FundWithdraw' export { default as GetStartedModal } from 'components/Modals/GetStartedModal' +export { default as HlsModal } from 'components/Modals/HLS' +export { default as HlsManageModal } from 'components/Modals/HLS/Manage' export { default as LendAndReclaimModalController } from 'components/Modals/LendAndReclaim' export { default as SettingsModal } from 'components/Modals/Settings' export { default as UnlockModal } from 'components/Modals/Unlock' +export { default as V1DepositAndWithdraw } from 'components/Modals/V1DepositAndWithdraw' export { default as VaultModal } from 'components/Modals/Vault' export { default as WalletAssets } from 'components/Modals/WalletAssets' export { default as WithdrawFromVaultsModal } from 'components/Modals/WithdrawFromVaultsModal' -export { default as HlsModal } from 'components/Modals/HLS' -export { default as HlsManageModal } from 'components/Modals/HLS/Manage' diff --git a/src/components/Wallet/RecentTransactions.tsx b/src/components/Wallet/RecentTransactions.tsx index 567da7d9..962e1a88 100644 --- a/src/components/Wallet/RecentTransactions.tsx +++ b/src/components/Wallet/RecentTransactions.tsx @@ -7,8 +7,8 @@ import Text from 'components/common/Text' import { TextLink } from 'components/common/TextLink' import { generateToastContent } from 'components/common/Toaster' import useTransactions from 'hooks/localStorage/useTransactions' -import useStore from 'store' import useChainConfig from 'hooks/useChainConfig' +import useStore from 'store' export default function RecentTransactions() { const address = useStore((s) => s.address) @@ -47,7 +47,9 @@ export default function RecentTransactions() { key={hash} >
- Credit Account {accountId} + + {accountId === address ? 'Red Bank' : `Credit Account ${accountId}`} + {moment.unix(timestamp).format('lll')} diff --git a/src/components/common/Toaster/index.tsx b/src/components/common/Toaster/index.tsx index f83e80b7..6baddeaf 100644 --- a/src/components/common/Toaster/index.tsx +++ b/src/components/common/Toaster/index.tsx @@ -11,11 +11,11 @@ import { TextLink } from 'components/common/TextLink' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' import { LocalStorageKeys } from 'constants/localStorageKeys' import useLocalStorage from 'hooks/localStorage/useLocalStorage' +import useChainConfig from 'hooks/useChainConfig' import useTransactionStore from 'hooks/useTransactionStore' import useStore from 'store' import { formatAmountWithSymbol } from 'utils/formatters' import { BN } from 'utils/helpers' -import useChainConfig from 'hooks/useChainConfig' const toastBodyClasses = classNames( 'flex flex-wrap w-full group/transaction', @@ -99,6 +99,13 @@ export default function Toaster() { if (!isError && toast.accountId) addTransaction(toast) const generalMessage = isError ? 'Transaction failed!' : 'Transaction completed successfully!' const showDetailElement = !!(!details && toast.hash) + const address = useStore.getState().address + + let target: string + if (!isError) { + target = toast.accountId === address ? 'Red Bank' : `Credit Account ${toast.accountId}` + } + const Msg = () => (
@@ -141,7 +148,7 @@ export default function Toaster() { )} > {!isError && toast.accountId && ( - {`Credit Account ${toast.accountId}`} + {target} )} {showDetailElement && toast.message && ( diff --git a/src/components/v1/Table/deposits/Columns/DepositButton.tsx b/src/components/v1/Table/deposits/Columns/DepositButton.tsx index 26c9e8d9..4fa1af9a 100644 --- a/src/components/v1/Table/deposits/Columns/DepositButton.tsx +++ b/src/components/v1/Table/deposits/Columns/DepositButton.tsx @@ -37,7 +37,9 @@ export default function DepositButton(props: Props) { disabled={!hasBalance} color='tertiary' onClick={(e) => { - useStore.setState({ fundAndWithdrawModal: 'fund' }) + useStore.setState({ + v1DepositAndWithdrawModal: { type: 'deposit', data: props.data }, + }) e.stopPropagation() }} text='Deposit' diff --git a/src/components/v1/Table/deposits/Columns/Manage.tsx b/src/components/v1/Table/deposits/Columns/Manage.tsx index 51c447a7..f36616ba 100644 --- a/src/components/v1/Table/deposits/Columns/Manage.tsx +++ b/src/components/v1/Table/deposits/Columns/Manage.tsx @@ -22,17 +22,23 @@ export default function Manage(props: Props) { { icon: , text: 'Deposit more', - onClick: () => openLend(props.data), + onClick: () => + useStore.setState({ + v1DepositAndWithdrawModal: { type: 'deposit', data: props.data }, + }), disabled: !hasBalance, disabledTooltip: `You don’t have any ${props.data.asset.symbol} in your Wallet.`, }, { icon: , text: 'Withdraw', - onClick: () => openReclaim(props.data), + onClick: () => + useStore.setState({ + v1DepositAndWithdrawModal: { type: 'withdraw', data: props.data }, + }), }, ], - [hasBalance, openLend, openReclaim, props.data], + [hasBalance, props.data], ) return ( diff --git a/src/hooks/accounts/useAccount.tsx b/src/hooks/accounts/useAccount.tsx index 2973d98e..2a5f6a06 100644 --- a/src/hooks/accounts/useAccount.tsx +++ b/src/hooks/accounts/useAccount.tsx @@ -3,10 +3,12 @@ import useSWR from 'swr' import getAccount from 'api/accounts/getAccount' import getV1Positions from 'api/v1/getV1Positions' import useChainConfig from 'hooks/useChainConfig' +import useStore from 'store' export default function useAccount(accountId?: string, suspense?: boolean) { const chainConfig = useChainConfig() - const isV1 = isNaN(parseInt(accountId || '')) + const address = useStore((s) => s.address) + const isV1 = accountId === address const cacheKey = isV1 ? `chains/${chainConfig.id}/v1/user/${accountId}` diff --git a/src/store/slices/broadcast.ts b/src/store/slices/broadcast.ts index fa9ff5bb..a759afb7 100644 --- a/src/store/slices/broadcast.ts +++ b/src/store/slices/broadcast.ts @@ -16,6 +16,7 @@ import { ExecuteMsg as CreditManagerExecuteMsg, ExecuteMsg, } from 'types/generated/mars-credit-manager/MarsCreditManager.types' +import { ExecuteMsg as RedBankExecuteMsg } from 'types/generated/mars-red-bank/MarsRedBank.types' import { AccountKind } from 'types/generated/mars-rover-health-types/MarsRoverHealthTypes.types' import { byDenom, bySymbol } from 'utils/array' import { generateErrorMessage, getSingleValueFromBroadcastResult } from 'utils/broadcast' @@ -30,7 +31,7 @@ import { getVaultDepositCoinsFromActions } from 'utils/vaults' function generateExecutionMessage( sender: string | undefined = '', contract: string, - msg: CreditManagerExecuteMsg | AccountNftExecuteMsg | PythUpdateExecuteMsg, + msg: CreditManagerExecuteMsg | AccountNftExecuteMsg | RedBankExecuteMsg | PythUpdateExecuteMsg, funds: Coin[], ) { return new MsgExecuteContract({ @@ -1065,5 +1066,69 @@ export default function createBroadcastSlice( { denom: get().chainConfig.assets[0].denom, amount: String(pythAssets.length) }, ]) }, + v1Action: async (type: V1ActionType, coin: BNCoin) => { + let msg: RedBankExecuteMsg + let toastOptions: ToastObjectOptions = { + action: type, + accountId: get().address, + changes: {}, + } + let funds: Coin[] = [] + + switch (type) { + case 'withdraw': + msg = { + withdraw: { + amount: coin.amount.toString(), + denom: coin.denom, + }, + } + toastOptions = { + ...toastOptions, + changes: { deposits: [coin] }, + target: 'wallet', + } + break + case 'repay': + msg = { + repay: {}, + } + toastOptions.changes = { deposits: [coin] } + funds = [coin.toCoin()] + break + case 'borrow': + msg = { + borrow: { + amount: coin.amount.toString(), + denom: coin.denom, + }, + } + toastOptions = { + ...toastOptions, + changes: { debts: [coin] }, + target: 'wallet', + } + break + default: + msg = { + deposit: {}, + } + toastOptions.changes = { deposits: [coin] } + funds = [coin.toCoin()] + } + + const redBankContract = get().chainConfig.contracts.redBank + + const response = get().executeMsg({ + messages: [generateExecutionMessage(get().address, redBankContract, msg, funds)], + }) + + get().setToast({ + response, + options: toastOptions, + }) + + return response.then((response) => !!response.result) + }, } } diff --git a/src/store/slices/modal.ts b/src/store/slices/modal.ts index fe373b2a..f0984fd2 100644 --- a/src/store/slices/modal.ts +++ b/src/store/slices/modal.ts @@ -19,5 +19,6 @@ export default function createModalSlice(set: SetState, get: GetStat vaultModal: null, walletAssetsModal: null, withdrawFromVaultsModal: null, + v1DepositAndWithdrawModal: null, } } diff --git a/src/types/interfaces/store/broadcast.d.ts b/src/types/interfaces/store/broadcast.d.ts index 719c1c69..1ec3edd1 100644 --- a/src/types/interfaces/store/broadcast.d.ts +++ b/src/types/interfaces/store/broadcast.d.ts @@ -167,4 +167,7 @@ interface BroadcastSlice { borrow: BNCoin[] reclaims: ActionCoin[] }) => Promise + v1Action: (type: V1ActionType, funds: BNCoin) => Promise } + +type V1ActionType = 'withdraw' | 'deposit' | 'borrow' | 'repay' diff --git a/src/types/interfaces/store/modals.d.ts b/src/types/interfaces/store/modals.d.ts index ceef9fa6..d2dd91e3 100644 --- a/src/types/interfaces/store/modals.d.ts +++ b/src/types/interfaces/store/modals.d.ts @@ -7,6 +7,7 @@ interface ModalSlice { hlsManageModal: HlsManageModal | null borrowModal: BorrowModal | null fundAndWithdrawModal: 'fund' | 'withdraw' | null + v1DepositAndWithdrawModal: V1DepositAndWithdrawModal | null getStartedModal: boolean hlsInformationModal: boolean | null lendAndReclaimModal: LendAndReclaimModalConfig | null @@ -84,3 +85,8 @@ interface HlsManageModal { } type HlsStakingManageAction = 'deposit' | 'withdraw' | 'repay' | 'leverage' + +interface V1DepositAndWithdrawModal { + type: 'deposit' | 'withdraw' + data: LendingMarketTableData +} From 21bedd7905955f0cb5f9b9b8b559220a31475233 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Thu, 15 Feb 2024 21:51:00 +0100 Subject: [PATCH 06/15] feat: added borrow and repay --- src/components/Modals/BorrowModal.tsx | 98 ++++----- src/components/Modals/ModalsContainer.tsx | 2 + src/components/Modals/index.tsx | 3 +- src/components/Modals/v1/Borrow.tsx | 179 +++++++++++++++ .../{V1DepositAndWithdraw => v1}/Deposit.tsx | 5 +- src/components/Modals/v1/Repay.tsx | 208 ++++++++++++++++++ src/components/Modals/v1/V1BorrowAndRepay.tsx | 15 ++ .../index.tsx => v1/V1DepositAndWithdraw.tsx} | 5 +- .../{V1DepositAndWithdraw => v1}/Withdraw.tsx | 5 +- .../borrow/Table/Columns/BorrowButton.tsx | 1 - .../borrow/Table/Columns/Manage.tsx | 1 - .../earn/farm/Table/Columns/Manage.tsx | 17 +- .../v1/Table/borrowings/Columns/Action.tsx | 8 +- .../Table/borrowings/Columns/BorrowButton.tsx | 50 +++++ .../v1/Table/borrowings/Columns/Manage.tsx | 47 ++++ .../v1/Table/deposits/Columns/Manage.tsx | 1 - src/store/slices/modal.ts | 1 + src/types/interfaces/store/modals.d.ts | 8 +- 18 files changed, 572 insertions(+), 82 deletions(-) create mode 100644 src/components/Modals/v1/Borrow.tsx rename src/components/Modals/{V1DepositAndWithdraw => v1}/Deposit.tsx (94%) create mode 100644 src/components/Modals/v1/Repay.tsx create mode 100644 src/components/Modals/v1/V1BorrowAndRepay.tsx rename src/components/Modals/{V1DepositAndWithdraw/index.tsx => v1/V1DepositAndWithdraw.tsx} (82%) rename src/components/Modals/{V1DepositAndWithdraw => v1}/Withdraw.tsx (92%) create mode 100644 src/components/v1/Table/borrowings/Columns/BorrowButton.tsx create mode 100644 src/components/v1/Table/borrowings/Columns/Manage.tsx diff --git a/src/components/Modals/BorrowModal.tsx b/src/components/Modals/BorrowModal.tsx index 515c94dc..9a56c66f 100644 --- a/src/components/Modals/BorrowModal.tsx +++ b/src/components/Modals/BorrowModal.tsx @@ -35,18 +35,9 @@ interface Props { modal: BorrowModal } -function getDebtAmount(modal: BorrowModal) { - return BN((modal.marketData as BorrowMarketTableData)?.debt ?? 0).toString() -} - -function getAssetLogo(modal: BorrowModal) { - if (!modal.asset) return null - return -} - function RepayNotAvailable(props: { asset: Asset; repayFromWallet: boolean }) { return ( - +
@@ -90,7 +81,6 @@ function BorrowModal(props: Props) { const apy = modal.marketData.apy.borrow const isAutoLendEnabled = autoLendEnabledAccountIds.includes(account.id) const { computeMaxBorrowAmount } = useHealthComputer(account) - const totalDebt = BN(getDebtAmount(modal)) const accountDebt = account.debts.find(byDenom(asset.denom))?.amount ?? BN_ZERO const markets = useMarkets() @@ -237,7 +227,7 @@ function BorrowModal(props: Props) { onClose={onClose} header={ - {getAssetLogo(modal)} + {isRepay ? 'Repay' : 'Borrow'} {asset.symbol} @@ -251,14 +241,14 @@ function BorrowModal(props: Props) { title={formatPercent(modal.marketData.apy.borrow)} sub={'Borrow Rate APY'} /> - {totalDebt.isGreaterThan(0) && ( + {accountDebt.isGreaterThan(0) && ( <>
@@ -303,59 +293,57 @@ function BorrowModal(props: Props) {
-
- - {isRepay && maxRepayAmount.isZero() && ( - - )} - {isRepay ? ( - <> - + + {isRepay && maxRepayAmount.isZero() && ( + + )} + {isRepay ? ( + <> + +
Repay from Wallet Repay your debt directly from your wallet
-
- -
- - ) : ( - <> - + +
+ + ) : ( + <> + +
Receive funds to Wallet Your borrowed funds will directly go to your wallet
-
- -
- - )} -
+ +
+ + )}
+ + ) +} diff --git a/src/components/Modals/V1DepositAndWithdraw/Deposit.tsx b/src/components/Modals/v1/Deposit.tsx similarity index 94% rename from src/components/Modals/V1DepositAndWithdraw/Deposit.tsx rename to src/components/Modals/v1/Deposit.tsx index b2849729..cb9000f2 100644 --- a/src/components/Modals/V1DepositAndWithdraw/Deposit.tsx +++ b/src/components/Modals/v1/Deposit.tsx @@ -11,9 +11,8 @@ import { BNCoin } from 'types/classes/BNCoin' import { byDenom } from 'utils/array' import { defaultFee } from 'utils/constants' import { BN } from 'utils/helpers' - -import AssetAmountSelectActionModal from '../AssetAmountSelectActionModal' -import DetailsHeader from '../LendAndReclaim/DetailsHeader' +import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal' +import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader' interface Props { account: Account diff --git a/src/components/Modals/v1/Repay.tsx b/src/components/Modals/v1/Repay.tsx new file mode 100644 index 00000000..8ab28264 --- /dev/null +++ b/src/components/Modals/v1/Repay.tsx @@ -0,0 +1,208 @@ +import BigNumber from 'bignumber.js' +import { useCallback, useEffect, useMemo, useState } from 'react' + +import Modal from 'components/Modals/Modal' +import AccountSummaryInModal from 'components/account/AccountSummary/AccountSummaryInModal' +import Button from 'components/common/Button' +import Card from 'components/common/Card' +import DisplayCurrency from 'components/common/DisplayCurrency' +import Divider from 'components/common/Divider' +import { FormattedNumber } from 'components/common/FormattedNumber' +import { ArrowRight, InfoCircle } from 'components/common/Icons' +import Text from 'components/common/Text' +import TitleAndSubCell from 'components/common/TitleAndSubCell' +import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider' +import AssetImage from 'components/common/assets/AssetImage' +import { BN_ZERO } from 'constants/math' +import useBaseAsset from 'hooks/assets/useBasetAsset' +import useMarkets from 'hooks/markets/useMarkets' +import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance' +import { useUpdatedAccount } from 'hooks/useUpdatedAccount' +import useStore from 'store' +import { BNCoin } from 'types/classes/BNCoin' +import { formatPercent } from 'utils/formatters' +import { BN } from 'utils/helpers' +import { getDebtAmountWithInterest } from 'utils/tokens' + +interface Props { + account: Account +} + +function RepayNotAvailable(props: { asset: Asset }) { + return ( + +
+ +
+ No funds for repay + {`Unfortunately you don't have any ${props.asset.symbol} in your Wallet to repay the debt.`} +
+
+
+ ) +} + +export default function Repay(props: Props) { + const { account } = props + const modal = useStore((s) => s.v1BorrowAndRepayModal) + const baseAsset = useBaseAsset() + const asset = modal?.data.asset ?? baseAsset + const [amount, setAmount] = useState(BN_ZERO) + const balance = useCurrentWalletBalance(asset.denom) + const v1Action = useStore((s) => s.v1Action) + const [max, setMax] = useState(BN_ZERO) + const { simulateRepay } = useUpdatedAccount(account) + const apy = modal?.data.apy.borrow ?? 0 + const accountDebt = modal?.data.accountDebtAmount ?? BN_ZERO + const markets = useMarkets() + + const accountDebtWithInterest = useMemo( + () => getDebtAmountWithInterest(accountDebt, apy), + [accountDebt, apy], + ) + + const overpayExeedsCap = useMemo(() => { + const marketAsset = markets.find((market) => market.asset.denom === asset.denom) + if (!marketAsset) return + const overpayAmount = accountDebtWithInterest.minus(accountDebt) + const marketCapAfterOverpay = marketAsset.cap.used.plus(overpayAmount) + + return marketAsset.cap.max.isLessThanOrEqualTo(marketCapAfterOverpay) + }, [markets, asset.denom, accountDebt, accountDebtWithInterest]) + + const maxRepayAmount = useMemo(() => { + const maxBalance = BN(balance?.amount ?? 0) + return BigNumber.min(maxBalance, overpayExeedsCap ? accountDebt : accountDebtWithInterest) + }, [accountDebtWithInterest, overpayExeedsCap, accountDebt, balance?.amount]) + + const close = useCallback(() => { + setAmount(BN_ZERO) + useStore.setState({ v1BorrowAndRepayModal: null }) + }, [setAmount]) + + const onConfirmClick = useCallback(() => { + v1Action('repay', BNCoin.fromDenomAndBigNumber(asset.denom, amount)) + close() + }, [v1Action, asset, amount, close]) + + const handleChange = useCallback( + (newAmount: BigNumber) => { + if (!amount.isEqualTo(newAmount)) setAmount(newAmount) + }, + [amount, setAmount], + ) + + const onDebounce = useCallback(() => { + const repayCoin = BNCoin.fromDenomAndBigNumber( + asset.denom, + amount.isGreaterThan(accountDebt) ? accountDebt : amount, + ) + simulateRepay(repayCoin, true) + }, [amount, accountDebt, asset, simulateRepay]) + + useEffect(() => { + if (maxRepayAmount.isEqualTo(max)) return + setMax(maxRepayAmount) + }, [max, maxRepayAmount]) + + useEffect(() => { + if (amount.isLessThanOrEqualTo(max)) return + handleChange(max) + setAmount(max) + }, [amount, max, handleChange]) + + if (!modal) return null + + return ( + + + + {'Repay'} {asset.symbol} + + + } + headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b' + contentClassName='flex flex-col' + > +
+ + +
+
+
+ + +
+ + Total Borrowed + +
+
+
+
+ + +
+ + Liquidity available + +
+
+
+ + + + {maxRepayAmount.isZero() && } +
+ + ) +} diff --git a/src/components/Modals/v1/V1BorrowAndRepay.tsx b/src/components/Modals/v1/V1BorrowAndRepay.tsx new file mode 100644 index 00000000..141a052f --- /dev/null +++ b/src/components/Modals/v1/V1BorrowAndRepay.tsx @@ -0,0 +1,15 @@ +import useAccount from 'hooks/accounts/useAccount' +import useStore from 'store' +import Borrow from 'components/Modals/v1/Borrow' +import Repay from 'components/Modals/v1/Repay' + +export default function V1BorrowAndRepayModal() { + const address = useStore((s) => s.address) + const { data: account } = useAccount(address) + const modal = useStore((s) => s.v1BorrowAndRepayModal) + const isBorrow = modal?.type === 'borrow' + + if (!modal || !account) return null + if (isBorrow) return + return +} diff --git a/src/components/Modals/V1DepositAndWithdraw/index.tsx b/src/components/Modals/v1/V1DepositAndWithdraw.tsx similarity index 82% rename from src/components/Modals/V1DepositAndWithdraw/index.tsx rename to src/components/Modals/v1/V1DepositAndWithdraw.tsx index 698db31d..475f0c06 100644 --- a/src/components/Modals/V1DepositAndWithdraw/index.tsx +++ b/src/components/Modals/v1/V1DepositAndWithdraw.tsx @@ -1,8 +1,7 @@ import useAccount from 'hooks/accounts/useAccount' import useStore from 'store' - -import Deposit from './Deposit' -import Withdraw from './Withdraw' +import Deposit from 'components/Modals/v1/Deposit' +import Withdraw from 'components/Modals/v1/Withdraw' export default function V1DepositAndWithdraw() { const address = useStore((s) => s.address) diff --git a/src/components/Modals/V1DepositAndWithdraw/Withdraw.tsx b/src/components/Modals/v1/Withdraw.tsx similarity index 92% rename from src/components/Modals/V1DepositAndWithdraw/Withdraw.tsx rename to src/components/Modals/v1/Withdraw.tsx index 7cc27f62..f4d111ce 100644 --- a/src/components/Modals/V1DepositAndWithdraw/Withdraw.tsx +++ b/src/components/Modals/v1/Withdraw.tsx @@ -1,5 +1,7 @@ import { useCallback, useState } from 'react' +import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal' +import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader' import { BN_ZERO } from 'constants/math' import useBaseAsset from 'hooks/assets/useBasetAsset' import useHealthComputer from 'hooks/useHealthComputer' @@ -7,9 +9,6 @@ import { useUpdatedAccount } from 'hooks/useUpdatedAccount' import useStore from 'store' import { BNCoin } from 'types/classes/BNCoin' -import AssetAmountSelectActionModal from '../AssetAmountSelectActionModal' -import DetailsHeader from '../LendAndReclaim/DetailsHeader' - interface Props { account: Account } diff --git a/src/components/borrow/Table/Columns/BorrowButton.tsx b/src/components/borrow/Table/Columns/BorrowButton.tsx index 8368bd83..421a277d 100644 --- a/src/components/borrow/Table/Columns/BorrowButton.tsx +++ b/src/components/borrow/Table/Columns/BorrowButton.tsx @@ -16,7 +16,6 @@ export const BORROW_BUTTON_META = { interface Props { data: LendingMarketTableData - v1?: boolean } export default function BorrowButton(props: Props) { const account = useCurrentAccount() diff --git a/src/components/borrow/Table/Columns/Manage.tsx b/src/components/borrow/Table/Columns/Manage.tsx index 59adbec6..8e5c72c6 100644 --- a/src/components/borrow/Table/Columns/Manage.tsx +++ b/src/components/borrow/Table/Columns/Manage.tsx @@ -12,7 +12,6 @@ export const MANAGE_META = { interface Props { data: BorrowMarketTableData - v1?: boolean } export default function Manage(props: Props) { diff --git a/src/components/earn/farm/Table/Columns/Manage.tsx b/src/components/earn/farm/Table/Columns/Manage.tsx index ab85bebc..0acd130b 100644 --- a/src/components/earn/farm/Table/Columns/Manage.tsx +++ b/src/components/earn/farm/Table/Columns/Manage.tsx @@ -1,17 +1,16 @@ import moment from 'moment/moment' -import React, { useCallback, useMemo, useState } from 'react' +import { useCallback, useMemo, useState } from 'react' +import DropDownButton from 'components/common/Button/DropDownButton' import { AccountArrowDown, LockLocked, LockUnlocked, Plus } from 'components/common/Icons' import Loading from 'components/common/Loading' +import { DEFAULT_SETTINGS } from 'constants/defaultSettings' +import { LocalStorageKeys } from 'constants/localStorageKeys' +import useLocalStorage from 'hooks/localStorage/useLocalStorage' +import useAccountId from 'hooks/useAccountId' +import useStore from 'store' import { VaultStatus } from 'types/enums/vault' -import { DEFAULT_SETTINGS } from '../../../../../constants/defaultSettings' -import { LocalStorageKeys } from '../../../../../constants/localStorageKeys' -import useLocalStorage from '../../../../../hooks/localStorage/useLocalStorage' -import useAccountId from '../../../../../hooks/useAccountId' -import useStore from '../../../../../store' -import DropDownButton from '../../../../common/Button/DropDownButton' - export const MANAGE_META = { accessorKey: 'details', enableSorting: false, header: '' } interface Props { @@ -104,7 +103,7 @@ export default function Manage(props: Props) { if (!address) return null return ( -
+
+ if (hasDebt) return - return + return } diff --git a/src/components/v1/Table/borrowings/Columns/BorrowButton.tsx b/src/components/v1/Table/borrowings/Columns/BorrowButton.tsx new file mode 100644 index 00000000..dc85b35d --- /dev/null +++ b/src/components/v1/Table/borrowings/Columns/BorrowButton.tsx @@ -0,0 +1,50 @@ +import ActionButton from 'components/common/Button/ActionButton' +import { Plus } from 'components/common/Icons' +import Text from 'components/common/Text' +import { Tooltip } from 'components/common/Tooltip' +import ConditionalWrapper from 'hocs/ConditionalWrapper' +import useAccount from 'hooks/accounts/useAccount' +import useStore from 'store' + +interface Props { + data: BorrowMarketTableData +} +export default function BorrowButton(props: Props) { + const address = useStore((s) => s.address) + const { data: account } = useAccount(address) + + const hasCollateral = account?.lends?.length ?? 0 > 0 + + return ( +
+ ( + {`You don’t have assets deposited in the Red Bank. Please deposit assets before you borrow.`} + } + contentClassName='max-w-[200px]' + className='ml-auto' + > + {children} + + )} + > + } + disabled={!hasCollateral} + color='tertiary' + onClick={(e) => { + useStore.setState({ + v1BorrowAndRepayModal: { type: 'borrow', data: props.data }, + }) + e.stopPropagation() + }} + text='Borrow' + /> + +
+ ) +} diff --git a/src/components/v1/Table/borrowings/Columns/Manage.tsx b/src/components/v1/Table/borrowings/Columns/Manage.tsx new file mode 100644 index 00000000..b05b896b --- /dev/null +++ b/src/components/v1/Table/borrowings/Columns/Manage.tsx @@ -0,0 +1,47 @@ +import { useMemo } from 'react' + +import DropDownButton from 'components/common/Button/DropDownButton' +import { HandCoins, Plus } from 'components/common/Icons' +import useWalletBalances from 'hooks/useWalletBalances' +import useStore from 'store' +import { byDenom } from 'utils/array' + +interface Props { + data: BorrowMarketTableData +} + +export default function Manage(props: Props) { + const address = useStore((s) => s.address) + const { data: balances } = useWalletBalances(address) + const hasBalance = !!balances.find(byDenom(props.data.asset.denom)) + + const ITEMS: DropDownItem[] = useMemo( + () => [ + { + icon: , + text: 'Borrow more', + onClick: () => + useStore.setState({ + v1BorrowAndRepayModal: { type: 'borrow', data: props.data }, + }), + }, + { + icon: , + text: 'Repay', + onClick: () => + useStore.setState({ + v1BorrowAndRepayModal: { type: 'repay', data: props.data }, + }), + disabled: !hasBalance, + disabledTooltip: `You don’t have any ${props.data.asset.symbol} in your Wallet.`, + }, + ], + [hasBalance, props.data], + ) + + return ( +
+ +
+ ) +} diff --git a/src/components/v1/Table/deposits/Columns/Manage.tsx b/src/components/v1/Table/deposits/Columns/Manage.tsx index f36616ba..728afbf1 100644 --- a/src/components/v1/Table/deposits/Columns/Manage.tsx +++ b/src/components/v1/Table/deposits/Columns/Manage.tsx @@ -12,7 +12,6 @@ interface Props { } export default function Manage(props: Props) { - const { openLend, openReclaim } = useLendAndReclaimModal() const address = useStore((s) => s.address) const { data: balances } = useWalletBalances(address) const hasBalance = !!balances.find(byDenom(props.data.asset.denom)) diff --git a/src/store/slices/modal.ts b/src/store/slices/modal.ts index f0984fd2..76277afe 100644 --- a/src/store/slices/modal.ts +++ b/src/store/slices/modal.ts @@ -20,5 +20,6 @@ export default function createModalSlice(set: SetState, get: GetStat walletAssetsModal: null, withdrawFromVaultsModal: null, v1DepositAndWithdrawModal: null, + v1BorrowAndRepayModal: null, } } diff --git a/src/types/interfaces/store/modals.d.ts b/src/types/interfaces/store/modals.d.ts index d2dd91e3..e0fc378f 100644 --- a/src/types/interfaces/store/modals.d.ts +++ b/src/types/interfaces/store/modals.d.ts @@ -7,7 +7,6 @@ interface ModalSlice { hlsManageModal: HlsManageModal | null borrowModal: BorrowModal | null fundAndWithdrawModal: 'fund' | 'withdraw' | null - v1DepositAndWithdrawModal: V1DepositAndWithdrawModal | null getStartedModal: boolean hlsInformationModal: boolean | null lendAndReclaimModal: LendAndReclaimModalConfig | null @@ -16,6 +15,8 @@ interface ModalSlice { vaultModal: VaultModal | null walletAssetsModal: WalletAssetModal | null withdrawFromVaultsModal: DepositedVault[] | null + v1DepositAndWithdrawModal: V1DepositAndWithdrawModal | null + v1BorrowAndRepayModal: V1BorrowAndRepayModal | null } interface AlertDialogButton { @@ -90,3 +91,8 @@ interface V1DepositAndWithdrawModal { type: 'deposit' | 'withdraw' data: LendingMarketTableData } + +interface V1BorrowAndRepayModal { + type: 'borrow' | 'repay' + data: BorrowMarketTableData +} From fc9fcf13fc6e4cf82352d9720788afa62b74e9e2 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 16 Feb 2024 17:15:59 +0100 Subject: [PATCH 07/15] fix: fixed lending table widths --- src/components/borrow/Table/Columns/Manage.tsx | 2 +- src/components/common/Table/Row.tsx | 2 +- src/components/common/Table/index.tsx | 1 - src/components/earn/lend/Table/Columns/DepositCap.tsx | 3 +++ src/components/earn/lend/Table/Columns/LendButton.tsx | 3 +++ src/components/earn/lend/Table/Columns/Manage.tsx | 5 ++++- 6 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/components/borrow/Table/Columns/Manage.tsx b/src/components/borrow/Table/Columns/Manage.tsx index c7112d06..8e5c72c6 100644 --- a/src/components/borrow/Table/Columns/Manage.tsx +++ b/src/components/borrow/Table/Columns/Manage.tsx @@ -48,7 +48,7 @@ export default function Manage(props: Props) { if (!address) return null return ( -
+
) diff --git a/src/components/common/Table/Row.tsx b/src/components/common/Table/Row.tsx index eb45dfb9..0d6b027f 100644 --- a/src/components/common/Table/Row.tsx +++ b/src/components/common/Table/Row.tsx @@ -66,7 +66,7 @@ export default function Row(props: Props) { spacingClassName ?? 'px-3 py-4', type && type !== 'strategies' && isSymbolOrName && 'border-l', type && type !== 'strategies' && getBorderColor(type, cell.row.original as any), - cell.column.columnDef.meta?.className ?? 'w-min', + cell.column.columnDef.meta?.className, )} > {flexRender(cell.column.columnDef.cell, cell.getContext())} diff --git a/src/components/common/Table/index.tsx b/src/components/common/Table/index.tsx index ce9ca248..3a31bed6 100644 --- a/src/components/common/Table/index.tsx +++ b/src/components/common/Table/index.tsx @@ -76,7 +76,6 @@ export default function Table(props: Props) { props.spacingClassName ?? 'px-4 py-3', header.column.getCanSort() && 'hover:cursor-pointer', header.id === 'symbol' || header.id === 'name' ? 'text-left' : 'text-right', - 'w-min', header.column.columnDef.meta?.className, )} > diff --git a/src/components/earn/lend/Table/Columns/DepositCap.tsx b/src/components/earn/lend/Table/Columns/DepositCap.tsx index 1ad1028e..e65f431a 100644 --- a/src/components/earn/lend/Table/Columns/DepositCap.tsx +++ b/src/components/earn/lend/Table/Columns/DepositCap.tsx @@ -10,6 +10,9 @@ export const DEPOSIT_CAP_META = { accessorKey: 'marketDepositCap', header: 'Deposit Cap', id: 'marketDepositCap', + meta: { + className: 'w-50', + }, } export const marketDepositCapSortingFn = ( diff --git a/src/components/earn/lend/Table/Columns/LendButton.tsx b/src/components/earn/lend/Table/Columns/LendButton.tsx index ffe78a75..3ca6e5c7 100644 --- a/src/components/earn/lend/Table/Columns/LendButton.tsx +++ b/src/components/earn/lend/Table/Columns/LendButton.tsx @@ -13,6 +13,9 @@ export const LEND_BUTTON_META = { accessorKey: 'lend', enableSorting: false, header: '', + meta: { + className: 'w-40', + }, } interface Props { diff --git a/src/components/earn/lend/Table/Columns/Manage.tsx b/src/components/earn/lend/Table/Columns/Manage.tsx index 4f031e9f..b210a447 100644 --- a/src/components/earn/lend/Table/Columns/Manage.tsx +++ b/src/components/earn/lend/Table/Columns/Manage.tsx @@ -13,6 +13,9 @@ export const MANAGE_META = { accessorKey: 'manage', enableSorting: false, header: '', + meta: { + className: 'w-40', + }, } interface Props { @@ -75,7 +78,7 @@ export default function Manage(props: Props) { if (!address) return null return ( -
+
) From 89d41fa5cf63da8952a6eb400f7e1748c234e201 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Mon, 19 Feb 2024 12:38:04 +0100 Subject: [PATCH 08/15] fix: isNaN Leverage --- src/components/portfolio/Overview/Summary.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/portfolio/Overview/Summary.tsx b/src/components/portfolio/Overview/Summary.tsx index dc121c9d..4b4afd88 100644 --- a/src/components/portfolio/Overview/Summary.tsx +++ b/src/components/portfolio/Overview/Summary.tsx @@ -89,7 +89,7 @@ export default function PortfolioSummary() { title: ( ), From 1a45d64bac8016522a47576f946598d5e761f2ec Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Mon, 19 Feb 2024 13:10:24 +0100 Subject: [PATCH 09/15] fix: fix getUrl --- src/api/cosmwasm-client.ts | 21 ++++++++++---------- src/components/Wallet/WalletConnecting.tsx | 5 +++-- src/components/portfolio/Account/Summary.tsx | 2 +- src/hooks/useClients.ts | 3 ++- src/utils/url.ts | 5 +++-- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/api/cosmwasm-client.ts b/src/api/cosmwasm-client.ts index f6a0ab0a..c391367c 100644 --- a/src/api/cosmwasm-client.ts +++ b/src/api/cosmwasm-client.ts @@ -8,6 +8,7 @@ import { MarsParamsQueryClient } from 'types/generated/mars-params/MarsParams.cl import { MarsPerpsQueryClient } from 'types/generated/mars-perps/MarsPerps.client' import { MarsRedBankQueryClient } from 'types/generated/mars-red-bank/MarsRedBank.client' import { MarsSwapperOsmosisQueryClient } from 'types/generated/mars-swapper-osmosis/MarsSwapperOsmosis.client' +import { getUrl } from 'utils/url' let _cosmWasmClient: Map = new Map() let _creditManagerQueryClient: Map = new Map() @@ -34,7 +35,7 @@ const getClient = async (rpc: string) => { const getCreditManagerQueryClient = async (chainConfig: ChainConfig) => { try { const contract = chainConfig.contracts.creditManager - const rpc = chainConfig.endpoints.rpc + const rpc = getUrl(chainConfig.endpoints.rpc) const key = rpc + contract if (!_creditManagerQueryClient.get(key)) { @@ -51,7 +52,7 @@ const getCreditManagerQueryClient = async (chainConfig: ChainConfig) => { const getParamsQueryClient = async (chainConfig: ChainConfig) => { try { const contract = chainConfig.contracts.params - const rpc = chainConfig.endpoints.rpc + const rpc = getUrl(chainConfig.endpoints.rpc) const key = rpc + contract if (!_paramsQueryClient.get(key)) { @@ -68,7 +69,7 @@ const getParamsQueryClient = async (chainConfig: ChainConfig) => { const getOracleQueryClient = async (chainConfig: ChainConfig) => { try { const contract = chainConfig.contracts.oracle - const rpc = chainConfig.endpoints.rpc + const rpc = getUrl(chainConfig.endpoints.rpc) const key = rpc + contract if (!_oracleQueryClient.get(key)) { @@ -84,7 +85,7 @@ const getOracleQueryClient = async (chainConfig: ChainConfig) => { const getVaultQueryClient = async (chainConfig: ChainConfig, address: string) => { try { - const client = await getClient(chainConfig.endpoints.rpc) + const client = await getClient(getUrl(chainConfig.endpoints.rpc)) return new MarsMockVaultQueryClient(client, address) } catch (error) { throw error @@ -94,7 +95,7 @@ const getVaultQueryClient = async (chainConfig: ChainConfig, address: string) => const getIncentivesQueryClient = async (chainConfig: ChainConfig) => { try { const contract = chainConfig.contracts.incentives - const rpc = chainConfig.endpoints.rpc + const rpc = getUrl(chainConfig.endpoints.rpc) const key = rpc + contract if (!_incentivesQueryClient.get(key)) { const client = await getClient(rpc) @@ -110,7 +111,7 @@ const getIncentivesQueryClient = async (chainConfig: ChainConfig) => { const getSwapperQueryClient = async (chainConfig: ChainConfig) => { try { const contract = chainConfig.contracts.swapper - const rpc = chainConfig.endpoints.rpc + const rpc = getUrl(chainConfig.endpoints.rpc) const key = rpc + contract if (!_swapperOsmosisClient.get(key)) { const client = await getClient(rpc) @@ -126,7 +127,7 @@ const getSwapperQueryClient = async (chainConfig: ChainConfig) => { const getPerpsQueryClient = async (chainConfig: ChainConfig) => { try { const contract = chainConfig.contracts.perps - const rpc = chainConfig.endpoints.rpc + const rpc = getUrl(chainConfig.endpoints.rpc) const key = rpc + contract if (!_perpsClient.get(key)) { const client = await getClient(rpc) @@ -142,7 +143,7 @@ const getPerpsQueryClient = async (chainConfig: ChainConfig) => { const getRedBankQueryClient = async (chainConfig: ChainConfig) => { try { const contract = chainConfig.contracts.redBank - const rpc = chainConfig.endpoints.rpc + const rpc = getUrl(chainConfig.endpoints.rpc) const key = rpc + contract if (!_redBankQueryClient.get(key)) { @@ -162,8 +163,8 @@ export { getIncentivesQueryClient, getOracleQueryClient, getParamsQueryClient, - getSwapperQueryClient, - getVaultQueryClient, getPerpsQueryClient, getRedBankQueryClient, + getSwapperQueryClient, + getVaultQueryClient, } diff --git a/src/components/Wallet/WalletConnecting.tsx b/src/components/Wallet/WalletConnecting.tsx index 156e01aa..78e4f9ee 100644 --- a/src/components/Wallet/WalletConnecting.tsx +++ b/src/components/Wallet/WalletConnecting.tsx @@ -10,6 +10,7 @@ import useChainConfig from 'hooks/useChainConfig' import useCurrentWallet from 'hooks/useCurrentWallet' import useToggle from 'hooks/useToggle' import useStore from 'store' +import { getUrl } from 'utils/url' interface Props { providerId?: string @@ -53,7 +54,7 @@ export default function WalletConnecting(props: Props) { setIsConnecting(true) try { const response = await connect({ extensionProviderId, chainId: chainConfig.id }) - const cosmClient = await CosmWasmClient.connect(chainConfig.endpoints.rpc) + const cosmClient = await CosmWasmClient.connect(getUrl(chainConfig.endpoints.rpc)) const walletClient: WalletClient = { broadcast, cosmWasmClient: cosmClient, @@ -137,7 +138,7 @@ export default function WalletConnecting(props: Props) { setIsConnecting(true) try { await mobileConnect({ mobileProviderId, chainId: chainConfig.id }) - const cosmClient = await CosmWasmClient.connect(chainConfig.endpoints.rpc) + const cosmClient = await CosmWasmClient.connect(getUrl(chainConfig.endpoints.rpc)) const walletClient: WalletClient = { broadcast, cosmWasmClient: cosmClient, diff --git a/src/components/portfolio/Account/Summary.tsx b/src/components/portfolio/Account/Summary.tsx index e6f5fdf2..00ed0247 100644 --- a/src/components/portfolio/Account/Summary.tsx +++ b/src/components/portfolio/Account/Summary.tsx @@ -75,7 +75,7 @@ function Content(props: Props) { title: ( ), diff --git a/src/hooks/useClients.ts b/src/hooks/useClients.ts index 24b03dbb..7fbee02f 100644 --- a/src/hooks/useClients.ts +++ b/src/hooks/useClients.ts @@ -11,6 +11,7 @@ import { MarsParamsQueryClient } from 'types/generated/mars-params/MarsParams.cl import { MarsPerpsQueryClient } from 'types/generated/mars-perps/MarsPerps.client' import { MarsRedBankQueryClient } from 'types/generated/mars-red-bank/MarsRedBank.client' import { MarsSwapperOsmosisQueryClient } from 'types/generated/mars-swapper-osmosis/MarsSwapperOsmosis.client' +import { getUrl } from 'utils/url' export default function useClients() { const chainConfig = useChainConfig() @@ -18,7 +19,7 @@ export default function useClients() { const swr = useSWR( `chains/${chainConfig.id}/clients`, async () => { - const client = await CosmWasmClient.connect(chainConfig.endpoints.rpc) + const client = await CosmWasmClient.connect(getUrl(chainConfig.endpoints.rpc)) return { creditManager: new MarsCreditManagerQueryClient( diff --git a/src/utils/url.ts b/src/utils/url.ts index a3a331c9..d411c924 100644 --- a/src/utils/url.ts +++ b/src/utils/url.ts @@ -1,9 +1,10 @@ -export const getUrl = (baseUrl: string, path: string): string => { +export const getUrl = (baseUrl: string, path?: string): string => { + if (!path) path = '' const isPlaceholder = baseUrl.split('APP_').length > 1 if (isPlaceholder) return baseUrl + '/' + path - const url = new URL(baseUrl) + const url = new URL(baseUrl.split('?')[0]) if (process.env.NEXT_PUBLIC_API_KEY) return `${url.href}${path}?x-apikey=${process.env.NEXT_PUBLIC_API_KEY}` From 89e9942b7e20859bf7e0ee7dbdc11cf80931ce73 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Wed, 21 Feb 2024 07:47:08 +0100 Subject: [PATCH 10/15] tidy: remove sentry --- sentry.client.config.js | 19 ------------------- sentry.properties | 4 ---- sentry.server.config.js | 19 ------------------- 3 files changed, 42 deletions(-) delete mode 100644 sentry.client.config.js delete mode 100644 sentry.properties delete mode 100644 sentry.server.config.js diff --git a/sentry.client.config.js b/sentry.client.config.js deleted file mode 100644 index dc6b933a..00000000 --- a/sentry.client.config.js +++ /dev/null @@ -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({ - dsn: SENTRY_DSN, - environment: process.env.NEXT_PUBLIC_SENTRY_ENV, - // Adjust this value in production, or use tracesSampler for greater control - tracesSampleRate: 0.5, - enabled: process.env.NODE_ENV !== 'development', - // ... - // 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 -}) diff --git a/sentry.properties b/sentry.properties deleted file mode 100644 index 46f155aa..00000000 --- a/sentry.properties +++ /dev/null @@ -1,4 +0,0 @@ -defaults.url=https://sentry.io/ -defaults.org=delphi-mars -defaults.project=mars-v2 -cli.executable=../../../.npm/_npx/a8388072043b4cbc/node_modules/@sentry/cli/bin/sentry-cli diff --git a/sentry.server.config.js b/sentry.server.config.js deleted file mode 100644 index 9f9ce8ab..00000000 --- a/sentry.server.config.js +++ /dev/null @@ -1,19 +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, - environment: process.env.NEXT_PUBLIC_SENTRY_ENV, - // Adjust this value in production, or use tracesSampler for greater control - tracesSampleRate: 0.5, - enabled: process.env.NODE_ENV !== 'development', - // ... - // 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 -}) From 071cceacec8f8e85b4749630696b5f3ee4f71934 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Wed, 21 Feb 2024 08:03:17 +0100 Subject: [PATCH 11/15] feat: separate v1 implementation --- src/components/header/ChainSelect.tsx | 36 ++++++--- src/components/header/DesktopHeader.tsx | 8 +- src/components/header/V1DesktopHeader.tsx | 81 +++++++++++++++++++ .../header/navigation/DesktopNavigation.tsx | 10 ++- src/pages/_layout.tsx | 6 +- 5 files changed, 120 insertions(+), 21 deletions(-) create mode 100644 src/components/header/V1DesktopHeader.tsx diff --git a/src/components/header/ChainSelect.tsx b/src/components/header/ChainSelect.tsx index 11f761d7..77035442 100644 --- a/src/components/header/ChainSelect.tsx +++ b/src/components/header/ChainSelect.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames' import { useCallback, useMemo } from 'react' -import { useLocation, useNavigate, useSearchParams } from 'react-router-dom' +import { useNavigate, useSearchParams } from 'react-router-dom' import { useSWRConfig } from 'swr' import Button from 'components/common/Button' @@ -15,11 +15,16 @@ import useToggle from 'hooks/useToggle' import useStore from 'store' import { NETWORK } from 'types/enums/network' import { ChainInfoID } from 'types/enums/wallet' -import { getPage, getRoute } from 'utils/route' +import { getRoute } from 'utils/route' const v1Outposts = [ - { chainId: ChainInfoID.Neutron1, name: 'Neutron', url: 'https://neutron.marsprotocol.io' }, - { chainId: ChainInfoID.Osmosis1, name: 'Osmosis', url: 'https://v1.marsprotocol.io' }, + { + chainId: ChainInfoID.Neutron1, + name: 'Neutron', + url: 'https://neutron.marsprotocol.io', + target: '_blank', + }, + { chainId: ChainInfoID.Osmosis1, name: 'Osmosis', url: '/v1', target: '_self' }, ] export default function ChainSelect() { @@ -27,8 +32,8 @@ export default function ChainSelect() { const chainConfig = useChainConfig() const { mutate } = useSWRConfig() const navigate = useNavigate() - const { pathname } = useLocation() const [searchParams] = useSearchParams() + const isV1 = useStore((s) => s.isV1) const [_, setCurrentChainId] = useCurrentChainId() @@ -39,14 +44,15 @@ export default function ChainSelect() { mutate(() => true) useStore.setState({ chainConfig, + isV1: false, client: undefined, address: undefined, userDomain: undefined, balances: [], }) - navigate(getRoute(getPage(pathname), searchParams)) + navigate(getRoute('trade', searchParams)) }, - [setCurrentChainId, setShowMenu, mutate, navigate, pathname, searchParams], + [setCurrentChainId, setShowMenu, mutate, navigate, searchParams], ) const currentChains = useMemo(() => { @@ -80,7 +86,7 @@ export default function ChainSelect() {
  • {v1Outposts.map((outpost) => (
  • window.open(outpost.url, '_blank')} + onClick={() => window.open(outpost.url, outpost.target)} key={outpost.name} > - {outpost.name} + {outpost.name}{' '} + {outpost.target !== '_self' && ( + + )}
  • ))} diff --git a/src/components/header/DesktopHeader.tsx b/src/components/header/DesktopHeader.tsx index 493e6f73..a3378156 100644 --- a/src/components/header/DesktopHeader.tsx +++ b/src/components/header/DesktopHeader.tsx @@ -16,7 +16,7 @@ import useStore from 'store' import { WalletID } from 'types/enums/wallet' import { getGovernanceUrl } from 'utils/helpers' -export const menuTree = (walletId: WalletID, chainConfig: ChainConfig): MenuTreeEntry[] => [ +const menuTree = (walletId: WalletID, chainConfig: ChainConfig): MenuTreeEntry[] => [ { pages: ['trade', 'trade-advanced'], label: 'Trade', @@ -40,7 +40,6 @@ export const menuTree = (walletId: WalletID, chainConfig: ChainConfig): MenuTree { pages: ['borrow'], label: 'Borrow' }, ...(chainConfig.hls ? [{ pages: ['hls-staking'] as Page[], label: 'High Leverage' }] : []), { pages: ['portfolio'], label: 'Portfolio' }, - { pages: ['v1'], label: 'V1' }, { pages: ['governance'], label: 'Governance', externalUrl: getGovernanceUrl(walletId) }, ] @@ -49,9 +48,8 @@ export default function DesktopHeader() { const focusComponent = useStore((s) => s.focusComponent) const isOracleStale = useStore((s) => s.isOracleStale) const isHLS = useStore((s) => s.isHLS) - const isV1 = useStore((s) => s.isV1) const accountId = useAccountId() - const showAccountMenu = address && !isHLS && !isV1 + const showAccountMenu = address && !isHLS function handleCloseFocusMode() { if (focusComponent && focusComponent.onClose) focusComponent.onClose() @@ -75,7 +73,7 @@ export default function DesktopHeader() { focusComponent ? 'relative isolate' : 'border-b border-white/20', )} > - + {focusComponent ? (
    diff --git a/src/components/header/V1DesktopHeader.tsx b/src/components/header/V1DesktopHeader.tsx new file mode 100644 index 00000000..4635ddd2 --- /dev/null +++ b/src/components/header/V1DesktopHeader.tsx @@ -0,0 +1,81 @@ +import classNames from 'classnames' +import { useMemo } from 'react' +import { isDesktop } from 'react-device-detect' + +import Wallet from 'components/Wallet' +import EscButton from 'components/common/Button/EscButton' +import Settings from 'components/common/Settings' +import ChainSelect from 'components/header/ChainSelect' +import OracleResyncButton from 'components/header/OracleResyncButton' +import RewardsCenter from 'components/header/RewardsCenter' +import DesktopNavigation from 'components/header/navigation/DesktopNavigation' +import useAccountId from 'hooks/useAccountId' +import useStore from 'store' +import { WalletID } from 'types/enums/wallet' +import { getGovernanceUrl } from 'utils/helpers' + +const menuTree = (walletId: WalletID, chainConfig: ChainConfig): MenuTreeEntry[] => [ + { + pages: ['v1'], + label: 'Red Bank', + }, + { pages: ['governance'], label: 'Governance', externalUrl: getGovernanceUrl(walletId) }, +] + +export default function DesktopHeader() { + const address = useStore((s) => s.address) + const focusComponent = useStore((s) => s.focusComponent) + const isOracleStale = useStore((s) => s.isOracleStale) + const accountId = useAccountId() + + function handleCloseFocusMode() { + if (focusComponent && focusComponent.onClose) focusComponent.onClose() + useStore.setState({ focusComponent: null }) + } + + const showStaleOracle = useMemo(() => isOracleStale && address, [isOracleStale, address]) + + if (!isDesktop) return null + + return ( +
    +
    + + + {focusComponent ? ( +
    +
    + {address && ( +
    + + +
    + )} +
    + {!address && } + +
    +
    + ) : ( +
    + {showStaleOracle && } + {accountId && } + + + +
    + )} +
    +
    + ) +} diff --git a/src/components/header/navigation/DesktopNavigation.tsx b/src/components/header/navigation/DesktopNavigation.tsx index 92fc1a47..a8c0c888 100644 --- a/src/components/header/navigation/DesktopNavigation.tsx +++ b/src/components/header/navigation/DesktopNavigation.tsx @@ -4,7 +4,6 @@ import { useMemo } from 'react' import Button from 'components/common/Button' import { ChevronDown, Logo } from 'components/common/Icons' -import { menuTree } from 'components/header/DesktopHeader' import { NavLink } from 'components/header/navigation/NavLink' import { NavMenu } from 'components/header/navigation/NavMenu' import useChainConfig from 'hooks/useChainConfig' @@ -12,19 +11,24 @@ import useToggle from 'hooks/useToggle' import useStore from 'store' import { WalletID } from 'types/enums/wallet' +interface Props { + menuTree: (walletId: WalletID, chainConfig: ChainConfig) => MenuTreeEntry[] +} + export function getIsActive(pages: string[]) { const segments = location.pathname.split('/') return pages.some((page) => segments.includes(page)) } -export default function DesktopNavigation() { +export default function DesktopNavigation(props: Props) { + const { menuTree } = props const [showMenu, setShowMenu] = useToggle() const { recentWallet } = useShuttle() const chainConfig = useChainConfig() const walletId = (recentWallet?.providerId as WalletID) ?? WalletID.Keplr const focusComponent = useStore((s) => s.focusComponent) - const menu = useMemo(() => menuTree(walletId, chainConfig), [walletId, chainConfig]) + const menu = useMemo(() => menuTree(walletId, chainConfig), [walletId, chainConfig, menuTree]) return (
    s.focusComponent) const address = useStore((s) => s.address) + const isV1 = useStore((s) => s.isV1) const [reduceMotion] = useLocalStorage( LocalStorageKeys.REDUCE_MOTION, @@ -67,7 +69,7 @@ export default function Layout({ children }: { children: React.ReactNode }) { - + {isV1 ? : }
    Date: Fri, 23 Feb 2024 11:40:28 +0100 Subject: [PATCH 12/15] feat: first v1 standalone adjustments --- package.json | 1 + public/images/bg-v1.svg | 63 +++++++++++++++++++ .../Wallet/WalletFetchBalancesAndAccounts.tsx | 6 +- src/components/common/Background.tsx | 4 +- src/components/common/Button/ActionButton.tsx | 3 +- src/components/common/Footer.tsx | 4 +- src/components/common/MarketDetails.tsx | 2 +- src/components/common/Table/Row.tsx | 2 +- src/components/common/Table/index.tsx | 1 - .../earn/lend/Table/Columns/Apy.tsx | 2 +- src/constants/pageMetadata.ts | 5 ++ src/pages/V1Page.tsx | 6 +- src/pages/_layout.tsx | 5 +- tailwind.config.js | 13 ++-- 14 files changed, 98 insertions(+), 19 deletions(-) create mode 100644 public/images/bg-v1.svg diff --git a/package.json b/package.json index ae1e23ad..0981d55a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "mars-v2-frontend", "version": "2.2.4", + "v1version": "1.7.5", "homepage": "./", "private": false, "license": "SEE LICENSE IN LICENSE FILE", diff --git a/public/images/bg-v1.svg b/public/images/bg-v1.svg new file mode 100644 index 00000000..c15fa8f9 --- /dev/null +++ b/public/images/bg-v1.svg @@ -0,0 +1,63 @@ + + + + + + + + + diff --git a/src/components/Wallet/WalletFetchBalancesAndAccounts.tsx b/src/components/Wallet/WalletFetchBalancesAndAccounts.tsx index 162a60f8..63876f95 100644 --- a/src/components/Wallet/WalletFetchBalancesAndAccounts.tsx +++ b/src/components/Wallet/WalletFetchBalancesAndAccounts.tsx @@ -29,7 +29,7 @@ function FetchLoading() { function Content() { const address = useStore((s) => s.address) const [searchParams] = useSearchParams() - + const isV1 = useStore((s) => s.isV1) const { address: urlAddress } = useParams() const urlAccountId = useAccountId() const navigate = useNavigate() @@ -62,7 +62,7 @@ function Content() { ) { const currentAccountIsHLS = urlAccountId && !accountIds.includes(urlAccountId) const currentAccount = currentAccountIsHLS || !urlAccountId ? accountIds[0] : urlAccountId - navigate(getRoute(page, searchParams, address, currentAccount)) + navigate(getRoute(page, searchParams, address, isV1 ? undefined : currentAccount)) useStore.setState({ balances: walletBalances, focusComponent: null }) } }, [ @@ -79,7 +79,7 @@ function Content() { if (isLoadingAccounts || isLoadingBalances) return if (BN(baseBalance).isLessThan(defaultFee.amount[0].amount)) return - if (accountIds && accountIds.length === 0) return + if (accountIds && accountIds.length === 0 && !isV1) return return null } diff --git a/src/components/common/Background.tsx b/src/components/common/Background.tsx index 3bdca489..2feb7339 100644 --- a/src/components/common/Background.tsx +++ b/src/components/common/Background.tsx @@ -27,7 +27,7 @@ export default function Background() { return ['bg-orb-primary-hls', 'bg-orb-secondary-hls', 'bg-orb-tertiary-hls', 'bg-body-hls'] } if (isV1) { - return ['bg-orb-primary-v1', 'bg-orb-secondary-v1', 'bg-orb-tertiary-v1', 'bg-body-v1'] + return ['bg-transparent', 'bg-transparent', 'bg-transparent', 'bg-body bg-v1 blur-[2px]'] } return ['bg-orb-primary', 'bg-orb-secondary', 'bg-orb-tertiary', 'bg-body'] @@ -38,7 +38,7 @@ export default function Background() { className={classNames( 'fixed inset-0', 'w-full h-full', - 'overflow-hidden pointer-events-none background ', + 'overflow-hidden pointer-events-none background', bodyClassName, !reduceMotion && 'transition-bg duration-1000 delay-300', )} diff --git a/src/components/common/Button/ActionButton.tsx b/src/components/common/Button/ActionButton.tsx index 7cf580df..0c5e9b46 100644 --- a/src/components/common/Button/ActionButton.tsx +++ b/src/components/common/Button/ActionButton.tsx @@ -13,6 +13,7 @@ export default function ActionButton(props: ButtonProps) { const { className, color, variant, size } = props const defaultProps = { className, color, variant, size } const address = useStore((s) => s.address) + const isV1 = useStore((s) => s.isV1) const { data: accountIds } = useAccountIds(address || '') const selectedAccountId = useAccountId() @@ -34,7 +35,7 @@ export default function ActionButton(props: ButtonProps) { ) } - if (!selectedAccountId) { + if (!selectedAccountId && !isV1) { return (
    ) } diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx index b3987684..72cb6af9 100644 --- a/src/pages/_layout.tsx +++ b/src/pages/_layout.tsx @@ -26,6 +26,8 @@ interface Props { } function PageContainer(props: Props) { + const isV1 = useStore((s) => s.isV1) + if (isMobile) return props.children if (!props.focusComponent) @@ -33,7 +35,8 @@ function PageContainer(props: Props) {
    {props.children} diff --git a/tailwind.config.js b/tailwind.config.js index 1bc2f416..6a9d67ff 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -102,7 +102,6 @@ module.exports = { axlusdc: '#478edc', body: '#0D0012', 'body-hls': '#090000', - 'body-v1': '#10000a', 'body-dark': '#141621', chart: '#220e1d', error: '#F04438', @@ -124,13 +123,10 @@ module.exports = { osmo: '#9f1ab9', 'orb-primary': '#b12f25', 'orb-primary-hls': '#FF645F', - 'orb-primary-v1': '#612e4d', 'orb-secondary': '#530781', 'orb-secondary-hls': '#a03b45', - 'orb-secondary-v1': '#692f55', 'orb-tertiary': '#ff00c7', 'orb-tertiary-hls': '#FB9562', - 'orb-tertiary-v1': '#993878', profit: '#4CA30D', primary: '#FF625E', secondary: '#FB9562', @@ -244,6 +240,7 @@ module.exports = { }, maxWidth: { content: '1024px', + v1: '1248px', modal: '895px', 'modal-md': '556px', 'modal-sm': '526px', @@ -263,7 +260,7 @@ module.exports = { screens: { sm: '480px', md: '720px', - lg: '1024px', + lg: '1280px', xl: '1280px', '2xl': '1920px', }, @@ -508,6 +505,12 @@ module.exports = { textTransform: 'uppercase', letterSpacing: '9px', }, + '.bg-v1': { + backgroundImage: 'url(/images/bg-v1.svg)', + backgroundRepeat: 'no-repeat', + backgroundSize: '100% auto', + backgroundPosition: 'top', + }, }) }), plugin(({ matchUtilities, theme }) => { From 103246261f39c9248b80d17bff7ee7e41d11b5d1 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 23 Feb 2024 11:46:12 +0100 Subject: [PATCH 13/15] fix: introduce shortened connect button --- src/components/Wallet/WalletConnectButton.tsx | 1 + src/components/account/AccountBalancesTable/index.tsx | 1 + src/components/borrow/Table/Columns/BorrowButton.tsx | 1 + src/components/common/Button/ActionButton.tsx | 11 ++++++++--- src/components/earn/farm/Table/Columns/Deposit.tsx | 1 + src/components/earn/lend/Table/Columns/LendButton.tsx | 1 + .../v1/Table/borrowings/Columns/BorrowButton.tsx | 1 + .../v1/Table/deposits/Columns/DepositButton.tsx | 1 + 8 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/components/Wallet/WalletConnectButton.tsx b/src/components/Wallet/WalletConnectButton.tsx index 08bb00ef..68b0f38c 100644 --- a/src/components/Wallet/WalletConnectButton.tsx +++ b/src/components/Wallet/WalletConnectButton.tsx @@ -15,6 +15,7 @@ interface Props { color?: ButtonProps['color'] variant?: ButtonProps['variant'] size?: ButtonProps['size'] + short?: boolean } export default function WalletConnectButton(props: Props) { diff --git a/src/components/account/AccountBalancesTable/index.tsx b/src/components/account/AccountBalancesTable/index.tsx index a3e00be6..2eee9ced 100644 --- a/src/components/account/AccountBalancesTable/index.tsx +++ b/src/components/account/AccountBalancesTable/index.tsx @@ -75,6 +75,7 @@ export default function AccountBalancesTable(props: Props) { }, }) }} + short />
    diff --git a/src/components/borrow/Table/Columns/BorrowButton.tsx b/src/components/borrow/Table/Columns/BorrowButton.tsx index 421a277d..da13ebb6 100644 --- a/src/components/borrow/Table/Columns/BorrowButton.tsx +++ b/src/components/borrow/Table/Columns/BorrowButton.tsx @@ -54,6 +54,7 @@ export default function BorrowButton(props: Props) { e.stopPropagation() }} text='Borrow' + short />
    diff --git a/src/components/common/Button/ActionButton.tsx b/src/components/common/Button/ActionButton.tsx index 0c5e9b46..49c5c022 100644 --- a/src/components/common/Button/ActionButton.tsx +++ b/src/components/common/Button/ActionButton.tsx @@ -9,8 +9,12 @@ import useAccountIds from 'hooks/accounts/useAccountIds' import useAccountId from 'hooks/useAccountId' import useStore from 'store' -export default function ActionButton(props: ButtonProps) { - const { className, color, variant, size } = props +interface Props extends ButtonProps { + short?: boolean +} + +export default function ActionButton(props: Props) { + const { className, color, variant, size, short } = props const defaultProps = { className, color, variant, size } const address = useStore((s) => s.address) const isV1 = useStore((s) => s.isV1) @@ -22,7 +26,8 @@ export default function ActionButton(props: ButtonProps) { useStore.setState({ focusComponent: { component: } }) }, []) - if (!address) return + if (!address) + return if (accountIds && accountIds.length === 0) { return ( diff --git a/src/components/earn/farm/Table/Columns/Deposit.tsx b/src/components/earn/farm/Table/Columns/Deposit.tsx index 54ba037a..9d675f14 100644 --- a/src/components/earn/farm/Table/Columns/Deposit.tsx +++ b/src/components/earn/farm/Table/Columns/Deposit.tsx @@ -34,6 +34,7 @@ export const Deposit = (props: Props) => { color='tertiary' text='Deposit' leftIcon={} + short />
    ) diff --git a/src/components/earn/lend/Table/Columns/LendButton.tsx b/src/components/earn/lend/Table/Columns/LendButton.tsx index 3ca6e5c7..72e10617 100644 --- a/src/components/earn/lend/Table/Columns/LendButton.tsx +++ b/src/components/earn/lend/Table/Columns/LendButton.tsx @@ -56,6 +56,7 @@ export default function LendButton(props: Props) { e.stopPropagation() }} text='Lend' + short />
    diff --git a/src/components/v1/Table/borrowings/Columns/BorrowButton.tsx b/src/components/v1/Table/borrowings/Columns/BorrowButton.tsx index dc85b35d..4232532a 100644 --- a/src/components/v1/Table/borrowings/Columns/BorrowButton.tsx +++ b/src/components/v1/Table/borrowings/Columns/BorrowButton.tsx @@ -43,6 +43,7 @@ export default function BorrowButton(props: Props) { e.stopPropagation() }} text='Borrow' + short />
    diff --git a/src/components/v1/Table/deposits/Columns/DepositButton.tsx b/src/components/v1/Table/deposits/Columns/DepositButton.tsx index 4fa1af9a..60c876cf 100644 --- a/src/components/v1/Table/deposits/Columns/DepositButton.tsx +++ b/src/components/v1/Table/deposits/Columns/DepositButton.tsx @@ -43,6 +43,7 @@ export default function DepositButton(props: Props) { e.stopPropagation() }} text='Deposit' + short />
    From b4647ff4404b04f8bedc57b97fbedf8f54acbc12 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 23 Feb 2024 15:10:03 +0100 Subject: [PATCH 14/15] feat: created a new ChainSelect --- public/images/tokens/stkatom.svg | 42 +++++ public/images/tokens/wsteth.svg | 26 +++ .../Wallet/WalletFetchBalancesAndAccounts.tsx | 1 + .../borrow/Table/AvailableBorrowingsTable.tsx | 2 +- src/components/borrow/Table/Columns/Name.tsx | 7 +- .../borrow/Table/Columns/useActiveColumns.tsx | 2 +- .../Table/Columns/useAvailableColumns.tsx | 10 +- src/components/common/Footer.tsx | 2 +- .../earn/lend/Table/Columns/Name.tsx | 7 +- .../Table/Columns/useAvailableColumns.tsx | 5 +- .../Table/Columns/useDepositedColumns.tsx | 2 +- src/components/header/ChainSelect.tsx | 161 ++++++++++-------- src/components/v1/V1Intro.tsx | 6 +- .../assets/{WETH.xal.ts => WETH.axl.ts} | 0 src/configs/assets/stkATOM.ts | 16 ++ src/configs/assets/wstETH.ts | 18 ++ src/configs/chains/neutron/neutron-1.ts | 86 ++++++++++ src/configs/chains/osmosis/osmosis-1.ts | 2 +- src/constants/wallets.ts | 2 +- src/hooks/useClients.ts | 1 - src/pages/BorrowPage.tsx | 2 - src/pages/FarmPage.tsx | 4 +- src/pages/HLSFarmPage.tsx | 2 - src/pages/HLSStakingPage.tsx | 2 - src/pages/LendPage.tsx | 2 - src/pages/PortfolioAccountPage.tsx | 2 - src/pages/PortfolioPage.tsx | 4 +- src/pages/TradePage.tsx | 2 - src/pages/V1Page.tsx | 2 - src/utils/getCurrentChainId.ts | 8 + 30 files changed, 324 insertions(+), 104 deletions(-) create mode 100644 public/images/tokens/stkatom.svg create mode 100644 public/images/tokens/wsteth.svg rename src/configs/assets/{WETH.xal.ts => WETH.axl.ts} (100%) create mode 100644 src/configs/assets/stkATOM.ts create mode 100644 src/configs/assets/wstETH.ts create mode 100644 src/configs/chains/neutron/neutron-1.ts diff --git a/public/images/tokens/stkatom.svg b/public/images/tokens/stkatom.svg new file mode 100644 index 00000000..c0679269 --- /dev/null +++ b/public/images/tokens/stkatom.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + diff --git a/public/images/tokens/wsteth.svg b/public/images/tokens/wsteth.svg new file mode 100644 index 00000000..656b0244 --- /dev/null +++ b/public/images/tokens/wsteth.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + diff --git a/src/components/Wallet/WalletFetchBalancesAndAccounts.tsx b/src/components/Wallet/WalletFetchBalancesAndAccounts.tsx index 63876f95..e006fc75 100644 --- a/src/components/Wallet/WalletFetchBalancesAndAccounts.tsx +++ b/src/components/Wallet/WalletFetchBalancesAndAccounts.tsx @@ -75,6 +75,7 @@ function Content() { urlAddress, urlAccountId, searchParams, + isV1, ]) if (isLoadingAccounts || isLoadingBalances) return diff --git a/src/components/borrow/Table/AvailableBorrowingsTable.tsx b/src/components/borrow/Table/AvailableBorrowingsTable.tsx index b025e004..5a89521d 100644 --- a/src/components/borrow/Table/AvailableBorrowingsTable.tsx +++ b/src/components/borrow/Table/AvailableBorrowingsTable.tsx @@ -13,7 +13,7 @@ type Props = { } export default function AvailableBorrowingsTable(props: Props) { - const columns = useAvailableColumns() + const columns = useAvailableColumns({ v1: false }) const renderExpanded = useCallback( (row: Row, _: TanstackTable) => { diff --git a/src/components/borrow/Table/Columns/Name.tsx b/src/components/borrow/Table/Columns/Name.tsx index 6ec22845..d4541f1f 100644 --- a/src/components/borrow/Table/Columns/Name.tsx +++ b/src/components/borrow/Table/Columns/Name.tsx @@ -5,6 +5,7 @@ export const NAME_META = { accessorKey: 'asset.symbol', header: 'Asset', id: 'sy interface Props { data: BorrowMarketTableData + v1?: boolean } export default function Name(props: Props) { @@ -12,7 +13,11 @@ export default function Name(props: Props) { return (
    - +
    ) } diff --git a/src/components/borrow/Table/Columns/useActiveColumns.tsx b/src/components/borrow/Table/Columns/useActiveColumns.tsx index 5cc6633f..afcbef32 100644 --- a/src/components/borrow/Table/Columns/useActiveColumns.tsx +++ b/src/components/borrow/Table/Columns/useActiveColumns.tsx @@ -24,7 +24,7 @@ export default function useActiveColumns(props: Props) { return [ { ...NAME_META, - cell: ({ row }) => , + cell: ({ row }) => , }, { ...DEBT_VALUE_META, diff --git a/src/components/borrow/Table/Columns/useAvailableColumns.tsx b/src/components/borrow/Table/Columns/useAvailableColumns.tsx index 9cc2bd8e..c55f5870 100644 --- a/src/components/borrow/Table/Columns/useAvailableColumns.tsx +++ b/src/components/borrow/Table/Columns/useAvailableColumns.tsx @@ -10,12 +10,16 @@ import Liquidity, { } from 'components/borrow/Table/Columns/Liquidity' import Name, { NAME_META } from 'components/borrow/Table/Columns/Name' -export default function useAvailableColumns() { +interface Props { + v1?: boolean +} + +export default function useAvailableColumns(props: Props) { return useMemo[]>(() => { return [ { ...NAME_META, - cell: ({ row }) => , + cell: ({ row }) => , }, { ...BORROW_RATE_META, @@ -35,5 +39,5 @@ export default function useAvailableColumns() { cell: ({ row }) => , }, ] - }, []) + }, [props.v1]) } diff --git a/src/components/common/Footer.tsx b/src/components/common/Footer.tsx index ff936c6d..10fa7b7f 100644 --- a/src/components/common/Footer.tsx +++ b/src/components/common/Footer.tsx @@ -1,7 +1,7 @@ import { TextLink } from 'components/common/TextLink' import { DocURL } from 'types/enums/docURL' - import useStore from 'store' + import packageInfo from '../../../package.json' export default function Footer() { diff --git a/src/components/earn/lend/Table/Columns/Name.tsx b/src/components/earn/lend/Table/Columns/Name.tsx index a2f5db0c..ac8ecdf2 100644 --- a/src/components/earn/lend/Table/Columns/Name.tsx +++ b/src/components/earn/lend/Table/Columns/Name.tsx @@ -4,13 +4,18 @@ import TitleAndSubCell from 'components/common/TitleAndSubCell' export const NAME_META = { accessorKey: 'asset.symbol', header: 'Asset', id: 'symbol' } interface Props { asset: Asset + v1?: boolean } export default function Name(props: Props) { const { asset } = props return (
    - +
    ) } diff --git a/src/components/earn/lend/Table/Columns/useAvailableColumns.tsx b/src/components/earn/lend/Table/Columns/useAvailableColumns.tsx index 976402c1..f776ba0e 100644 --- a/src/components/earn/lend/Table/Columns/useAvailableColumns.tsx +++ b/src/components/earn/lend/Table/Columns/useAvailableColumns.tsx @@ -12,6 +12,7 @@ import Name, { NAME_META } from 'components/earn/lend/Table/Columns/Name' interface Props { isLoading: boolean + v1?: boolean } export default function useAvailableColumns(props: Props) { @@ -19,7 +20,7 @@ export default function useAvailableColumns(props: Props) { return [ { ...NAME_META, - cell: ({ row }) => , + cell: ({ row }) => , }, { ...APY_META, @@ -45,5 +46,5 @@ export default function useAvailableColumns(props: Props) { cell: ({ row }) => , }, ] - }, [props.isLoading]) + }, [props.isLoading, props.v1]) } diff --git a/src/components/earn/lend/Table/Columns/useDepositedColumns.tsx b/src/components/earn/lend/Table/Columns/useDepositedColumns.tsx index 638888fe..aa392747 100644 --- a/src/components/earn/lend/Table/Columns/useDepositedColumns.tsx +++ b/src/components/earn/lend/Table/Columns/useDepositedColumns.tsx @@ -25,7 +25,7 @@ export default function useDepositedColumns(props: Props) { return [ { ...NAME_META, - cell: ({ row }) => , + cell: ({ row }) => , }, { ...DEPOSIT_VALUE_META, diff --git a/src/components/header/ChainSelect.tsx b/src/components/header/ChainSelect.tsx index 77035442..c56844bd 100644 --- a/src/components/header/ChainSelect.tsx +++ b/src/components/header/ChainSelect.tsx @@ -1,11 +1,12 @@ import classNames from 'classnames' -import { useCallback, useMemo } from 'react' +import React, { useCallback, useMemo } from 'react' import { useNavigate, useSearchParams } from 'react-router-dom' import { useSWRConfig } from 'swr' import Button from 'components/common/Button' import { ExternalLink } from 'components/common/Icons' import Overlay from 'components/common/Overlay' +import Radio from 'components/common/Radio' import Text from 'components/common/Text' import ChainLogo from 'components/common/chain/ChainLogo' import chains from 'configs/chains' @@ -17,14 +18,35 @@ import { NETWORK } from 'types/enums/network' import { ChainInfoID } from 'types/enums/wallet' import { getRoute } from 'utils/route' -const v1Outposts = [ +interface V1Outpost { + chainId: ChainInfoID + name: string + url: string + target: '_blank' | '_self' +} + +interface ChainOptionProps { + chainConfig?: ChainConfig + onSelect?: (chain: ChainConfig) => void + active: boolean + outpost?: V1Outpost +} + +const v1Outposts: V1Outpost[] = [ { chainId: ChainInfoID.Neutron1, name: 'Neutron', url: 'https://neutron.marsprotocol.io', target: '_blank', }, + { + chainId: ChainInfoID.Pion1, + name: 'Neutron Testnet', + url: '/v1', + target: '_self', + }, { chainId: ChainInfoID.Osmosis1, name: 'Osmosis', url: '/v1', target: '_self' }, + { chainId: ChainInfoID.OsmosisDevnet, name: 'Osmosis Devnet', url: '/v1', target: '_self' }, ] export default function ChainSelect() { @@ -55,10 +77,51 @@ export default function ChainSelect() { [setCurrentChainId, setShowMenu, mutate, navigate, searchParams], ) - const currentChains = useMemo(() => { - const currentNetworkType = process.env.NEXT_PUBLIC_NETWORK ?? NETWORK.TESTNET + const ChainOption = (props: ChainOptionProps) => { + const { onSelect, active, outpost, chainConfig } = props + return ( +
    onSelect(chainConfig) + : () => { + if (chainConfig) { + setCurrentChainId(chainConfig.id) + useStore.setState({ + chainConfig, + }) + } + window.open(outpost?.url, outpost?.target) + } + } + > + + {outpost ? 'v1' : 'v2'} Outpost + {outpost && outpost.target !== '_self' && } +
    + ) + } - return Object.entries(chains).filter(([_, chain]) => chain.network === currentNetworkType) + const availableChains = useMemo(() => { + const currentNetworkType = process.env.NEXT_PUBLIC_NETWORK ?? NETWORK.TESTNET + const availableChains: { chainId: ChainInfoID; name: string }[] = [] + Object.entries(chains).forEach(([chainId, chainConfig]) => { + if (chainConfig.network !== currentNetworkType) return + availableChains.push({ chainId: chainId as ChainInfoID, name: chainConfig.name }) + }) + if (currentNetworkType === NETWORK.TESTNET) return availableChains + + v1Outposts.forEach((v1Outpost) => { + if (!availableChains.find((chain) => chain.chainId === v1Outpost.chainId)) + availableChains.push({ chainId: v1Outpost.chainId, name: v1Outpost.name }) + }) + + return availableChains }, []) return ( @@ -69,73 +132,37 @@ export default function ChainSelect() { color='secondary' onClick={() => setShowMenu()} className={classNames('!p-0 w-8 flex items-center justify-center')} - > - -
    - - Select Chain - -
    -
      - {currentChains.map(([name, chain]) => ( -
    • selectChain(chain)} - > - - {chain.name} -
    • - ))} -
    - {process.env.NEXT_PUBLIC_NETWORK === NETWORK.MAINNET && ( - <> + /> + + {availableChains.map((chain, index) => ( +
    0 && 'border-t', )} > - - V1 Outposts - + + {chain.name}
    -
      - {v1Outposts.map((outpost) => ( -
    • window.open(outpost.url, outpost.target)} - key={outpost.name} - > - - - {outpost.name}{' '} - {outpost.target !== '_self' && ( - - )} - -
    • - ))} -
    - - )} + {!!chains[chain.chainId] && ( + selectChain(chains[chain.chainId])} + active={chainConfig.name === chain.name && !isV1} + /> + )} + outpost.chainId === chain.chainId)} + active={chainConfig.name === chain.name && isV1} + /> +
    + ))}
    ) diff --git a/src/components/v1/V1Intro.tsx b/src/components/v1/V1Intro.tsx index f8212b8a..a5a67838 100644 --- a/src/components/v1/V1Intro.tsx +++ b/src/components/v1/V1Intro.tsx @@ -11,9 +11,9 @@ export default function V1Intro() { Welcome to the Red Bank!
    This is the first version (v1) of the Red Bank. It provides simple lending and borrowing, - without the use of Credit Accounts. Funds are{' '} - not cross-collateralized and can‘t be used on v2 - as collateral. + without the use of Credit Accounts. +
    + Deposited funds can‘t be used on v2 as collateral. } bg='v1' diff --git a/src/configs/assets/WETH.xal.ts b/src/configs/assets/WETH.axl.ts similarity index 100% rename from src/configs/assets/WETH.xal.ts rename to src/configs/assets/WETH.axl.ts diff --git a/src/configs/assets/stkATOM.ts b/src/configs/assets/stkATOM.ts new file mode 100644 index 00000000..4116de1b --- /dev/null +++ b/src/configs/assets/stkATOM.ts @@ -0,0 +1,16 @@ +const stkATOM: AssetMetaData = { + symbol: 'stkATOM', + name: 'Persistence Staked Atom', + id: 'stkATOM', + color: '#c73238', + logo: '/images/tokens/stkatom.svg', + decimals: 6, + hasOraclePrice: true, + isEnabled: true, + isMarket: true, + isDisplayCurrency: true, + isAutoLendEnabled: false, + isStaking: true, +} + +export default stkATOM diff --git a/src/configs/assets/wstETH.ts b/src/configs/assets/wstETH.ts new file mode 100644 index 00000000..d2d24e63 --- /dev/null +++ b/src/configs/assets/wstETH.ts @@ -0,0 +1,18 @@ +const wstETH: AssetMetaData = { + symbol: 'wstETH', + id: 'wstETH', + name: 'Lido Wrapped Staked Ethereum', + color: '#00a3ff', + logo: '/images/tokens/wsteth.svg', + decimals: 18, + hasOraclePrice: true, + isEnabled: true, + isMarket: true, + isDisplayCurrency: true, + isAutoLendEnabled: true, + isBorrowEnabled: true, + pythPriceFeedId: '0x6df640f3b8963d8f8358f791f352b8364513f6ab1cca5ed3f1f7b5448980e784', + pythFeedName: 'WSTETHUSD', +} + +export default wstETH diff --git a/src/configs/chains/neutron/neutron-1.ts b/src/configs/chains/neutron/neutron-1.ts new file mode 100644 index 00000000..00f0b9bb --- /dev/null +++ b/src/configs/chains/neutron/neutron-1.ts @@ -0,0 +1,86 @@ +import { Bech32Address } from '@keplr-wallet/cosmos' + +import ATOM from 'configs/assets/ATOM' +import DYDX from 'configs/assets/DYDX' +import NTRN from 'configs/assets/NTRN' +import USDCaxl from 'configs/assets/USDC.axl' +import USDollar from 'configs/assets/USDollar' +import WETHaxl from 'configs/assets/WETH.axl' +import stATOM from 'configs/assets/stATOM' +import stkATOM from 'configs/assets/stkATOM' +import wstETH from 'configs/assets/wstETH' +import { NETWORK } from 'types/enums/network' +import { ChainInfoID } from 'types/enums/wallet' + +const Neutron1: ChainConfig = { + assets: [ + { ...NTRN, denom: 'untrn' }, + { ...USDCaxl, denom: 'ibc/F082B65C88E4B6D5EF1DB243CDA1D331D002759E938A0F5CD3FFDC5D53B3E349' }, + { + ...ATOM, + denom: 'ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9', + }, + { + ...stATOM, + denom: 'ibc/B7864B03E1B9FD4F049243E92ABD691586F682137037A9F3FCA5222815620B3C', + }, + { + ...stkATOM, + denom: 'ibc/3649CE0C8A2C79048D8C6F31FF18FA69C9BC7EB193512E0BD03B733011290445', + }, + { ...WETHaxl, denom: 'ibc/A585C2D15DCD3B010849B453A2CFCB5E213208A5AB665691792684C26274304D' }, + { + ...wstETH, + denom: 'factory/neutron1ug740qrkquxzrk2hh29qrlx3sktkfml3je7juusc2te7xmvsscns0n2wry/wstETH', + }, + { ...DYDX, denom: 'ibc/2CB87BCE0937B1D1DFCEE79BE4501AAF3C265E923509AEAC410AD85D27F35130' }, + USDollar, + ], + id: ChainInfoID.Neutron1, + name: 'Neutron', + contracts: { + redBank: 'neutron1n97wnm7q6d2hrcna3rqlnyqw2we6k0l8uqvmyqq6gsml92epdu7quugyph', + incentives: 'neutron1aszpdh35zsaz0yj80mz7f5dtl9zq5jfl8hgm094y0j0vsychfekqxhzd39', + oracle: 'neutron1dwp6m7pdrz6rnhdyrx5ha0acsduydqcpzkylvfgspsz60pj2agxqaqrr7g', + swapper: 'neutron1udr9fc3kd743dezrj38v2ac74pxxr6qsx4xt4nfpcfczgw52rvyqyjp5au', + params: 'neutron16kqg3hr2qc36gz2wqvdzsctatkmzd3ss5gc07tnj6u3n5ajw89asrx8hfp', + creditManager: 'neutron1kj50g96c86nu7jmy5y7uy5cyjanntgru0eekmwz2qcmyyvx6383s8dgvm6', + accountNft: 'neutron17wvpxdc3k37054ume0ga4r0r6ra2rpfe622m0ecgd9s7xd5s0qusspc4ct', + perps: 'neutron14v9g7regs90qvful7djcajsvrfep5pg9qau7qm6wya6c2lzcpnms692dlt', + pyth: 'neutron1m2emc93m9gpwgsrsf2vylv9xvgqh654630v7dfrhrkmr5slly53spg85wv', + }, + endpoints: { + routes: 'https://app.astroport.fi/api/routes', + rpc: process.env.NEXT_PUBLIC_NEUTRON_RPC ?? 'https://rpc-kralum.neutron-1.neutron.org', + rest: process.env.NEXT_PUBLIC_NEUTRON_REST ?? 'https://rest-kralum.neutron-1.neutron.org', + swap: 'https://neutron.astroport.fi/swap', + pools: '', //TODO: ⛓️ Implement this + explorer: 'https://mintscan.io/neutron', + aprs: { + vaults: 'https://api.marsprotocol.io/v1/vaults/neutron', + stride: 'https://edge.stride.zone/api/stake-stats', + }, + }, + network: NETWORK.MAINNET, + vaults: [], + explorerName: 'Mintscan', + bech32Config: Bech32Address.defaultBech32Config('neutron'), + defaultCurrency: { + coinDenom: 'NTRN', + coinMinimalDenom: 'untrn', + coinDecimals: 6, + coinGeckoId: 'neutron', + gasPriceStep: { + low: 0, + average: 0.025, + high: 0.04, + }, + }, + features: ['ibc-transfer', 'ibc-go'], + gasPrice: '0.025untrn', + hls: false, + perps: false, + farm: false, +} + +export default Neutron1 diff --git a/src/configs/chains/osmosis/osmosis-1.ts b/src/configs/chains/osmosis/osmosis-1.ts index b25594e8..227c022b 100644 --- a/src/configs/chains/osmosis/osmosis-1.ts +++ b/src/configs/chains/osmosis/osmosis-1.ts @@ -13,7 +13,7 @@ import USDCaxl from 'configs/assets/USDC.axl' import USDT from 'configs/assets/USDT' import USDollar from 'configs/assets/USDollar' import WBTCaxl from 'configs/assets/WBTC.axl' -import WETHaxl from 'configs/assets/WETH.xal' +import WETHaxl from 'configs/assets/WETH.axl' import OSMO_ATOM from 'configs/assets/lp/OSMO-ATOM' import OSMO_USDC from 'configs/assets/lp/OSMO_USDC' import OSMO_WBTC from 'configs/assets/lp/OSMO_WBTC' diff --git a/src/constants/wallets.ts b/src/constants/wallets.ts index c641fbf9..062d7948 100644 --- a/src/constants/wallets.ts +++ b/src/constants/wallets.ts @@ -15,7 +15,7 @@ export const WALLETS: WalletInfos = { walletConnect: 'Cosmostation WalletConnect', imageURL: '/images/wallets/cosmostation.png', mobileImageURL: '/images/wallets/cosmostation-wc.png', - supportedChains: [ChainInfoID.Osmosis1, ChainInfoID.OsmosisDevnet], + supportedChains: [ChainInfoID.Osmosis1, ChainInfoID.OsmosisDevnet, ChainInfoID.Pion1], }, [WalletID.Keplr]: { name: 'Keplr Wallet', diff --git a/src/hooks/useClients.ts b/src/hooks/useClients.ts index 7fbee02f..4cd3d471 100644 --- a/src/hooks/useClients.ts +++ b/src/hooks/useClients.ts @@ -20,7 +20,6 @@ export default function useClients() { `chains/${chainConfig.id}/clients`, async () => { const client = await CosmWasmClient.connect(getUrl(chainConfig.endpoints.rpc)) - return { creditManager: new MarsCreditManagerQueryClient( client, diff --git a/src/pages/BorrowPage.tsx b/src/pages/BorrowPage.tsx index 8eefc851..eec33e71 100644 --- a/src/pages/BorrowPage.tsx +++ b/src/pages/BorrowPage.tsx @@ -1,11 +1,9 @@ import Borrowings from 'components/borrow/Borrowings' import BorrowIntro from 'components/borrow/BorrowIntro' -import MigrationBanner from 'components/common/MigrationBanner' export default function BorrowPage() { return (
    -
    diff --git a/src/pages/FarmPage.tsx b/src/pages/FarmPage.tsx index 4c6bcccd..07938c9c 100644 --- a/src/pages/FarmPage.tsx +++ b/src/pages/FarmPage.tsx @@ -1,13 +1,11 @@ +import Tab from 'components/earn/Tab' import FarmIntro from 'components/earn/farm/FarmIntro' import Vaults from 'components/earn/farm/Vaults' -import Tab from 'components/earn/Tab' -import MigrationBanner from 'components/common/MigrationBanner' import { EARN_TABS } from 'constants/pages' export default function FarmPage() { return (
    - diff --git a/src/pages/HLSFarmPage.tsx b/src/pages/HLSFarmPage.tsx index 40e75b38..290ceb4a 100644 --- a/src/pages/HLSFarmPage.tsx +++ b/src/pages/HLSFarmPage.tsx @@ -1,13 +1,11 @@ import Tab from 'components/earn/Tab' import AvailableHLSVaults from 'components/hls/Farm/AvailableHLSVaults' import HlsFarmIntro from 'components/hls/Farm/HLSFarmIntro' -import MigrationBanner from 'components/common/MigrationBanner' import { HLS_TABS } from 'constants/pages' export default function HLSFarmPage() { return (
    - diff --git a/src/pages/HLSStakingPage.tsx b/src/pages/HLSStakingPage.tsx index 3aeac7c8..d8f57501 100644 --- a/src/pages/HLSStakingPage.tsx +++ b/src/pages/HLSStakingPage.tsx @@ -2,13 +2,11 @@ import Tab from 'components/earn/Tab' import ActiveStakingAccounts from 'components/hls/Staking/ActiveStakingAccounts' import AvailableHlsStakingAssets from 'components/hls/Staking/AvailableHLSStakingAssets' import HLSStakingIntro from 'components/hls/Staking/HLSStakingIntro' -import MigrationBanner from 'components/common/MigrationBanner' import { HLS_TABS } from 'constants/pages' export default function HLSStakingPage() { return (
    - diff --git a/src/pages/LendPage.tsx b/src/pages/LendPage.tsx index 81a15210..450ede17 100644 --- a/src/pages/LendPage.tsx +++ b/src/pages/LendPage.tsx @@ -1,4 +1,3 @@ -import MigrationBanner from 'components/common/MigrationBanner' import Tab from 'components/earn/Tab' import LendIntro from 'components/earn/lend/LendIntro' import Lends from 'components/earn/lend/Lends' @@ -10,7 +9,6 @@ export default function LendPage() { return (
    - {chainConfig.farm && } diff --git a/src/pages/PortfolioAccountPage.tsx b/src/pages/PortfolioAccountPage.tsx index b0308320..ab0464b5 100644 --- a/src/pages/PortfolioAccountPage.tsx +++ b/src/pages/PortfolioAccountPage.tsx @@ -1,6 +1,5 @@ import { useNavigate, useParams, useSearchParams } from 'react-router-dom' -import MigrationBanner from 'components/common/MigrationBanner' import ShareBar from 'components/common/ShareBar' import Balances from 'components/portfolio/Account/Balances' import BreadCrumbs from 'components/portfolio/Account/BreadCrumbs' @@ -25,7 +24,6 @@ export default function PortfolioAccountPage() { return (
    - diff --git a/src/pages/PortfolioPage.tsx b/src/pages/PortfolioPage.tsx index 6e7269c2..0d05c6ae 100644 --- a/src/pages/PortfolioPage.tsx +++ b/src/pages/PortfolioPage.tsx @@ -1,13 +1,11 @@ -import MigrationBanner from 'components/common/MigrationBanner' +import ShareBar from 'components/common/ShareBar' import AccountOverview from 'components/portfolio/Overview' import PortfolioSummary from 'components/portfolio/Overview/Summary' import PortfolioIntro from 'components/portfolio/PortfolioIntro' -import ShareBar from 'components/common/ShareBar' export default function PortfolioPage() { return (
    - diff --git a/src/pages/TradePage.tsx b/src/pages/TradePage.tsx index 0a27a128..34eef272 100644 --- a/src/pages/TradePage.tsx +++ b/src/pages/TradePage.tsx @@ -1,7 +1,6 @@ import { useMemo } from 'react' import { useLocation } from 'react-router-dom' -import MigrationBanner from 'components/common/MigrationBanner' import AccountDetailsCard from 'components/trade/AccountDetailsCard' import TradeChart from 'components/trade/TradeChart' import TradeModule from 'components/trade/TradeModule' @@ -47,7 +46,6 @@ export default function TradePage() { ) return (
    -
    diff --git a/src/pages/V1Page.tsx b/src/pages/V1Page.tsx index 9ca42fb5..0236c50a 100644 --- a/src/pages/V1Page.tsx +++ b/src/pages/V1Page.tsx @@ -1,4 +1,3 @@ -import MigrationBanner from 'components/common/MigrationBanner' import Summary from 'components/portfolio/Account/Summary' import Borrowings from 'components/v1/Borrowings' import Deposits from 'components/v1/Deposits' @@ -9,7 +8,6 @@ export default function V1Page() { const address = useStore((s) => s.address) return (
    - {address && }
    diff --git a/src/utils/getCurrentChainId.ts b/src/utils/getCurrentChainId.ts index 2350c12a..701e5400 100644 --- a/src/utils/getCurrentChainId.ts +++ b/src/utils/getCurrentChainId.ts @@ -23,6 +23,10 @@ export const getCurrentChainId = () => { if (currentNetwork === NETWORK.TESTNET) chainId = ChainInfoID.OsmosisDevnet break + case 'neutron': + if (currentNetwork === NETWORK.MAINNET) chainId = ChainInfoID.Neutron1 + break + case 'testnet-neutron': if (currentNetwork === NETWORK.TESTNET) chainId = ChainInfoID.Pion1 break @@ -42,6 +46,10 @@ export const getCurrentChainId = () => { if (currentNetwork === NETWORK.TESTNET) chainId = ChainInfoID.OsmosisDevnet break + case ChainInfoID.Neutron1: + if (currentNetwork === NETWORK.MAINNET) chainId = ChainInfoID.Neutron1 + break + case ChainInfoID.Pion1: if (currentNetwork === NETWORK.TESTNET) chainId = ChainInfoID.Pion1 break From 4ebcc44ded53cb81d28d9db39747c0fc1915e7a6 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Fri, 23 Feb 2024 15:14:42 +0100 Subject: [PATCH 15/15] fix: filter available networks --- src/components/header/ChainSelect.tsx | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/components/header/ChainSelect.tsx b/src/components/header/ChainSelect.tsx index c56844bd..f036fa3d 100644 --- a/src/components/header/ChainSelect.tsx +++ b/src/components/header/ChainSelect.tsx @@ -22,6 +22,7 @@ interface V1Outpost { chainId: ChainInfoID name: string url: string + network: NETWORK.MAINNET | NETWORK.TESTNET target: '_blank' | '_self' } @@ -37,16 +38,30 @@ const v1Outposts: V1Outpost[] = [ chainId: ChainInfoID.Neutron1, name: 'Neutron', url: 'https://neutron.marsprotocol.io', + network: NETWORK.MAINNET, target: '_blank', }, { chainId: ChainInfoID.Pion1, name: 'Neutron Testnet', url: '/v1', + network: NETWORK.TESTNET, + target: '_self', + }, + { + chainId: ChainInfoID.Osmosis1, + name: 'Osmosis', + url: '/v1', + network: NETWORK.MAINNET, + target: '_self', + }, + { + chainId: ChainInfoID.OsmosisDevnet, + name: 'Osmosis Devnet', + network: NETWORK.TESTNET, + url: '/v1', target: '_self', }, - { chainId: ChainInfoID.Osmosis1, name: 'Osmosis', url: '/v1', target: '_self' }, - { chainId: ChainInfoID.OsmosisDevnet, name: 'Osmosis Devnet', url: '/v1', target: '_self' }, ] export default function ChainSelect() { @@ -117,7 +132,10 @@ export default function ChainSelect() { if (currentNetworkType === NETWORK.TESTNET) return availableChains v1Outposts.forEach((v1Outpost) => { - if (!availableChains.find((chain) => chain.chainId === v1Outpost.chainId)) + if ( + !availableChains.find((chain) => chain.chainId === v1Outpost.chainId) && + v1Outpost.network === currentNetworkType + ) availableChains.push({ chainId: v1Outpost.chainId, name: v1Outpost.name }) })