From 3dc1752ae2c52d25b4e3ad86b70e84e07c71d2fb Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Tue, 24 Oct 2023 16:41:40 +0200 Subject: [PATCH] v2.0.1 (#579) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Build(deps): bump @splinetool/runtime from 0.9.477 to 0.9.482 (#544) Bumps @splinetool/runtime from 0.9.477 to 0.9.482. --- updated-dependencies: - dependency-name: "@splinetool/runtime" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Build(deps-dev): bump @types/node from 20.7.0 to 20.8.6 (#548) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.7.0 to 20.8.6. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Build(deps): bump @sentry/nextjs from 7.73.0 to 7.74.0 (#545) Bumps [@sentry/nextjs](https://github.com/getsentry/sentry-javascript) from 7.73.0 to 7.74.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.73.0...7.74.0) --- updated-dependencies: - dependency-name: "@sentry/nextjs" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Mp 3245 usehlsvaults hook (#541) * ✨ routing and pages for HLS * ✨ create hooks for fetching HLS vaults and Strategies * Share accounts (#539) * feat: do not redirect to wallet on portfolio page * fix: use connected wallet for AccountMenu * fix: fixed ghost AccountDetails * feat: created ShareBar and share functionality * fix: don’t show shareBar if no address is present * fix: stupid 'next/navigation' * tidy: format * fix: fixed tests * ✨ routing and pages for HLS (#538) * πŸ› use useAccountIds * fix: fixed the tests * fix: accountIds is now a suspense --------- Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com> * πŸ› fix build --------- Co-authored-by: Linkie Link * Mp 2837 pre commit hook (#549) * MP-2837: added husys and lint-staged * MP-2837: enabled lint-staged * MP-2837: setup .prettierignore * MP-2837: setup .prettierignore * MP-3483: all Depo. Caps are now % filled (#551) * MP-3487: changed the copy of the bridging intro screen (#553) * MP-3482: replaced Max LTV with Max. Leverage (#550) * added hatched health masks (#552) * added hatched health masks * Mp 2837 pre commit hook (#549) * MP-2837: added husys and lint-staged * MP-2837: enabled lint-staged * MP-2837: setup .prettierignore * MP-2837: setup .prettierignore * MP-3483: all Depo. Caps are now % filled (#551) * MP-3487: changed the copy of the bridging intro screen (#553) * MP-3482: replaced Max LTV with Max. Leverage (#550) * sneak: change filled to used * fix: fixed the foregroundColor on increase and my ocd * ♻️ refactor table (Farm) (#555) * ♻️ refactor table (Farm) * 🧽 clean up PR * 🧽 clean up PR * Build(deps): bump @babel/traverse from 7.21.2 to 7.23.2 (#554) Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.21.2 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: rename withdraw to unlend (#557) * Full refactor tables (#556) * πŸ“ˆ Improve structure generic Table component * ♻️ Update Borrow Table and overall structure of Table comp * ♻️ Update Lend table * ✨ add loading state for lend table * πŸ§ͺ Fix unit tests * ✨ Add available HLS Vaults page (#558) * Table updates (#559) * fix: adjusted table colors and hover interactions * fix: added actionButtons back and changed lend to APY * fix: build update * tidy: fixed the CircularProgress indicators on the loading modals * fix: relative import * env: updated shuttle, keplr and version (#566) * fix: fixed dust left when trying to buy max amount without leverage (#565) * feat: added squidrouter to the bridges (#561) * feat: added squidrouter to the bridges * fix: copy update * MP-3521: updated the APR calculation (#572) * Table fixes (#563) * fix: fixed the sorting of the tables * fix: added sorting functions * fix: farm sorting for deposit cap * fix: fixed Row types * Build(deps-dev): bump prettier-plugin-tailwindcss from 0.5.5 to 0.5.6 (#567) Bumps [prettier-plugin-tailwindcss](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) from 0.5.5 to 0.5.6. - [Release notes](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/compare/v0.5.5...v0.5.6) --- updated-dependencies: - dependency-name: prettier-plugin-tailwindcss dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Build(deps): bump react-router-dom from 6.16.0 to 6.17.0 (#571) Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.16.0 to 6.17.0. - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.17.0/packages/react-router-dom) --- updated-dependencies: - dependency-name: react-router-dom dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * ✨ HLS: Add info modal (#573) * MP-3484: remember summaryAccount tabs and auto expand both (#574) * User feedback (#575) * feat: added debt indicator and adjusted the borrowModal * fix: wallet interaction fix * Add usdc noble (#576) * env: added USDC.n * env: updated usdc noble variables * fix: fixed the pool on USDC for devnet purposes * πŸ› Fix initial status of chart (#577) * Mp 3480 persist last trading pair (#578) * MP-3480: remove favourite asset and prepare localStore * env: updated shuttle, keplr and version (#566) * fix: fixed dust left when trying to buy max amount without leverage (#565) * feat: added squidrouter to the bridges (#561) * feat: added squidrouter to the bridges * fix: copy update * MP-3521: updated the APR calculation (#572) * Table fixes (#563) * fix: fixed the sorting of the tables * fix: added sorting functions * fix: farm sorting for deposit cap * fix: fixed Row types * Build(deps-dev): bump prettier-plugin-tailwindcss from 0.5.5 to 0.5.6 (#567) Bumps [prettier-plugin-tailwindcss](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) from 0.5.5 to 0.5.6. - [Release notes](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/compare/v0.5.5...v0.5.6) --- updated-dependencies: - dependency-name: prettier-plugin-tailwindcss dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Build(deps): bump react-router-dom from 6.16.0 to 6.17.0 (#571) Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.16.0 to 6.17.0. - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.17.0/packages/react-router-dom) --- updated-dependencies: - dependency-name: react-router-dom dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * ✨ HLS: Add info modal (#573) * MP-3484: remember summaryAccount tabs and auto expand both (#574) * User feedback (#575) * feat: added debt indicator and adjusted the borrowModal * fix: wallet interaction fix * Add usdc noble (#576) * env: added USDC.n * env: updated usdc noble variables * fix: fixed the pool on USDC for devnet purposes * πŸ› Fix initial status of chart (#577) * MP-3480: persist trading pair * fix: updated according to feedback * fix: remove pair from Trading View header --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com> --- .env.example | 65 +- .husky/pre-commit | 4 + .prettierignore | 3 +- .../components/CircularProgress.test.tsx | 8 +- .../MarketAssetTable/MarketDetails.test.tsx | 38 - .../Modals/Unlock/UnlockModal.test.tsx | 1 + .../Modals/vault/VaultBorrowings.test.tsx | 88 - .../Tooltip/TooltipContent.test.tsx | 1 - package.json | 27 +- public/images/bridges/squid.png | Bin 0 -> 1846 bytes public/images/tokens/nusdc.svg | 25 - public/images/tokens/usdc.svg | 28 + src/api/hls/getHLSStakingAssets.ts | 28 + src/api/vaults/getVaults.ts | 16 + src/components/AccordionContent.tsx | 7 +- .../Account/AccountBalancesTable/index.tsx | 4 +- src/components/Account/AccountComposition.tsx | 8 +- .../Account/AccountDetails/index.tsx | 17 +- .../AccountFund/AccountFundContent.tsx | 4 +- .../Account/AccountList/AccountStats.tsx | 8 +- src/components/Account/AccountMenuContent.tsx | 7 +- src/components/Account/AccountSummary.tsx | 34 +- src/components/Account/HealthBar.tsx | 50 +- src/components/Account/RiskChart.tsx | 9 +- src/components/AmountAndValue.tsx | 7 +- src/components/Background.tsx | 7 +- src/components/Borrow/BorrowTable.tsx | 129 -- src/components/Borrow/Borrowings.tsx | 37 + .../Borrow/Table/AvailableBorrowingsTable.tsx | 46 + .../Borrow/Table/Columns/BorrowRate.tsx | 25 + src/components/Borrow/Table/Columns/Debt.tsx | 42 + .../Borrow/Table/Columns/Liquidity.tsx | 44 + .../Borrow/Table/Columns/Manage.tsx | 17 + src/components/Borrow/Table/Columns/Name.tsx | 18 + .../Table/Columns/useAvailableColumns.tsx | 34 + .../Table/Columns/useDepositedColumns.tsx | 40 + .../Borrow/Table/DepositedBorrowingsTable.tsx | 42 + src/components/BorrowCapacity.tsx | 7 +- src/components/Button/index.tsx | 7 +- src/components/CheckMark.tsx | 7 +- src/components/Checkbox.tsx | 38 +- src/components/CircularProgress.tsx | 7 +- src/components/DisplayCurrency.tsx | 4 +- .../Earn/Farm/Table/AvailableVaultsTable.tsx | 22 + .../Earn/Farm/Table/Columns/Apy.tsx | 25 + .../Earn/Farm/Table/Columns/Deposit.tsx | 32 + .../Earn/Farm/Table/Columns/DepositCap.tsx | 57 + .../Earn/Farm/Table/Columns/Details.tsx | 24 + .../Earn/Farm/Table/Columns/MaxLTV.tsx | 23 + .../Earn/Farm/Table/Columns/Name.tsx | 72 + .../Earn/Farm/Table/Columns/PositionValue.tsx | 23 + .../Earn/Farm/Table/Columns/TVL.tsx | 20 + .../Table/Columns/useAvailableColumns.tsx | 50 + .../Table/Columns/useDepositedColumns.tsx | 63 + .../Earn/Farm/Table/DepositedVaultsTable.tsx | 33 + src/components/Earn/Farm/VaultExpanded.tsx | 12 +- src/components/Earn/Farm/VaultRow.tsx | 4 +- src/components/Earn/Farm/VaultTable.tsx | 303 --- .../Earn/Farm/VaultUnlockBanner.tsx | 4 +- src/components/Earn/Farm/Vaults.tsx | 47 +- .../Earn/Lend/LendingActionButtons.tsx | 13 +- .../Earn/Lend/LendingMarketsTable.tsx | 140 -- src/components/Earn/Lend/Lends.tsx | 37 + .../Earn/Lend/Table/AvailableLendsTable.tsx | 42 + .../Earn/Lend/Table/Columns/Apy.tsx | 24 + .../Earn/Lend/Table/Columns/DepositCap.tsx | 55 + .../Earn/Lend/Table/Columns/DepositValue.tsx | 32 + .../Earn/Lend/Table/Columns/Manage.tsx | 16 + .../Earn/Lend/Table/Columns/Name.tsx | 16 + .../Table/Columns/useAvailableColumns.tsx | 44 + .../Table/Columns/useDepositedColumns.tsx | 55 + .../Earn/Lend/Table/DepositedLendsTable.tsx | 42 + src/components/FormattedNumber.tsx | 4 +- src/components/Gauge.tsx | 7 +- .../HLS/Farm/AvailableHLSVaults.tsx | 64 + src/components/HLS/Farm/HLSFarmIntro.tsx | 31 + src/components/HLS/Farm/Table/Columns/APY.tsx | 40 + .../HLS/Farm/Table/Columns/Deposit.tsx | 104 + .../HLS/Farm/Table/Columns/MaxLeverage.tsx | 19 + .../Table/Columns/useAvailableColumns.tsx | 51 + src/components/HLS/Farm/Table/index.tsx | 7 + .../HLS/Staking/AvailableHLSStakingAssets.tsx | 3 + .../HLS/Staking/HLSStakingIntro.tsx | 31 + src/components/HealthGauge.tsx | 143 +- .../MarketAssetTable/MarketAssetTableRow.tsx | 73 - .../{MarketAssetTable => }/MarketDetails.tsx | 47 +- src/components/MigrationBanner.tsx | 4 +- .../Account/AccountDeleteAlertDialog.tsx | 15 +- .../Modals/Account/AccountDeleteModal.tsx | 11 +- .../AddVaultBorrowAssetsModalContent.tsx | 4 +- .../Modals/AddVaultAssets/index.tsx | 4 +- src/components/Modals/AlertDialog/index.tsx | 53 +- .../Modals/AssetsSelect/AssetSelectTable.tsx | 5 +- .../AssetsSelect/useAssetTableColumns.tsx | 9 +- src/components/Modals/BorrowModal.tsx | 37 +- src/components/Modals/FundWithdraw/index.tsx | 4 +- .../Modals/LendAndReclaim/index.tsx | 2 +- src/components/Modals/Settings/index.tsx | 80 +- .../Modals/Vault/VaultModalContent.tsx | 4 +- .../WalletAssets/WalletAssetsModalContent.tsx | 2 + .../Modals/WithdrawFromVaultsModal.tsx | 8 +- src/components/Portfolio/Account/Balances.tsx | 5 +- src/components/Portfolio/Account/Summary.tsx | 3 +- src/components/Portfolio/Card/index.tsx | 10 +- src/components/Portfolio/Overview/Summary.tsx | 3 +- src/components/Table/ActionButtonRow.tsx | 25 + src/components/Table/Row.tsx | 42 + .../{MarketAssetTable => Table}/index.tsx | 52 +- src/components/TableSkeleton.tsx | 5 +- src/components/TermsOfService.tsx | 4 +- src/components/Toaster/index.tsx | 9 +- src/components/Tooltip/index.tsx | 7 +- src/components/Trade/AccountDetailsCard.tsx | 8 +- .../Trade/TradeChart/TVChartContainer.tsx | 8 +- .../TradeModule/AssetSelector/AssetItem.tsx | 4 +- .../Trade/TradeModule/AssetSelector/index.tsx | 33 +- .../TradeModule/SwapForm/AssetAmountInput.tsx | 2 +- .../TradeModule/SwapForm/TradeSummary.tsx | 2 +- .../Trade/TradeModule/SwapForm/index.tsx | 9 +- src/components/Trade/TradeModule/index.tsx | 12 +- src/components/Wallet/RecentTransactions.tsx | 11 +- src/components/Wallet/WalletBridges.tsx | 7 +- src/components/Wallet/WalletConnectButton.tsx | 4 +- .../Wallet/WalletConnectedButton.tsx | 12 +- src/components/Wallet/index.tsx | 5 +- src/constants/assets.ts | 27 + src/constants/bridges.ts | 5 + src/constants/defaultSettings.ts | 7 +- src/constants/env.ts | 8 - src/constants/localStorageKeys.ts | 15 + src/constants/localStore.ts | 11 - src/hooks/broadcast/useDepositVault.ts | 4 +- src/hooks/useAssets.ts | 6 +- src/hooks/useAutoLend.ts | 4 +- src/hooks/useBorrowEnabledMarkets.ts | 6 + src/hooks/useBorrowMarketAssetsTableData.ts | 79 +- src/hooks/useDisplayAsset.tsx | 4 +- src/hooks/useDisplayCurrencyPrice.ts | 7 +- src/hooks/useHLSStakingAssets.ts | 10 + src/hooks/useIsOpenArray.tsx | 5 +- src/hooks/useTransactionStore.ts | 11 +- src/hooks/useUpdatedAccount/index.ts | 4 +- src/pages/BorrowPage.tsx | 8 +- src/pages/FarmPage.tsx | 5 +- src/pages/HLSFarmPage.tsx | 4 + src/pages/HLSStakingPage.tsx | 4 + src/pages/LendPage.tsx | 7 +- src/pages/TradePage.tsx | 30 +- src/pages/_layout.tsx | 9 +- src/store/slices/broadcast.ts | 2 +- src/store/slices/modal.ts | 5 +- src/styles/globals.css | 4 + src/types/interfaces/asset.d.ts | 23 +- src/types/interfaces/store/modals.d.ts | 11 +- src/types/interfaces/store/settings.d.ts | 3 +- src/types/interfaces/vaults.d.ts | 5 + src/utils/accounts.ts | 6 +- src/utils/assets.ts | 4 + src/utils/checkAutoLendEnabled.ts | 4 +- src/utils/healthIndicator.ts | 4 +- src/utils/helpers.ts | 4 + src/utils/resolvers.ts | 43 +- tailwind.config.js | 4 +- yarn.lock | 1979 +++++++++-------- 164 files changed, 3656 insertions(+), 2241 deletions(-) create mode 100755 .husky/pre-commit delete mode 100644 __tests__/components/MarketAssetTable/MarketDetails.test.tsx delete mode 100644 __tests__/components/Modals/vault/VaultBorrowings.test.tsx create mode 100644 public/images/bridges/squid.png delete mode 100644 public/images/tokens/nusdc.svg create mode 100644 public/images/tokens/usdc.svg create mode 100644 src/api/hls/getHLSStakingAssets.ts delete mode 100644 src/components/Borrow/BorrowTable.tsx create mode 100644 src/components/Borrow/Borrowings.tsx create mode 100644 src/components/Borrow/Table/AvailableBorrowingsTable.tsx create mode 100644 src/components/Borrow/Table/Columns/BorrowRate.tsx create mode 100644 src/components/Borrow/Table/Columns/Debt.tsx create mode 100644 src/components/Borrow/Table/Columns/Liquidity.tsx create mode 100644 src/components/Borrow/Table/Columns/Manage.tsx create mode 100644 src/components/Borrow/Table/Columns/Name.tsx create mode 100644 src/components/Borrow/Table/Columns/useAvailableColumns.tsx create mode 100644 src/components/Borrow/Table/Columns/useDepositedColumns.tsx create mode 100644 src/components/Borrow/Table/DepositedBorrowingsTable.tsx create mode 100644 src/components/Earn/Farm/Table/AvailableVaultsTable.tsx create mode 100644 src/components/Earn/Farm/Table/Columns/Apy.tsx create mode 100644 src/components/Earn/Farm/Table/Columns/Deposit.tsx create mode 100644 src/components/Earn/Farm/Table/Columns/DepositCap.tsx create mode 100644 src/components/Earn/Farm/Table/Columns/Details.tsx create mode 100644 src/components/Earn/Farm/Table/Columns/MaxLTV.tsx create mode 100644 src/components/Earn/Farm/Table/Columns/Name.tsx create mode 100644 src/components/Earn/Farm/Table/Columns/PositionValue.tsx create mode 100644 src/components/Earn/Farm/Table/Columns/TVL.tsx create mode 100644 src/components/Earn/Farm/Table/Columns/useAvailableColumns.tsx create mode 100644 src/components/Earn/Farm/Table/Columns/useDepositedColumns.tsx create mode 100644 src/components/Earn/Farm/Table/DepositedVaultsTable.tsx delete mode 100644 src/components/Earn/Farm/VaultTable.tsx delete mode 100644 src/components/Earn/Lend/LendingMarketsTable.tsx create mode 100644 src/components/Earn/Lend/Lends.tsx create mode 100644 src/components/Earn/Lend/Table/AvailableLendsTable.tsx create mode 100644 src/components/Earn/Lend/Table/Columns/Apy.tsx create mode 100644 src/components/Earn/Lend/Table/Columns/DepositCap.tsx create mode 100644 src/components/Earn/Lend/Table/Columns/DepositValue.tsx create mode 100644 src/components/Earn/Lend/Table/Columns/Manage.tsx create mode 100644 src/components/Earn/Lend/Table/Columns/Name.tsx create mode 100644 src/components/Earn/Lend/Table/Columns/useAvailableColumns.tsx create mode 100644 src/components/Earn/Lend/Table/Columns/useDepositedColumns.tsx create mode 100644 src/components/Earn/Lend/Table/DepositedLendsTable.tsx create mode 100644 src/components/HLS/Farm/AvailableHLSVaults.tsx create mode 100644 src/components/HLS/Farm/HLSFarmIntro.tsx create mode 100644 src/components/HLS/Farm/Table/Columns/APY.tsx create mode 100644 src/components/HLS/Farm/Table/Columns/Deposit.tsx create mode 100644 src/components/HLS/Farm/Table/Columns/MaxLeverage.tsx create mode 100644 src/components/HLS/Farm/Table/Columns/useAvailableColumns.tsx create mode 100644 src/components/HLS/Farm/Table/index.tsx create mode 100644 src/components/HLS/Staking/AvailableHLSStakingAssets.tsx create mode 100644 src/components/HLS/Staking/HLSStakingIntro.tsx delete mode 100644 src/components/MarketAssetTable/MarketAssetTableRow.tsx rename src/components/{MarketAssetTable => }/MarketDetails.tsx (78%) create mode 100644 src/components/Table/ActionButtonRow.tsx create mode 100644 src/components/Table/Row.tsx rename src/components/{MarketAssetTable => Table}/index.tsx (67%) create mode 100644 src/constants/localStorageKeys.ts delete mode 100644 src/constants/localStore.ts create mode 100644 src/hooks/useBorrowEnabledMarkets.ts create mode 100644 src/hooks/useHLSStakingAssets.ts diff --git a/.env.example b/.env.example index 03d1da3d..079078d3 100644 --- a/.env.example +++ b/.env.example @@ -1,46 +1,37 @@ +# DEVNET # NEXT_PUBLIC_NETWORK=devnet NEXT_PUBLIC_CHAIN_ID=devnet NEXT_PUBLIC_RPC=https://rpc.devnet.osmosis.zone/ NEXT_PUBLIC_GQL=https://devnet-osmosis-gql.marsprotocol.io/graphql NEXT_PUBLIC_REST=https://lcd.devnet.osmosis.zone/ -NEXT_PUBLIC_SWAP=https://testnet.osmosis.zone -NEXT_PUBLIC_VAULT_APR=https://api.marsprotocol.io/v1/vaults/osmosis -NEXT_PUBLIC_ACCOUNT_NFT=osmo1pdr8mvj2ky9hzj5pjp026apfmd0pacd3xrzx3mzazy7lulnsdrkq96gzk3 -NEXT_PUBLIC_ORACLE=osmo156elt2tp5455q9a6vfrvnpncxyd33cxm9z2lgguwg6dgws9tedps5tq3rc -NEXT_PUBLIC_RED_BANK=osmo1vxpdcw092n9rngvekve8g324c2yjx56496ck98ag4sdxr4p4zd4q0wr7x6 -NEXT_PUBLIC_CREDIT_MANAGER=osmo1m83kw2vehyt9urxf79qa9rxk8chgs4464e5h8s37yhnw3pwauuqq7lux8r -NEXT_PUBLIC_INCENTIVES=osmo1r9w7k774vcxeuvq6ctq0z2j6wkkxpskucgjkqt0uu7u07l03s3js6ukge4 -NEXT_PUBLIC_ZAPPER=osmo1q4kkvuy8wc9fs8sfm7zyeh4k25vssd0l68nrph8s7unvq5jdq67swrepj4 -NEXT_PUBLIC_SWAPPER=osmo1wee0z8c7tcawyl647eapqs4a88q8jpa7ddy6nn2nrs7t47p2zhxswetwla -NEXT_PUBLIC_PARAMS=osmo1pzszwkyy0x9cu6p2uknwa3wccr79xwmqn9gj66fnjnayr28tzp6qh2n4qg -NEXT_PUBLIC_PYTH=osmo13ge29x4e2s63a8ytz2px8gurtyznmue4a69n5275692v3qn3ks8q7cwck7 -NEXT_PUBLIC_API=http://localhost:3000/api -NEXT_PUBLIC_PYTH_ENDPOINT=https://xc-mainnet.pyth.network/api -NEXT_PUBLIC_MAINNET_REST=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-lcd-front/ -NEXT_PUBLIC_CANDLES_ENDPOINT=https://api.thegraph.com/subgraphs/name/donovansolms/osmosis-tv-candles-test -NEXT_PUBLIC_WALLET_CONNECT_ID=d93fdffb159bae5ec87d8fee4cdbb045 +NEXT_PUBLIC_ZAPPER=osmo1yhh8mhthj5jn5c6ty59z3tpsk554qxmlkrkcderw6jls0pcg8zxsdjdj94 +NEXT_PUBLIC_PARAMS=osmo1aye5qcer5n52crrkaf35jprsad2807q6kg3eeeu7k79h4slxfausfqhc9y -CHARTING_LIBRARY_REPOSITORY=github.com/tradingview/charting_library -CHARTING_LIBRARY_ACCESS_TOKEN=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 -CHARTING_LIBRARY_USERNAME=mars-git-demo # MAINNET # -# NEXT_PUBLIC_NETWORK=mainnet -# NEXT_PUBLIC_CHAIN_ID=osmosis-1 -# NEXT_PUBLIC_RPC=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-rpc-front/ -# NEXT_PUBLIC_GQL=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-hive-front/graphql -# NEXT_PUBLIC_REST=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-lcd-front/ -# NEXT_PUBLIC_SWAP=https://app.osmosis.zone -# NEXT_PUBLIC_VAULT_APR=https://api.marsprotocol.io/v1/vaults/osmosis -# NEXT_PUBLIC_ACCOUNT_NFT=osmo1450hrg6dv2l58c0rvdwx8ec2a0r6dd50hn4frk370tpvqjhy8khqw7sw09 -# NEXT_PUBLIC_ORACLE=osmo1mhznfr60vjdp2gejhyv2gax9nvyyzhd3z0qcwseyetkfustjauzqycsy2g -# NEXT_PUBLIC_RED_BANK=osmo1c3ljch9dfw5kf52nfwpxd2zmj2ese7agnx0p9tenkrryasrle5sqf3ftpg -# NEXT_PUBLIC_CREDIT_MANAGER=osmo1f2m24wktq0sw3c0lexlg7fv4kngwyttvzws3a3r3al9ld2s2pvds87jqvf -# NEXT_PUBLIC_INCENTIVES=osmo1nkahswfr8shg8rlxqwup0vgahp0dk4x8w6tkv3rra8rratnut36sk22vrm -# NEXT_PUBLIC_ZAPPER=osmo17qwvc70pzc9mudr8t02t3pl74hhqsgwnskl734p4hug3s8mkerdqzduf7c -# NEXT_PUBLIC_SWAPPER=osmo1wee0z8c7tcawyl647eapqs4a88q8jpa7ddy6nn2nrs7t47p2zhxswetwla -# NEXT_PUBLIC_API=http://localhost:3000/api -# NEXT_PUBLIC_PYTH_ENDPOINT=https://xc-mainnet.pyth.network/api -# NEXT_PUBLIC_MAINNET_REST=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-lcd-front/ -# NEXT_PUBLIC_WALLET_CONNECT_ID=d93fdffb159bae5ec87d8fee4cdbb045 +NEXT_PUBLIC_NETWORK=mainnet +NEXT_PUBLIC_CHAIN_ID=osmosis-1 +NEXT_PUBLIC_RPC=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-rpc-front/ +NEXT_PUBLIC_GQL=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-hive-front/graphql +NEXT_PUBLIC_REST=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-lcd-front/ +NEXT_PUBLIC_ZAPPER=osmo17qwvc70pzc9mudr8t02t3pl74hhqsgwnskl734p4hug3s8mkerdqzduf7c +NEXT_PUBLIC_PARAMS=osmo1nlmdxt9ctql2jr47qd4fpgzg84cjswxyw6q99u4y4u4q6c2f5ksq7ysent + +# COMMON # +NEXT_PUBLIC_SWAP=https://app.osmosis.zone +NEXT_PUBLIC_VAULT_APR=https://api.marsprotocol.io/v1/vaults/osmosis +NEXT_PUBLIC_ACCOUNT_NFT=osmo1450hrg6dv2l58c0rvdwx8ec2a0r6dd50hn4frk370tpvqjhy8khqw7sw09 +NEXT_PUBLIC_ORACLE=osmo1mhznfr60vjdp2gejhyv2gax9nvyyzhd3z0qcwseyetkfustjauzqycsy2g +NEXT_PUBLIC_RED_BANK=osmo1c3ljch9dfw5kf52nfwpxd2zmj2ese7agnx0p9tenkrryasrle5sqf3ftpg +NEXT_PUBLIC_CREDIT_MANAGER=osmo1f2m24wktq0sw3c0lexlg7fv4kngwyttvzws3a3r3al9ld2s2pvds87jqvf +NEXT_PUBLIC_INCENTIVES=osmo1nkahswfr8shg8rlxqwup0vgahp0dk4x8w6tkv3rra8rratnut36sk22vrm +NEXT_PUBLIC_SWAPPER=osmo1wee0z8c7tcawyl647eapqs4a88q8jpa7ddy6nn2nrs7t47p2zhxswetwla +NEXT_PUBLIC_PYTH=osmo13ge29x4e2s63a8ytz2px8gurtyznmue4a69n5275692v3qn3ks8q7cwck7 +NEXT_PUBLIC_PYTH_ENDPOINT=https://hermes.pyth.network/api +NEXT_PUBLIC_MAINNET_REST=https://osmosis.rpc.p2p.world/4dqst8e8Cgd2HMb2HDNkimP7NIkcbjuk/lcd/ +NEXT_PUBLIC_CANDLES_ENDPOINT=https://osmosis-candles.marsprotocol.io/ +NEXT_PUBLIC_WALLET_CONNECT_ID=d93fdffb159bae5ec87d8fee4cdbb045 +CHARTING_LIBRARY_REPOSITORY=github.com/tradingview/charting_library +CHARTING_LIBRARY_ACCESS_TOKEN=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 +CHARTING_LIBRARY_USERNAME=mars-git-demo \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..25d22357 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npx lint-staged \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index 671e5d89..5b87e7c1 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ src/utils/charting_library src/utils/datafeeds -src/types/generated \ No newline at end of file +src/types/generated +.husky \ No newline at end of file diff --git a/__tests__/components/CircularProgress.test.tsx b/__tests__/components/CircularProgress.test.tsx index 9ff05356..98251b19 100644 --- a/__tests__/components/CircularProgress.test.tsx +++ b/__tests__/components/CircularProgress.test.tsx @@ -1,11 +1,11 @@ import { render } from '@testing-library/react' import { CircularProgress } from 'components/CircularProgress' -import { REDUCE_MOTION_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' describe('', () => { afterAll(() => { - localStorage.removeItem(REDUCE_MOTION_KEY) + localStorage.removeItem(LocalStorageKeys.REDUCE_MOTION) }) it('should render', () => { @@ -15,7 +15,7 @@ describe('', () => { }) it('should render `...` when animations disabled', () => { - localStorage.setItem(REDUCE_MOTION_KEY, 'true') + localStorage.setItem(LocalStorageKeys.REDUCE_MOTION, 'true') const { getByText } = render() const threeDots = getByText('...') @@ -24,7 +24,7 @@ describe('', () => { }) it('should render the component with animation classes when animations enabled', () => { - localStorage.setItem(REDUCE_MOTION_KEY, 'false') + localStorage.setItem(LocalStorageKeys.REDUCE_MOTION, 'false') const { container } = render() const progressWithAnimations = container.querySelector('.animate-progress') diff --git a/__tests__/components/MarketAssetTable/MarketDetails.test.tsx b/__tests__/components/MarketAssetTable/MarketDetails.test.tsx deleted file mode 100644 index 75b193c4..00000000 --- a/__tests__/components/MarketAssetTable/MarketDetails.test.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { render } from '@testing-library/react' - -import MarketDetails from 'components/MarketAssetTable/MarketDetails' -import { ASSETS } from 'constants/assets' -import { BN } from 'utils/helpers' - -const data: LendingMarketTableData = { - asset: ASSETS[0], - marketDepositAmount: BN('890546916'), - accountLentValue: BN('0.5498406009348686811'), - marketLiquidityAmount: BN('629396551'), - marketDepositCap: BN('2500000000000'), - marketLiquidityRate: 0.017, - marketLiquidationThreshold: 0.61, - marketMaxLtv: 0.59, - borrowEnabled: true, -} - -jest.mock('hooks/useDisplayCurrencyPrice', () => () => { - const { BN } = require('utils/helpers') - - return { - getConversionRate: () => BN(1), - convertAmount: () => BN(1), - symbol: 'MARS', - } -}) - -describe('', () => { - afterAll(() => { - jest.unmock('hooks/usePrices') - }) - - it('should render', () => { - const { container } = render() - expect(container).toBeInTheDocument() - }) -}) diff --git a/__tests__/components/Modals/Unlock/UnlockModal.test.tsx b/__tests__/components/Modals/Unlock/UnlockModal.test.tsx index 2297fa19..c19fbdf3 100644 --- a/__tests__/components/Modals/Unlock/UnlockModal.test.tsx +++ b/__tests__/components/Modals/Unlock/UnlockModal.test.tsx @@ -14,6 +14,7 @@ const mockedDepositedVault: DepositedVault = { ...TESTNET_VAULTS_META_DATA[0], status: 'active', apy: 1, + apr: null, ltv: { max: 0.65, liq: 0.7, diff --git a/__tests__/components/Modals/vault/VaultBorrowings.test.tsx b/__tests__/components/Modals/vault/VaultBorrowings.test.tsx deleted file mode 100644 index a1b7be74..00000000 --- a/__tests__/components/Modals/vault/VaultBorrowings.test.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { render } from '@testing-library/react' - -import DisplayCurrency from 'components/DisplayCurrency' -import VaultBorrowings, { VaultBorrowingsProps } from 'components/Modals/Vault/VaultBorrowings' -import { ASSETS } from 'constants/assets' -import { TESTNET_VAULTS_META_DATA } from 'constants/vaults' -import useStore from 'store' -import { BNCoin } from 'types/classes/BNCoin' -import { BN } from 'utils/helpers' - -jest.mock('hooks/usePrices', () => - jest.fn(() => ({ - data: [], - })), -) - -jest.mock('hooks/usePrice', () => jest.fn(() => '1')) - -jest.mock('hooks/useMarketAssets', () => - jest.fn(() => ({ - data: [], - })), -) - -jest.mock('hooks/broadcast/useDepositVault', () => jest.fn(() => ({ actions: [] }))) - -jest.mock('components/DisplayCurrency') - -jest.mock('hooks/useHealthComputer', () => - jest.fn(() => ({ - computeMaxBorrowAmount: () => {}, - })), -) - -const mockedDisplayCurrency = jest - .mocked(DisplayCurrency) - .mockImplementation(() =>
Display currency
) - -const mockedVault: Vault = { - ...TESTNET_VAULTS_META_DATA[0], - apy: 0, - ltv: { - liq: 0.2, - max: 0.1, - }, - cap: { - denom: 'test', - max: BN(10), - used: BN(2), - }, -} -describe('', () => { - const defaultProps: VaultBorrowingsProps = { - primaryAsset: ASSETS[0], - secondaryAsset: ASSETS[1], - vault: mockedVault, - borrowings: [], - deposits: [], - onChangeBorrowings: jest.fn(), - depositActions: [], - depositCapReachedCoins: [], - displayCurrency: 'uosmo', - } - - beforeAll(() => { - useStore.setState({ - baseCurrency: ASSETS[0], - }) - }) - - afterAll(() => { - useStore.clearState() - mockedDisplayCurrency.mockClear() - }) - - it('should render', () => { - const { container } = render() - expect(container).toBeInTheDocument() - }) - - it('should render DisplayCurrency correctly', () => { - expect(mockedDisplayCurrency).toHaveBeenCalledTimes(1) - expect(mockedDisplayCurrency).toHaveBeenCalledWith( - { coin: new BNCoin({ denom: 'usd', amount: '0' }) }, - expect.anything(), - ) - }) -}) diff --git a/__tests__/components/Tooltip/TooltipContent.test.tsx b/__tests__/components/Tooltip/TooltipContent.test.tsx index 0f84091d..a7e114c1 100644 --- a/__tests__/components/Tooltip/TooltipContent.test.tsx +++ b/__tests__/components/Tooltip/TooltipContent.test.tsx @@ -1,6 +1,5 @@ import { render } from '@testing-library/react' -import { TooltipType } from 'components/Tooltip' import TooltipContent from 'components/Tooltip/TooltipContent' describe('', () => { diff --git a/package.json b/package.json index 37d6a079..2997c227 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mars-v2-frontend", - "version": "2.0.0", + "version": "2.0.1", "private": true, "scripts": { "build": "yarn validate-env && next build", @@ -12,15 +12,22 @@ "prettier-check": "prettier --ignore-path .gitignore --check ./src/", "start": "next start", "validate-env": "node ./validate-env", - "install-charting-library": "dotenv -e .env.local node install_charting_library.js" + "install-charting-library": "dotenv -e .env.local node install_charting_library.js", + "prepare": "husky install" + }, + "lint-staged": { + "*.ts*": [ + "eslint ./src/ ./__tests__/ --fix", + "prettier --write ./src/ ./__tests__/" + ] }, "dependencies": { "@cosmjs/cosmwasm-stargate": "^0.31.1", - "@delphi-labs/shuttle-react": "^3.9.0", - "@keplr-wallet/cosmos": "^0.12.32", - "@sentry/nextjs": "^7.73.0", + "@delphi-labs/shuttle-react": "^3.9.1", + "@keplr-wallet/cosmos": "^0.12.35", + "@sentry/nextjs": "^7.74.0", "@splinetool/react-spline": "^2.2.6", - "@splinetool/runtime": "^0.9.477", + "@splinetool/runtime": "^0.9.482", "@tanstack/react-table": "^8.10.6", "@tippyjs/react": "^4.2.6", "bignumber.js": "^9.1.2", @@ -36,7 +43,7 @@ "react-draggable": "^4.4.6", "react-helmet-async": "^1.3.0", "react-qr-code": "^2.0.12", - "react-router-dom": "^6.16.0", + "react-router-dom": "^6.17.0", "react-spring": "^9.7.3", "react-toastify": "^9.1.3", "react-use-clipboard": "^1.0.9", @@ -52,7 +59,7 @@ "@types/debounce-promise": "^3.1.7", "@types/lodash.debounce": "^4.0.7", "@types/lodash.throttle": "^4.1.7", - "@types/node": "^20.7.0", + "@types/node": "^20.8.6", "@types/react": "18.2.28", "@types/react-dom": "18.2.13", "@types/react-helmet": "^6.1.7", @@ -63,11 +70,13 @@ "eslint": "^8.51.0", "eslint-config-next": "^13.5.4", "eslint-plugin-import": "^2.28.1", + "husky": "^8.0.3", "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", + "lint-staged": "^15.0.1", "prettier": "^3.0.3", - "prettier-plugin-tailwindcss": "^0.5.5", + "prettier-plugin-tailwindcss": "^0.5.6", "shelljs": "^0.8.5", "tailwindcss": "^3.3.3", "typescript": "5.2.2" diff --git a/public/images/bridges/squid.png b/public/images/bridges/squid.png new file mode 100644 index 0000000000000000000000000000000000000000..5c4cd5be443b63a4de4f0f7db205b399d0e5aa3c GIT binary patch literal 1846 zcmaJ?eKb^Q7@v+viOO1)RJTiQ#N7EbX3&H&NFz-X!ij6M1Qx1nCLIjq?e&2*R zl1MsMQjtIIFWA5lqjEB=jUlV$N&-zHIdauXSR9GqKqwL}RXBlzm#>3>RN@5sF$ENX z(iMr2dc>)a&2io$aa^RBEdjY}0Y^25Adn+C45;NY1;$Z3fuDIfgspW$An-W^k8}e6 zDav2q1Gu6p1Ynb?wqgo}3a|)^VMnuPvso;b4M3yN=n%yYqA_i0bPkinp)!EU2P878 zBw-vOk3X4@*g1g_IIiSCP)tk=IfhO~RpAho&1ORs8bqVn5)rmotOAGCwhGK*ih+k< zVwF^hOHl=&WrRb~DBKAos{N-8x$+CG0-HQ4;?N*9tc0j!ingt(K!M=@L*?=>(HJg7 zzSjGn#F!{ni9kXGL!(q;Vt8Q|+Ehx8s|tZ}R3$=D+0;q;M4&i|MW9N6#w1gK)dE;7 zRcJj{pCJMP$5VmfutJP@@|-||ge;XxICNJRD#O*DZf9rDq*C47?CDgtiyOF!b-kk8Pj65ijXUb%sfOTjYcGV6)Fclcg>MbUki0wzDcZP`dVny zVj-d!NITnq&3ej32uN#wQ7^IiqJKm|h+ajgySFo|fVeaTo;+uf`f>7>_zj!=7EN@{ zs!jnbD^pCefqAL<1{GFbjH6Fz>HLPIH0Kuvd1)8dbr^pm*qerJ?eL*Rg=LQQl-Ii} zkFCrxOOPn`Y|tw#-W@99$<{!K&k0Ri1?D~YI&L-`Jlq^Jc&Tf|;YIwtEvu7TrJeeE#^x)mwE__9amgY8-ZYWUhNz zw8)fve0}cR%h`f@3D3BmWwxO1z#t~J%$S>UD%|?Q2xJr<8GXzBp8AG;i>t|&p&awk zdyRiytFh5ITJzuT3_Vzu-OygT&HT=-5>eNL{)2*nn>AxAIxf?b%KLm}j}GfO=Qp$k zSo}UPyD`U4+Ouc-j8_ds`DNjI2X6pxZvZ6WBvQvOYa78(@h_a`j%(x=XbvueG<7CW?OJBw(bEN1tzDeLK9ES zN<{TXb<0u+fkf&_Nl~uq=Mp7kc`$^(x2p8B&fN&>{<EMn#@`-E9x<>=HO6!_>ubwY1*3Y`phNEhH%R}tNlce zFO1aUzWu)J?^@qgSQ-rU&JILpia_z%lk!|R+m^=PIrC~K928;w+wJ3_hhupG)_qo+ z-Aht~kNx_xnIV7BIh;ArUT#tv8C{6Iem!Ss8zN^oV;`-|Zs_F2>Xa=stLk@o@1Dal zzT1`i)AGMg3b=n9K2v@?ot}84zfM|`u|4=~)tNddJn!vO@o-=-*Iv_=YMG_qe&yMa z#C?lVr>6L!M^~UMe=z_s*k!sk(PNK(cHY1Ys7kHVG>1JQ?pS^Nt2Ls7wQh4p+~wsak{s?IsK9L OPv+_7%`0^YN%|X_3Fwai literal 0 HcmV?d00001 diff --git a/public/images/tokens/nusdc.svg b/public/images/tokens/nusdc.svg deleted file mode 100644 index ce1fe8fd..00000000 --- a/public/images/tokens/nusdc.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - diff --git a/public/images/tokens/usdc.svg b/public/images/tokens/usdc.svg new file mode 100644 index 00000000..60234a85 --- /dev/null +++ b/public/images/tokens/usdc.svg @@ -0,0 +1,28 @@ + + + + + diff --git a/src/api/hls/getHLSStakingAssets.ts b/src/api/hls/getHLSStakingAssets.ts new file mode 100644 index 00000000..ea2a73b5 --- /dev/null +++ b/src/api/hls/getHLSStakingAssets.ts @@ -0,0 +1,28 @@ +import { getParamsQueryClient } from 'api/cosmwasm-client' +import getAssetParams from 'api/params/getAssetParams' +import { BN } from 'utils/helpers' +import { resolveHLSStrategies } from 'utils/resolvers' + +export default async function getHLSStakingAssets() { + const assetParams = await getAssetParams() + const client = await getParamsQueryClient() + const HLSAssets = assetParams.filter((asset) => asset.credit_manager.hls) + const strategies = resolveHLSStrategies('coin', HLSAssets) + + const depositCaps$ = strategies.map((strategy) => + client.totalDeposit({ denom: strategy.denoms.deposit }), + ) + + return Promise.all(depositCaps$).then((depositCaps) => { + return depositCaps.map((depositCap, index) => { + return { + ...strategies[index], + depositCap: { + denom: depositCap.denom, + used: BN(depositCap.amount), + max: BN(depositCap.cap), + }, + } as HLSStrategy + }) + }) +} diff --git a/src/api/vaults/getVaults.ts b/src/api/vaults/getVaults.ts index 0e5e762c..2a145777 100644 --- a/src/api/vaults/getVaults.ts +++ b/src/api/vaults/getVaults.ts @@ -1,3 +1,4 @@ +import getAssetParams from 'api/params/getAssetParams' import getAprs from 'api/vaults/getVaultAprs' import { getVaultConfigs } from 'api/vaults/getVaultConfigs' import { getVaultUtilizations } from 'api/vaults/getVaultUtilizations' @@ -6,13 +7,17 @@ import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults' import { NETWORK } from 'types/enums/network' import { BN } from 'utils/helpers' import { convertAprToApy } from 'utils/parsers' +import { resolveHLSStrategies } from 'utils/resolvers' export default async function getVaults(): Promise { + const assetParams = await getAssetParams() const vaultConfigs = await getVaultConfigs() const $vaultUtilizations = getVaultUtilizations(vaultConfigs) const $aprs = getAprs() const vaultMetaDatas = ENV.NETWORK === NETWORK.TESTNET ? TESTNET_VAULTS_META_DATA : VAULTS_META_DATA + const HLSAssets = assetParams.filter((asset) => asset.credit_manager.hls) + const hlsStrategies = resolveHLSStrategies('vault', HLSAssets) const vaults: Vault[] = [] await Promise.all([$vaultUtilizations, $aprs]).then(([vaultUtilizations, aprs]) => { @@ -43,6 +48,17 @@ export default async function getVaults(): Promise { }, } + const hlsStrategy = hlsStrategies.find( + (strategy) => strategy.denoms.deposit === vaultConfig.addr, + ) + if (hlsStrategy) { + vault.hls = { + maxLTV: hlsStrategy.maxLTV, + maxLeverage: hlsStrategy.maxLeverage, + borrowDenom: hlsStrategy.denoms.borrow, + } + } + vaults.push(vault) }) }) diff --git a/src/components/AccordionContent.tsx b/src/components/AccordionContent.tsx index 80a9f262..9b7a7cea 100644 --- a/src/components/AccordionContent.tsx +++ b/src/components/AccordionContent.tsx @@ -20,14 +20,13 @@ export default function AccordionContent(props: Props) { const { title, renderContent, isOpen, renderSubTitle, toggleOpen } = props.item return ( -
+
toggleOpen(props.index)} className={classNames( - 'mb-0 flex hover:cursor-pointer items-center justify-between border-t border-white/10 bg-white/10 p-4 text-white', - 'group-[&:first-child]:border-t-0 group-[[open]]:border-b', + 'mb-0 flex hover:cursor-pointer items-center justify-between bg-white/10 p-4 text-white border-b border-transparent', '[&::marker]:hidden [&::marker]:content-[""]', - isOpen && 'border-b [&:first-child]:border-t-0', + isOpen && 'border-white/20', )} >
diff --git a/src/components/Account/AccountBalancesTable/index.tsx b/src/components/Account/AccountBalancesTable/index.tsx index 91e4a914..334e4d18 100644 --- a/src/components/Account/AccountBalancesTable/index.tsx +++ b/src/components/Account/AccountBalancesTable/index.tsx @@ -45,7 +45,6 @@ export default function Index(props: Props) { const navigate = useNavigate() const { pathname } = useLocation() const address = useStore((s) => s.address) - const baseCurrency = useStore((s) => s.baseCurrency) const [sorting, setSorting] = useState([]) const updatedAccount = useStore((s) => s.updatedAccount) const accountBalanceData = useAccountBalanceData({ @@ -65,6 +64,7 @@ export default function Index(props: Props) { return ( {row.original.symbol} + {row.original.type === 'borrowing' && (debt)} {row.original.type === 'lending' && (lent)} {row.original.type === 'vault' && (farm)} @@ -186,7 +186,7 @@ export default function Index(props: Props) { return ( - + {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { diff --git a/src/components/Account/AccountComposition.tsx b/src/components/Account/AccountComposition.tsx index e24b6522..f6c0a3c5 100644 --- a/src/components/Account/AccountComposition.tsx +++ b/src/components/Account/AccountComposition.tsx @@ -37,14 +37,10 @@ export default function AccountComposition(props: Props) { const { account } = props const hasChanged = !!updatedAccount const { data: prices } = usePrices() - const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } = - useBorrowMarketAssetsTableData() + const { data } = useBorrowMarketAssetsTableData(false) + const borrowAssetsData = useMemo(() => data?.allAssets || [], [data]) const { availableAssets: lendingAvailableAssets, accountLentAssets } = useLendingMarketAssetsTableData() - const borrowAssetsData = useMemo( - () => [...borrowAvailableAssets, ...accountBorrowedAssets], - [borrowAvailableAssets, accountBorrowedAssets], - ) const lendingAssetsData = useMemo( () => [...lendingAvailableAssets, ...accountLentAssets], [lendingAvailableAssets, accountLentAssets], diff --git a/src/components/Account/AccountDetails/index.tsx b/src/components/Account/AccountDetails/index.tsx index 9d6ffe57..550e5d78 100644 --- a/src/components/Account/AccountDetails/index.tsx +++ b/src/components/Account/AccountDetails/index.tsx @@ -14,7 +14,7 @@ import { HealthGauge } from 'components/HealthGauge' import { ThreeDots } from 'components/Icons' import Text from 'components/Text' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { REDUCE_MOTION_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import { ORACLE_DENOM } from 'constants/oracle' import useAccountId from 'hooks/useAccountId' import useAccountIds from 'hooks/useAccountIds' @@ -56,7 +56,10 @@ interface Props { function AccountDetails(props: Props) { const { account } = props - const [reduceMotion] = useLocalStorage(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion) + const [reduceMotion] = useLocalStorage( + LocalStorageKeys.REDUCE_MOTION, + DEFAULT_SETTINGS.reduceMotion, + ) const updatedAccount = useStore((s) => s.updatedAccount) const accountDetailsExpanded = useStore((s) => s.accountDetailsExpanded) const { health } = useHealthComputer(account) @@ -76,14 +79,12 @@ function AccountDetails(props: Props) { return updatedLeverage }, [updatedAccount, prices, leverage]) - const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } = - useBorrowMarketAssetsTableData() + const { data } = useBorrowMarketAssetsTableData(false) + const borrowAssetsData = useMemo(() => data?.allAssets || [], [data]) + const { availableAssets: lendingAvailableAssets, accountLentAssets } = useLendingMarketAssetsTableData() - const borrowAssetsData = useMemo( - () => [...borrowAvailableAssets, ...accountBorrowedAssets], - [borrowAvailableAssets, accountBorrowedAssets], - ) + const lendingAssetsData = useMemo( () => [...lendingAvailableAssets, ...accountLentAssets], [lendingAvailableAssets, accountLentAssets], diff --git a/src/components/Account/AccountFund/AccountFundContent.tsx b/src/components/Account/AccountFund/AccountFundContent.tsx index 3e5bbc37..85ebeeff 100644 --- a/src/components/Account/AccountFund/AccountFundContent.tsx +++ b/src/components/Account/AccountFund/AccountFundContent.tsx @@ -9,7 +9,7 @@ import Text from 'components/Text' import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider' import WalletBridges from 'components/Wallet/WalletBridges' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { LEND_ASSETS_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import { BN_ZERO } from 'constants/math' import useAutoLend from 'hooks/useAutoLend' import useLocalStorage from 'hooks/useLocalStorage' @@ -38,7 +38,7 @@ export default function AccountFundContent(props: Props) { const walletAssetModal = useStore((s) => s.walletAssetsModal) const [isConfirming, setIsConfirming] = useState(false) const [lendAssets, setLendAssets] = useLocalStorage( - LEND_ASSETS_KEY, + LocalStorageKeys.LEND_ASSETS, DEFAULT_SETTINGS.lendAssets, ) const [fundingAssets, setFundingAssets] = useState([]) diff --git a/src/components/Account/AccountList/AccountStats.tsx b/src/components/Account/AccountList/AccountStats.tsx index 92a656e2..2ff13667 100644 --- a/src/components/Account/AccountList/AccountStats.tsx +++ b/src/components/Account/AccountList/AccountStats.tsx @@ -28,14 +28,10 @@ export default function AccountStats(props: Props) { [account, prices], ) const { health } = useHealthComputer(account) - const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } = - useBorrowMarketAssetsTableData() + const { data } = useBorrowMarketAssetsTableData(false) + const borrowAssetsData = useMemo(() => data?.allAssets || [], [data]) const { availableAssets: lendingAvailableAssets, accountLentAssets } = useLendingMarketAssetsTableData() - const borrowAssetsData = useMemo( - () => [...borrowAvailableAssets, ...accountBorrowedAssets], - [borrowAvailableAssets, accountBorrowedAssets], - ) const lendingAssetsData = useMemo( () => [...lendingAvailableAssets, ...accountLentAssets], [lendingAvailableAssets, accountLentAssets], diff --git a/src/components/Account/AccountMenuContent.tsx b/src/components/Account/AccountMenuContent.tsx index e1a939c9..f1f11038 100644 --- a/src/components/Account/AccountMenuContent.tsx +++ b/src/components/Account/AccountMenuContent.tsx @@ -11,7 +11,7 @@ import Overlay from 'components/Overlay' import Text from 'components/Text' import WalletBridges from 'components/Wallet/WalletBridges' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { LEND_ASSETS_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useAccountId from 'hooks/useAccountId' import useAccountIds from 'hooks/useAccountIds' import useAutoLend from 'hooks/useAutoLend' @@ -39,7 +39,10 @@ export default function AccountMenuContent() { const [showMenu, setShowMenu] = useToggle() const [isCreating, setIsCreating] = useToggle() const transactionFeeCoinBalance = useCurrentWalletBalance(baseCurrency.denom) - const [lendAssets] = useLocalStorage(LEND_ASSETS_KEY, DEFAULT_SETTINGS.lendAssets) + const [lendAssets] = useLocalStorage( + LocalStorageKeys.LEND_ASSETS, + DEFAULT_SETTINGS.lendAssets, + ) const { enableAutoLendAccountId } = useAutoLend() const hasCreditAccounts = !!accountIds?.length diff --git a/src/components/Account/AccountSummary.tsx b/src/components/Account/AccountSummary.tsx index de914b20..048ba2b1 100644 --- a/src/components/Account/AccountSummary.tsx +++ b/src/components/Account/AccountSummary.tsx @@ -1,5 +1,5 @@ import classNames from 'classnames' -import { HTMLAttributes, useMemo } from 'react' +import { HTMLAttributes, useCallback, useMemo } from 'react' import Accordion from 'components/Accordion' import AccountBalancesTable from 'components/Account/AccountBalancesTable' @@ -10,12 +10,14 @@ import DisplayCurrency from 'components/DisplayCurrency' import { FormattedNumber } from 'components/FormattedNumber' import { ArrowRight } from 'components/Icons' import Text from 'components/Text' +import { DEFAULT_SETTINGS } from 'constants/defaultSettings' +import { LocalStorageKeys } from 'constants/localStorageKeys' import { BN_ZERO } from 'constants/math' import { ORACLE_DENOM } from 'constants/oracle' import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData' import useHealthComputer from 'hooks/useHealthComputer' -import useIsOpenArray from 'hooks/useIsOpenArray' import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData' +import useLocalStorage from 'hooks/useLocalStorage' import usePrices from 'hooks/usePrices' import useStore from 'store' import { BNCoin } from 'types/classes/BNCoin' @@ -26,7 +28,10 @@ interface Props { } export default function AccountSummary(props: Props) { - const [isOpen, toggleOpen] = useIsOpenArray(2, true) + const [accountSummaryTabs, setAccountSummaryTabs] = useLocalStorage( + LocalStorageKeys.ACCOUNT_SUMMARY_TABS, + DEFAULT_SETTINGS.accountSummaryTabs, + ) const { data: prices } = usePrices() const updatedAccount = useStore((s) => s.updatedAccount) const accountBalance = useMemo( @@ -36,15 +41,11 @@ export default function AccountSummary(props: Props) { : BN_ZERO, [props.account, updatedAccount, prices], ) - const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } = - useBorrowMarketAssetsTableData() + const { data } = useBorrowMarketAssetsTableData(false) + const borrowAssetsData = useMemo(() => data?.allAssets || [], [data]) const { availableAssets: lendingAvailableAssets, accountLentAssets } = useLendingMarketAssetsTableData() - const borrowAssetsData = useMemo( - () => [...borrowAvailableAssets, ...accountBorrowedAssets], - [borrowAvailableAssets, accountBorrowedAssets], - ) const lendingAssetsData = useMemo( () => [...lendingAvailableAssets, ...accountLentAssets], [lendingAvailableAssets, accountLentAssets], @@ -63,6 +64,13 @@ export default function AccountSummary(props: Props) { return updatedLeverage }, [updatedAccount, prices, leverage]) + const handleToggle = useCallback( + (index: number) => { + setAccountSummaryTabs(accountSummaryTabs.map((tab, i) => (i === index ? !tab : tab))) + }, + [accountSummaryTabs, setAccountSummaryTabs], + ) + if (!props.account) return null return (
@@ -110,8 +118,8 @@ export default function AccountSummary(props: Props) { title: `Credit Account ${props.account.id} Composition`, renderContent: () => props.account ? : null, - isOpen: isOpen[0], - toggleOpen: (index: number) => toggleOpen(index), + isOpen: accountSummaryTabs[0], + toggleOpen: (index: number) => handleToggle(index), renderSubTitle: () => <>, }, { @@ -124,8 +132,8 @@ export default function AccountSummary(props: Props) { lendingData={lendingAssetsData} /> ) : null, - isOpen: isOpen[1], - toggleOpen: (index: number) => toggleOpen(index), + isOpen: accountSummaryTabs[1], + toggleOpen: (index: number) => handleToggle(index), renderSubTitle: () => <>, }, ]} diff --git a/src/components/Account/HealthBar.tsx b/src/components/Account/HealthBar.tsx index b764f54a..8a4ce6f7 100644 --- a/src/components/Account/HealthBar.tsx +++ b/src/components/Account/HealthBar.tsx @@ -3,7 +3,7 @@ import { useMemo } from 'react' import { Tooltip } from 'components/Tooltip' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { REDUCE_MOTION_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useHealthColorAndLabel from 'hooks/useHealthColorAndLabel' import useLocalStorage from 'hooks/useLocalStorage' import { getHealthIndicatorColors } from 'utils/healthIndicator' @@ -30,7 +30,10 @@ function calculateHealth(health: number): number { export default function HealthBar(props: Props) { const { health, updatedHealth } = props - const [reduceMotion] = useLocalStorage(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion) + const [reduceMotion] = useLocalStorage( + LocalStorageKeys.REDUCE_MOTION, + DEFAULT_SETTINGS.reduceMotion, + ) const width = calculateHealth(health) const updatedWidth = calculateHealth(updatedHealth ?? 0) const isUpdated = updatedWidth > 0 && updatedWidth !== width @@ -55,12 +58,53 @@ export default function HealthBar(props: Props) { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {isUpdated && ( { - const [reduceMotion] = useLocalStorage(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion) + const [reduceMotion] = useLocalStorage( + LocalStorageKeys.REDUCE_MOTION, + DEFAULT_SETTINGS.reduceMotion, + ) const currentRisk = BN_ZERO return ( diff --git a/src/components/AmountAndValue.tsx b/src/components/AmountAndValue.tsx index 67fb6368..3d42cc3b 100644 --- a/src/components/AmountAndValue.tsx +++ b/src/components/AmountAndValue.tsx @@ -11,11 +11,14 @@ interface Props { export default function AmountAndValue(props: Props) { const amount = demagnify(props.amount.toString(), props.asset) + const isZero = amount === 0 + const isBelowMinAmount = amount < MIN_AMOUNT + const displayAmount = isBelowMinAmount ? MIN_AMOUNT : amount return (
diff --git a/src/components/Background.tsx b/src/components/Background.tsx index d02a8426..79fb7cb6 100644 --- a/src/components/Background.tsx +++ b/src/components/Background.tsx @@ -1,11 +1,14 @@ import classNames from 'classnames' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { REDUCE_MOTION_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useLocalStorage from 'hooks/useLocalStorage' export default function Background() { - const [reduceMotion] = useLocalStorage(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion) + const [reduceMotion] = useLocalStorage( + LocalStorageKeys.REDUCE_MOTION, + DEFAULT_SETTINGS.reduceMotion, + ) return (
diff --git a/src/components/Borrow/BorrowTable.tsx b/src/components/Borrow/BorrowTable.tsx deleted file mode 100644 index 440693e3..00000000 --- a/src/components/Borrow/BorrowTable.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { ColumnDef, Row, Table } from '@tanstack/react-table' -import { useCallback, useMemo } from 'react' - -import AmountAndValue from 'components/AmountAndValue' -import AssetImage from 'components/Asset/AssetImage' -import BorrowActionButtons from 'components/Borrow/BorrowActionButtons' -import { FormattedNumber } from 'components/FormattedNumber' -import { ChevronDown, ChevronUp } from 'components/Icons' -import Loading from 'components/Loading' -import AssetListTable from 'components/MarketAssetTable' -import MarketAssetTableRow from 'components/MarketAssetTable/MarketAssetTableRow' -import MarketDetails from 'components/MarketAssetTable/MarketDetails' -import TitleAndSubCell from 'components/TitleAndSubCell' -import { BN_ZERO } from 'constants/math' -import { getEnabledMarketAssets } from 'utils/assets' - -interface Props { - title: string - data: BorrowMarketTableData[] -} - -export default function BorrowTable(props: Props) { - const { title, data } = props - const shouldShowAccountBorrowed = !!data[0]?.debt - const marketAssets = getEnabledMarketAssets() - - const rowRenderer = useCallback( - (row: Row, table: Table) => { - return ( - } - expandedDetails={} - /> - ) - }, - [], - ) - - const columns = useMemo[]>( - () => [ - { - accessorKey: 'asset.name', - header: 'Asset', - id: 'symbol', - cell: ({ row }) => { - const asset = row.original.asset - - return ( -
- - -
- ) - }, - }, - ...(shouldShowAccountBorrowed - ? [ - { - accessorKey: 'debt', - header: 'Borrowed', - cell: (info: any) => { - const borrowAsset = info.row.original as BorrowMarketTableData - const asset = marketAssets.find((asset) => asset.denom === borrowAsset.asset.denom) - - if (!asset) return null - - return - }, - }, - ] - : []), - { - accessorKey: 'borrowRate', - header: 'Borrow Rate', - cell: ({ row }) => { - if (row.original.borrowRate === null) { - return - } - - return ( - - ) - }, - }, - { - accessorKey: 'liquidity', - header: 'Liquidity Available', - cell: ({ row }) => { - const { liquidity, asset: borrowAsset } = row.original - const asset = marketAssets.find((asset) => asset.denom === borrowAsset.denom) - - if (!asset) return null - - if (liquidity === null) { - return - } - - return - }, - }, - { - accessorKey: 'manage', - enableSorting: false, - header: 'Manage', - cell: ({ row }) => ( -
-
{row.getIsExpanded() ? : }
-
- ), - }, - ], - [shouldShowAccountBorrowed, marketAssets], - ) - - return -} diff --git a/src/components/Borrow/Borrowings.tsx b/src/components/Borrow/Borrowings.tsx new file mode 100644 index 00000000..ca72ff0d --- /dev/null +++ b/src/components/Borrow/Borrowings.tsx @@ -0,0 +1,37 @@ +import React from 'react' + +import AvailableBorrowingsTable from 'components/Borrow/Table/AvailableBorrowingsTable' +import DepositedBorrowingsTable from 'components/Borrow/Table/DepositedBorrowingsTable' +import { BN_ZERO } from 'constants/math' +import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData' +import { getBorrowEnabledAssets } from 'utils/assets' + +export default function Borrowings() { + const { data } = useBorrowMarketAssetsTableData() + + if (!data?.allAssets?.length) { + return + } + return ( + <> + + + + ) +} + +function Fallback() { + const assets = getBorrowEnabledAssets() + const data: BorrowMarketTableData[] = assets.map((asset) => ({ + asset, + borrowRate: null, + liquidity: null, + marketMaxLtv: 0, + marketDepositAmount: BN_ZERO, + marketLiquidityRate: 0, + marketLiquidityAmount: BN_ZERO, + marketLiquidationThreshold: 0, + })) + + return +} diff --git a/src/components/Borrow/Table/AvailableBorrowingsTable.tsx b/src/components/Borrow/Table/AvailableBorrowingsTable.tsx new file mode 100644 index 00000000..05d91c60 --- /dev/null +++ b/src/components/Borrow/Table/AvailableBorrowingsTable.tsx @@ -0,0 +1,46 @@ +import { Row } from '@tanstack/react-table' +import { Table as TanstackTable } from '@tanstack/table-core/build/lib/types' +import { useCallback } from 'react' + +import BorrowActionButtons from 'components/Borrow/BorrowActionButtons' +import { NAME_META } from 'components/Borrow/Table/Columns/Name' +import useAvailableColumns from 'components/Borrow/Table/Columns/useAvailableColumns' +import MarketDetails from 'components/MarketDetails' +import Table from 'components/Table' +import ActionButtonRow from 'components/Table/ActionButtonRow' + +type Props = { + data: BorrowMarketTableData[] + isLoading: boolean +} + +export default function AvailableBorrowingsTable(props: Props) { + const columns = useAvailableColumns() + + const renderExpanded = useCallback( + (row: Row, _: TanstackTable) => { + const currentRow = row as Row + return ( + <> + + + + + + ) + }, + [], + ) + + if (!props.data.length) return null + + return ( +
+ ) +} diff --git a/src/components/Borrow/Table/Columns/BorrowRate.tsx b/src/components/Borrow/Table/Columns/BorrowRate.tsx new file mode 100644 index 00000000..d221d65d --- /dev/null +++ b/src/components/Borrow/Table/Columns/BorrowRate.tsx @@ -0,0 +1,25 @@ +import React from 'react' + +import { FormattedNumber } from 'components/FormattedNumber' +import Loading from 'components/Loading' + +export const BORROW_RATE_META = { accessorKey: 'borrowRate', header: 'Borrow Rate' } + +interface Props { + borrowRate: number | null +} + +export default function BorrowRate(props: Props) { + if (props.borrowRate === null) { + return + } + + return ( + + ) +} diff --git a/src/components/Borrow/Table/Columns/Debt.tsx b/src/components/Borrow/Table/Columns/Debt.tsx new file mode 100644 index 00000000..35ccf42b --- /dev/null +++ b/src/components/Borrow/Table/Columns/Debt.tsx @@ -0,0 +1,42 @@ +import { Row } from '@tanstack/react-table' + +import AmountAndValue from 'components/AmountAndValue' +import { BN_ZERO } from 'constants/math' +import { byDenom } from 'utils/array' +import { getEnabledMarketAssets } from 'utils/assets' + +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.debt || !b.original.debt) return 0 + const assetAPrice = (a.original.liquidity?.value ?? BN_ZERO).div( + a.original.liquidity?.amount ?? BN_ZERO, + ) + const assetBPrice = (b.original.liquidity?.value ?? BN_ZERO).div( + b.original.liquidity?.amount ?? BN_ZERO, + ) + const debtA = a.original.debt.times(assetAPrice).shiftedBy(-assetA.decimals) + const debtB = b.original.debt.times(assetBPrice).shiftedBy(-assetB.decimals) + return debtA.minus(debtB).toNumber() +} + +interface Props { + data: BorrowMarketTableData +} + +export default function Debt(props: Props) { + const marketAssets = getEnabledMarketAssets() + const asset = marketAssets.find(byDenom(props.data.asset.denom)) + + if (!asset) return null + + return +} diff --git a/src/components/Borrow/Table/Columns/Liquidity.tsx b/src/components/Borrow/Table/Columns/Liquidity.tsx new file mode 100644 index 00000000..64854b87 --- /dev/null +++ b/src/components/Borrow/Table/Columns/Liquidity.tsx @@ -0,0 +1,44 @@ +import { Row } from '@tanstack/react-table' + +import AmountAndValue from 'components/AmountAndValue' +import Loading from 'components/Loading' +import { BN_ZERO } from 'constants/math' +import { byDenom } from 'utils/array' +import { getEnabledMarketAssets } from 'utils/assets' +import { demagnify } from 'utils/formatters' + +export const LIQUIDITY_META = { + accessorKey: 'liquidity', + header: 'Liquidity Available', + id: 'liquidity', +} + +export const liquiditySortingFn = ( + a: Row, + b: Row, +): number => { + const assetA = a.original.asset + const assetB = b.original.asset + const liquidityA = demagnify(a.original.liquidity?.amount ?? 0, assetA) + const liquidityB = demagnify(b.original.liquidity?.amount ?? 0, assetB) + + return liquidityA - liquidityB +} + +interface Props { + data: BorrowMarketTableData +} + +export default function Liquidity(props: Props) { + const { liquidity, asset: borrowAsset } = props.data + const marketAssets = getEnabledMarketAssets() + const asset = marketAssets.find(byDenom(borrowAsset.denom)) + + if (!asset) return null + + if (liquidity === null) { + return + } + + return +} diff --git a/src/components/Borrow/Table/Columns/Manage.tsx b/src/components/Borrow/Table/Columns/Manage.tsx new file mode 100644 index 00000000..58c95820 --- /dev/null +++ b/src/components/Borrow/Table/Columns/Manage.tsx @@ -0,0 +1,17 @@ +import React from 'react' + +import { ChevronDown, ChevronUp } from 'components/Icons' + +export const MANAGE_META = { accessorKey: 'manage', enableSorting: false, header: 'Manage' } + +interface Props { + isExpanded: boolean +} + +export default function Manage(props: Props) { + return ( +
+
{props.isExpanded ? : }
+
+ ) +} diff --git a/src/components/Borrow/Table/Columns/Name.tsx b/src/components/Borrow/Table/Columns/Name.tsx new file mode 100644 index 00000000..352a61bb --- /dev/null +++ b/src/components/Borrow/Table/Columns/Name.tsx @@ -0,0 +1,18 @@ +import AssetImage from 'components/Asset/AssetImage' +import TitleAndSubCell from 'components/TitleAndSubCell' + +export const NAME_META = { accessorKey: 'asset.symbol', header: 'Asset', id: 'symbol' } + +interface Props { + data: BorrowMarketTableData +} + +export default function Name(props: Props) { + const { asset } = props.data + return ( +
+ + +
+ ) +} diff --git a/src/components/Borrow/Table/Columns/useAvailableColumns.tsx b/src/components/Borrow/Table/Columns/useAvailableColumns.tsx new file mode 100644 index 00000000..70413d5b --- /dev/null +++ b/src/components/Borrow/Table/Columns/useAvailableColumns.tsx @@ -0,0 +1,34 @@ +import { ColumnDef } from '@tanstack/react-table' +import { useMemo } from 'react' + +import BorrowRate, { BORROW_RATE_META } from 'components/Borrow/Table/Columns/BorrowRate' +import Liquidity, { + LIQUIDITY_META, + liquiditySortingFn, +} 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' + +export default function useAvailableColumns() { + return useMemo[]>(() => { + return [ + { + ...NAME_META, + cell: ({ row }) => , + }, + { + ...BORROW_RATE_META, + cell: ({ row }) => , + }, + { + ...LIQUIDITY_META, + cell: ({ row }) => , + sortingFn: liquiditySortingFn, + }, + { + ...MANAGE_META, + cell: ({ row }) => , + }, + ] + }, []) +} diff --git a/src/components/Borrow/Table/Columns/useDepositedColumns.tsx b/src/components/Borrow/Table/Columns/useDepositedColumns.tsx new file mode 100644 index 00000000..bd3d66b6 --- /dev/null +++ b/src/components/Borrow/Table/Columns/useDepositedColumns.tsx @@ -0,0 +1,40 @@ +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 Liquidity, { + LIQUIDITY_META, + liquiditySortingFn, +} 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' + +export default function useDepositedColumns() { + return useMemo[]>(() => { + return [ + { + ...NAME_META, + cell: ({ row }) => , + }, + { + ...DEBT_META, + cell: ({ row }) => , + sortingFn: debtSortingFn, + }, + { + ...BORROW_RATE_META, + cell: ({ row }) => , + }, + { + ...LIQUIDITY_META, + cell: ({ row }) => , + sortingFn: liquiditySortingFn, + }, + { + ...MANAGE_META, + cell: ({ row }) => , + }, + ] + }, []) +} diff --git a/src/components/Borrow/Table/DepositedBorrowingsTable.tsx b/src/components/Borrow/Table/DepositedBorrowingsTable.tsx new file mode 100644 index 00000000..f37b4e31 --- /dev/null +++ b/src/components/Borrow/Table/DepositedBorrowingsTable.tsx @@ -0,0 +1,42 @@ +import { Row } from '@tanstack/react-table' +import { useCallback } from 'react' + +import BorrowActionButtons from 'components/Borrow/BorrowActionButtons' +import { NAME_META } from 'components/Borrow/Table/Columns/Name' +import useDepositedColumns from 'components/Borrow/Table/Columns/useDepositedColumns' +import MarketDetails from 'components/MarketDetails' +import Table from 'components/Table' +import ActionButtonRow from 'components/Table/ActionButtonRow' + +type Props = { + data: BorrowMarketTableData[] + isLoading: boolean +} + +export default function DepositedBorrowingsTable(props: Props) { + const columns = useDepositedColumns() + + const renderExpanded = useCallback((row: Row) => { + const currentRow = row as Row + return ( + <> + + + + + + ) + }, []) + + if (!props.data.length) return null + + return ( +
+ ) +} diff --git a/src/components/BorrowCapacity.tsx b/src/components/BorrowCapacity.tsx index 76eff4f9..93f9c306 100644 --- a/src/components/BorrowCapacity.tsx +++ b/src/components/BorrowCapacity.tsx @@ -5,7 +5,7 @@ import { FormattedNumber } from 'components/FormattedNumber' import Text from 'components/Text' import { Tooltip } from 'components/Tooltip' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { REDUCE_MOTION_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useLocalStorage from 'hooks/useLocalStorage' interface Props { @@ -31,7 +31,10 @@ export const BorrowCapacity = ({ hideValues, decimals = 2, }: Props) => { - const [reduceMotion] = useLocalStorage(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion) + const [reduceMotion] = useLocalStorage( + LocalStorageKeys.REDUCE_MOTION, + DEFAULT_SETTINGS.reduceMotion, + ) const [percentOfMaxRound, setPercentOfMaxRound] = useState(0) const [percentOfMaxRange, setPercentOfMaxRange] = useState(0) const [limitPercentOfMax, setLimitPercentOfMax] = useState(0) diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 27eebed4..63a7eab1 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -17,7 +17,7 @@ import { glowElement } from 'components/Button/utils' import { CircularProgress } from 'components/CircularProgress' import { ChevronDown } from 'components/Icons' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { REDUCE_MOTION_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useLocalStorage from 'hooks/useLocalStorage' const Button = React.forwardRef(function Button( @@ -44,7 +44,10 @@ const Button = React.forwardRef(function Button( }: ButtonProps, ref, ) { - const [reduceMotion] = useLocalStorage(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion) + const [reduceMotion] = useLocalStorage( + LocalStorageKeys.REDUCE_MOTION, + DEFAULT_SETTINGS.reduceMotion, + ) const isDisabled = disabled || showProgressIndicator const shouldShowText = text && !children const shouldShowGlowElement = variant === 'solid' && !isDisabled && !reduceMotion diff --git a/src/components/CheckMark.tsx b/src/components/CheckMark.tsx index 0c20ba5d..7d6ee4ce 100644 --- a/src/components/CheckMark.tsx +++ b/src/components/CheckMark.tsx @@ -2,7 +2,7 @@ import classNames from 'classnames' import { CheckCircled } from 'components/Icons' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { REDUCE_MOTION_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useLocalStorage from 'hooks/useLocalStorage' interface Props { @@ -12,7 +12,10 @@ interface Props { } export const CheckMark = ({ color = '#FFFFFF', size = 20, className }: Props) => { - const [reduceMotion] = useLocalStorage(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion) + const [reduceMotion] = useLocalStorage( + LocalStorageKeys.REDUCE_MOTION, + DEFAULT_SETTINGS.reduceMotion, + ) const classes = classNames('inline-block relative', className) if (reduceMotion) diff --git a/src/components/Checkbox.tsx b/src/components/Checkbox.tsx index fee21e9b..5b27acc8 100644 --- a/src/components/Checkbox.tsx +++ b/src/components/Checkbox.tsx @@ -1,27 +1,39 @@ import classNames from 'classnames' import { Check } from 'components/Icons' +import Text from 'components/Text' interface Props { checked: boolean onChange: (checked: boolean) => void + text?: string } export default function Checkbox(props: Props) { return ( - + ) } diff --git a/src/components/CircularProgress.tsx b/src/components/CircularProgress.tsx index b48ff777..2aa318e6 100644 --- a/src/components/CircularProgress.tsx +++ b/src/components/CircularProgress.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { REDUCE_MOTION_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useLocalStorage from 'hooks/useLocalStorage' interface Props { @@ -11,7 +11,10 @@ interface Props { } export const CircularProgress = ({ color = '#FFFFFF', size = 20, className }: Props) => { - const [reduceMotion] = useLocalStorage(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion) + const [reduceMotion] = useLocalStorage( + LocalStorageKeys.REDUCE_MOTION, + DEFAULT_SETTINGS.reduceMotion, + ) const borderWidth = `${size / 10}px` const borderColor = `${color} transparent transparent transparent` const loaderClasses = classNames('inline-block relative', className) diff --git a/src/components/DisplayCurrency.tsx b/src/components/DisplayCurrency.tsx index 8939deb3..f361201e 100644 --- a/src/components/DisplayCurrency.tsx +++ b/src/components/DisplayCurrency.tsx @@ -3,7 +3,7 @@ import { useMemo } from 'react' import { FormattedNumber } from 'components/FormattedNumber' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { DISPLAY_CURRENCY_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import { ORACLE_DENOM } from 'constants/oracle' import useLocalStorage from 'hooks/useLocalStorage' import usePrices from 'hooks/usePrices' @@ -22,7 +22,7 @@ interface Props { export default function DisplayCurrency(props: Props) { const displayCurrencies = getDisplayCurrencies() const [displayCurrency] = useLocalStorage( - DISPLAY_CURRENCY_KEY, + LocalStorageKeys.DISPLAY_CURRENCY, DEFAULT_SETTINGS.displayCurrency, ) const { data: prices } = usePrices() diff --git a/src/components/Earn/Farm/Table/AvailableVaultsTable.tsx b/src/components/Earn/Farm/Table/AvailableVaultsTable.tsx new file mode 100644 index 00000000..6107929f --- /dev/null +++ b/src/components/Earn/Farm/Table/AvailableVaultsTable.tsx @@ -0,0 +1,22 @@ +import React from 'react' + +import useAvailableColumns from 'components/Earn/Farm/Table/Columns/useAvailableColumns' +import Table from 'components/Table' + +type Props = { + data: Vault[] + isLoading: boolean +} + +export default function AvailableVaultsTable(props: Props) { + const columns = useAvailableColumns({ isLoading: props.isLoading }) + + return ( +
+ ) +} diff --git a/src/components/Earn/Farm/Table/Columns/Apy.tsx b/src/components/Earn/Farm/Table/Columns/Apy.tsx new file mode 100644 index 00000000..e1000ed6 --- /dev/null +++ b/src/components/Earn/Farm/Table/Columns/Apy.tsx @@ -0,0 +1,25 @@ +import React from 'react' + +import { FormattedNumber } from 'components/FormattedNumber' +import Loading from 'components/Loading' + +export const APY_META = { accessorKey: 'apy', header: 'APY' } + +interface Props { + vault: Vault | DepositedVault +} + +export default function Apy(props: Props) { + const { vault } = props + + if (vault.apy === null) return + + return ( + + ) +} diff --git a/src/components/Earn/Farm/Table/Columns/Deposit.tsx b/src/components/Earn/Farm/Table/Columns/Deposit.tsx new file mode 100644 index 00000000..207b21ca --- /dev/null +++ b/src/components/Earn/Farm/Table/Columns/Deposit.tsx @@ -0,0 +1,32 @@ +import React from 'react' + +import ActionButton from 'components/Button/ActionButton' +import Loading from 'components/Loading' +import useStore from 'store' + +interface Props { + vault: Vault | DepositedVault + isLoading: boolean +} + +export const Deposit = (props: Props) => { + const { vault } = props + + function enterVaultHandler() { + useStore.setState({ + vaultModal: { + vault, + selectedBorrowDenoms: [vault.denoms.secondary], + isCreate: true, + }, + }) + } + + if (props.isLoading) return + + return ( +
+ +
+ ) +} diff --git a/src/components/Earn/Farm/Table/Columns/DepositCap.tsx b/src/components/Earn/Farm/Table/Columns/DepositCap.tsx new file mode 100644 index 00000000..37ca70b9 --- /dev/null +++ b/src/components/Earn/Farm/Table/Columns/DepositCap.tsx @@ -0,0 +1,57 @@ +import { Row } from '@tanstack/react-table' + +import { FormattedNumber } from 'components/FormattedNumber' +import Loading from 'components/Loading' +import TitleAndSubCell from 'components/TitleAndSubCell' +import { VAULT_DEPOSIT_BUFFER } from 'constants/vaults' +import { getAssetByDenom } from 'utils/assets' + +export const DEPOSIT_CAP_META = { accessorKey: 'cap', header: 'Deposit Cap' } + +interface Props { + vault: Vault | DepositedVault + isLoading: boolean +} + +export const depositCapSortingFn = ( + a: Row | Row, + b: Row | Row, +): number => { + const depositCapA = a.original.cap.max + const depositCapB = b.original.cap.max + return depositCapA.minus(depositCapB).toNumber() +} + +export default function DepositCap(props: Props) { + const { vault } = props + + if (props.isLoading) return + + const percent = vault.cap.used + .dividedBy(vault.cap.max.multipliedBy(VAULT_DEPOSIT_BUFFER)) + .multipliedBy(100) + .integerValue() + + const decimals = getAssetByDenom(vault.cap.denom)?.decimals ?? 6 + + return ( + + } + sub={ + + } + /> + ) +} diff --git a/src/components/Earn/Farm/Table/Columns/Details.tsx b/src/components/Earn/Farm/Table/Columns/Details.tsx new file mode 100644 index 00000000..65e920c5 --- /dev/null +++ b/src/components/Earn/Farm/Table/Columns/Details.tsx @@ -0,0 +1,24 @@ +import classNames from 'classnames' +import React from 'react' + +import { ChevronDown } from 'components/Icons' +import Loading from 'components/Loading' + +export const DETAILS_META = { accessorKey: 'details', enableSorting: false, header: 'Deposit' } + +interface Props { + isLoading: boolean + isExpanded: boolean +} + +export default function Details(props: Props) { + if (props.isLoading) return + + return ( +
+
+ +
+
+ ) +} diff --git a/src/components/Earn/Farm/Table/Columns/MaxLTV.tsx b/src/components/Earn/Farm/Table/Columns/MaxLTV.tsx new file mode 100644 index 00000000..7d90c756 --- /dev/null +++ b/src/components/Earn/Farm/Table/Columns/MaxLTV.tsx @@ -0,0 +1,23 @@ +import React from 'react' + +import { FormattedNumber } from 'components/FormattedNumber' +import Loading from 'components/Loading' + +export const LTV_MAX_META = { accessorKey: 'ltv.max', header: 'Max LTV' } + +interface Props { + vault: Vault | DepositedVault + isLoading: boolean +} +export default function MaxLtv(props: Props) { + const { vault } = props + if (props.isLoading) return + return ( + + ) +} diff --git a/src/components/Earn/Farm/Table/Columns/Name.tsx b/src/components/Earn/Farm/Table/Columns/Name.tsx new file mode 100644 index 00000000..d891bce0 --- /dev/null +++ b/src/components/Earn/Farm/Table/Columns/Name.tsx @@ -0,0 +1,72 @@ +import classNames from 'classnames' +import React from 'react' + +import VaultLogo from 'components/Earn/Farm/VaultLogo' +import Text from 'components/Text' +import TitleAndSubCell from 'components/TitleAndSubCell' +import { VaultStatus } from 'types/enums/vault' +import { produceCountdown } from 'utils/formatters' + +export const NAME_META = { id: 'name', header: 'Vault', accessorKey: 'name' } +interface Props { + vault: Vault | DepositedVault +} + +export default function Name(props: Props) { + const { vault } = props + const timeframe = vault.lockup.timeframe[0] + const unlockDuration = !!timeframe ? ` - (${vault.lockup.duration}${timeframe})` : '' + + let remainingTime = 0 + let status: VaultStatus = VaultStatus.ACTIVE + if ('status' in vault) { + status = vault.status as VaultStatus + if (vault.status === VaultStatus.UNLOCKING && vault.unlocksAt) { + remainingTime = vault.unlocksAt - Date.now() + } + } + + return ( +
+ + + {status === VaultStatus.UNLOCKING && ( + + + Unlocking + + + {produceCountdown(remainingTime)} + + + )} + {status === VaultStatus.UNLOCKED && ( + + Unlocked + + )} +
+ ) +} diff --git a/src/components/Earn/Farm/Table/Columns/PositionValue.tsx b/src/components/Earn/Farm/Table/Columns/PositionValue.tsx new file mode 100644 index 00000000..b3e4590d --- /dev/null +++ b/src/components/Earn/Farm/Table/Columns/PositionValue.tsx @@ -0,0 +1,23 @@ +import React from 'react' + +import DisplayCurrency from 'components/DisplayCurrency' +import { ORACLE_DENOM } from 'constants/oracle' +import { BNCoin } from 'types/classes/BNCoin' + +export const POSITION_VALUE_META = { + header: 'Pos. Value', +} + +interface Props { + vault: DepositedVault + isLoading: boolean +} +export default function PositionValue(props: Props) { + const { vault } = props + const positionValue = vault.values.primary + .plus(vault.values.secondary) + .plus(vault.values.unlocking) + .plus(vault.values.unlocked) + const coin = BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, positionValue) + return +} diff --git a/src/components/Earn/Farm/Table/Columns/TVL.tsx b/src/components/Earn/Farm/Table/Columns/TVL.tsx new file mode 100644 index 00000000..8b9e1b9b --- /dev/null +++ b/src/components/Earn/Farm/Table/Columns/TVL.tsx @@ -0,0 +1,20 @@ +import React from 'react' + +import DisplayCurrency from 'components/DisplayCurrency' +import Loading from 'components/Loading' +import { BNCoin } from 'types/classes/BNCoin' + +export const TVL_META = { accessorKey: 'tvl', header: 'TVL' } + +interface Props { + vault: Vault | DepositedVault + isLoading: boolean +} + +export default function TVL(props: Props) { + const { vault } = props + if (props.isLoading) return + const coin = BNCoin.fromDenomAndBigNumber(vault.cap.denom, vault.cap.used) + + return +} diff --git a/src/components/Earn/Farm/Table/Columns/useAvailableColumns.tsx b/src/components/Earn/Farm/Table/Columns/useAvailableColumns.tsx new file mode 100644 index 00000000..132ab70d --- /dev/null +++ b/src/components/Earn/Farm/Table/Columns/useAvailableColumns.tsx @@ -0,0 +1,50 @@ +import { ColumnDef } from '@tanstack/react-table' +import { useMemo } from 'react' + +import Apy, { APY_META } from 'components/Earn/Farm/Table/Columns/Apy' +import { Deposit } from 'components/Earn/Farm/Table/Columns/Deposit' +import DepositCap, { + DEPOSIT_CAP_META, + depositCapSortingFn, +} from 'components/Earn/Farm/Table/Columns/DepositCap' +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 { DETAILS_META } from './Details' + +interface Props { + isLoading: boolean +} + +export default function useAvailableColumns(props: Props) { + return useMemo[]>(() => { + return [ + { + ...NAME_META, + cell: ({ row }) => , + }, + { + ...APY_META, + cell: ({ row }) => , + }, + { + ...TVL_META, + cell: ({ row }) => , + }, + { + ...DEPOSIT_CAP_META, + cell: ({ row }) => , + sortingFn: depositCapSortingFn, + }, + { + ...LTV_MAX_META, + cell: ({ row }) => , + }, + { + ...DETAILS_META, + cell: ({ row }) => , + }, + ] + }, [props.isLoading]) +} diff --git a/src/components/Earn/Farm/Table/Columns/useDepositedColumns.tsx b/src/components/Earn/Farm/Table/Columns/useDepositedColumns.tsx new file mode 100644 index 00000000..44a6559a --- /dev/null +++ b/src/components/Earn/Farm/Table/Columns/useDepositedColumns.tsx @@ -0,0 +1,63 @@ +import { ColumnDef, Row } from '@tanstack/react-table' +import { useMemo } from 'react' + +import Apy, { APY_META } from 'components/Earn/Farm/Table/Columns/Apy' +import DepositCap, { + DEPOSIT_CAP_META, + depositCapSortingFn, +} from 'components/Earn/Farm/Table/Columns/DepositCap' +import Details, { DETAILS_META } from 'components/Earn/Farm/Table/Columns/Details' +import MaxLTV, { LTV_MAX_META } from 'components/Earn/Farm/Table/Columns/MaxLTV' +import Name, { NAME_META } from 'components/Earn/Farm/Table/Columns/Name' +import PositionValue, { + POSITION_VALUE_META, +} from 'components/Earn/Farm/Table/Columns/PositionValue' +import TVL, { TVL_META } from 'components/Earn/Farm/Table/Columns/TVL' + +interface Props { + isLoading: boolean +} + +export default function useDepositedColumns(props: Props) { + return useMemo[]>(() => { + return [ + { + ...NAME_META, + cell: ({ row }) => , + }, + { + ...POSITION_VALUE_META, + cell: ({ row }: { row: Row }) => ( + + ), + }, + { + ...APY_META, + cell: ({ row }) => , + }, + { + ...TVL_META, + cell: ({ row }) => ( + + ), + }, + { + ...DEPOSIT_CAP_META, + cell: ({ row }) => ( + + ), + sortingFn: depositCapSortingFn, + }, + { + ...LTV_MAX_META, + cell: ({ row }) => ( + + ), + }, + { + ...DETAILS_META, + cell: ({ row }) =>
, + }, + ] + }, [props.isLoading]) +} diff --git a/src/components/Earn/Farm/Table/DepositedVaultsTable.tsx b/src/components/Earn/Farm/Table/DepositedVaultsTable.tsx new file mode 100644 index 00000000..4d6e23c2 --- /dev/null +++ b/src/components/Earn/Farm/Table/DepositedVaultsTable.tsx @@ -0,0 +1,33 @@ +import { Row } from '@tanstack/react-table' +import { Table as TanStackTable } from '@tanstack/table-core/build/lib/types' +import React, { useCallback } from 'react' + +import useDepositedColumns from 'components/Earn/Farm/Table/Columns/useDepositedColumns' +import VaultExpanded from 'components/Earn/Farm/VaultExpanded' +import Table from 'components/Table' + +type Props = { + data: DepositedVault[] + isLoading: boolean +} + +export default function DepositedVaultsTable(props: Props) { + const columns = useDepositedColumns({ isLoading: props.isLoading }) + + const renderExpanded = useCallback( + (row: Row, table: TanStackTable) => ( + + ), + [], + ) + + return ( +
+ ) +} diff --git a/src/components/Earn/Farm/VaultExpanded.tsx b/src/components/Earn/Farm/VaultExpanded.tsx index 5c4d7532..b9f70292 100644 --- a/src/components/Earn/Farm/VaultExpanded.tsx +++ b/src/components/Earn/Farm/VaultExpanded.tsx @@ -6,23 +6,23 @@ import Button from 'components/Button' import { AccountArrowDown, LockLocked, LockUnlocked, Plus } from 'components/Icons' import { Tooltip } from 'components/Tooltip' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { SLIPPAGE_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useAccountId from 'hooks/useAccountId' import useLocalStorage from 'hooks/useLocalStorage' import useStore from 'store' import { VaultStatus } from 'types/enums/vault' interface Props { - row: Row + row: Row resetExpanded: (defaultState?: boolean | undefined) => void } export default function VaultExpanded(props: Props) { - const vault = props.row.original as DepositedVault + const vault = props.row.original const accountId = useAccountId() const [isConfirming, setIsConfirming] = useState(false) const withdrawFromVaults = useStore((s) => s.withdrawFromVaults) - const [slippage] = useLocalStorage(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage) + const [slippage] = useLocalStorage(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage) function depositMoreHandler() { useStore.setState({ @@ -105,7 +105,7 @@ export default function VaultExpanded(props: Props) { return ( { e.preventDefault() const isExpanded = props.row.getIsExpanded() @@ -113,7 +113,7 @@ export default function VaultExpanded(props: Props) { !isExpanded && props.row.toggleExpanded() }} > - { e.preventDefault() diff --git a/src/components/Earn/Farm/VaultTable.tsx b/src/components/Earn/Farm/VaultTable.tsx deleted file mode 100644 index 59957bb1..00000000 --- a/src/components/Earn/Farm/VaultTable.tsx +++ /dev/null @@ -1,303 +0,0 @@ -import { - ColumnDef, - flexRender, - getCoreRowModel, - getSortedRowModel, - Row, - SortingState, - useReactTable, -} from '@tanstack/react-table' -import classNames from 'classnames' -import React from 'react' - -import ActionButton from 'components/Button/ActionButton' -import DisplayCurrency from 'components/DisplayCurrency' -import VaultExpanded from 'components/Earn/Farm/VaultExpanded' -import VaultLogo from 'components/Earn/Farm/VaultLogo' -import { VaultRow } from 'components/Earn/Farm/VaultRow' -import { FormattedNumber } from 'components/FormattedNumber' -import { ChevronDown, SortAsc, SortDesc, SortNone } from 'components/Icons' -import Loading from 'components/Loading' -import Text from 'components/Text' -import TitleAndSubCell from 'components/TitleAndSubCell' -import { ORACLE_DENOM } from 'constants/oracle' -import { VAULT_DEPOSIT_BUFFER } from 'constants/vaults' -import useStore from 'store' -import { BNCoin } from 'types/classes/BNCoin' -import { VaultStatus } from 'types/enums/vault' -import { getAssetByDenom } from 'utils/assets' -import { formatPercent, produceCountdown } from 'utils/formatters' - -type Props = { - data: Vault[] | DepositedVault[] - isLoading?: boolean -} - -export const VaultTable = (props: Props) => { - const [sorting, setSorting] = React.useState([{ id: 'name', desc: true }]) - - const columns = React.useMemo[]>(() => { - return [ - { - header: 'Vault', - accessorKey: 'name', - cell: ({ row }) => { - const vault = row.original as DepositedVault - const timeframe = vault.lockup.timeframe[0] - const unlockDuration = !!timeframe ? ` - (${vault.lockup.duration}${timeframe})` : '' - - const status = vault.status - let remainingTime = 0 - - if (status === VaultStatus.UNLOCKING && vault.unlocksAt) { - remainingTime = vault.unlocksAt - Date.now() - } - - return ( -
- - - {status === VaultStatus.UNLOCKING && ( - - - Unlocking - - - {produceCountdown(remainingTime)} - - - )} - {status === VaultStatus.UNLOCKED && ( - - Unlocked - - )} -
- ) - }, - }, - - ...((props.data[0] as DepositedVault)?.values - ? [ - { - header: 'Pos. Value', - cell: ({ row }: { row: Row }) => { - const vault = row.original as DepositedVault - const positionValue = vault.values.primary - .plus(vault.values.secondary) - .plus(vault.values.unlocking) - .plus(vault.values.unlocked) - const coin = BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, positionValue) - return - }, - }, - ] - : []), - { - accessorKey: 'apy', - header: 'APY', - cell: ({ row }) => { - const vault = row.original as DepositedVault - if (vault.apy === null) return - return ( - - ) - }, - }, - { - accessorKey: 'tvl', - header: 'TVL', - cell: ({ row }) => { - const vault = row.original as DepositedVault - if (props.isLoading) return - const coin = new BNCoin({ - denom: vault.cap.denom, - amount: vault.cap.used.toString(), - }) - - return - }, - }, - { - accessorKey: 'cap', - header: 'Depo. Cap', - cell: ({ row }) => { - const vault = row.original as DepositedVault - if (props.isLoading) return - const percent = vault.cap.used - .dividedBy(vault.cap.max.multipliedBy(VAULT_DEPOSIT_BUFFER)) - .multipliedBy(100) - .integerValue() - - const decimals = getAssetByDenom(vault.cap.denom)?.decimals ?? 6 - - return ( - - } - sub={ - - } - /> - ) - }, - }, - { - accessorKey: 'ltv.max', - header: 'Max LTV', - cell: ({ row }) => { - if (props.isLoading) return - return {formatPercent(row.original.ltv.max)} - }, - }, - { - accessorKey: 'details', - enableSorting: false, - header: (data) => { - const tableData = data.table.options.data as DepositedVault[] - if (tableData.length && tableData[0].status) return 'Details' - return 'Deposit' - }, - cell: ({ row }) => { - const vault = row.original as DepositedVault - - function enterVaultHandler() { - useStore.setState({ - vaultModal: { - vault, - selectedBorrowDenoms: [vault.denoms.secondary], - isCreate: true, - }, - }) - } - - if (props.isLoading) return - return ( -
- {vault.status ? ( -
- -
- ) : ( - - )} -
- ) - }, - }, - ] - }, [props.data, props.isLoading]) - - const table = useReactTable({ - data: props.data, - columns: columns, - state: { - sorting, - }, - onSortingChange: setSorting, - getCoreRowModel: getCoreRowModel(), - getSortedRowModel: getSortedRowModel(), - }) - - return ( -
+
{status && } {status === VaultStatus.ACTIVE && } diff --git a/src/components/Earn/Farm/VaultRow.tsx b/src/components/Earn/Farm/VaultRow.tsx index 49fc31b3..60eaf2a9 100644 --- a/src/components/Earn/Farm/VaultRow.tsx +++ b/src/components/Earn/Farm/VaultRow.tsx @@ -12,9 +12,9 @@ export const VaultRow = (props: AssetRowProps) => {
- - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { - if (row.getIsExpanded()) { - return ( - - - - - ) - } - return ( - - ) - })} - -
-
- - {header.column.getCanSort() - ? { - asc: , - desc: , - false: , - }[header.column.getIsSorted() as string] ?? null - : null} - - - {flexRender(header.column.columnDef.header, header.getContext())} - -
-
- ) -} diff --git a/src/components/Earn/Farm/VaultUnlockBanner.tsx b/src/components/Earn/Farm/VaultUnlockBanner.tsx index 84fbcaea..84e57c64 100644 --- a/src/components/Earn/Farm/VaultUnlockBanner.tsx +++ b/src/components/Earn/Farm/VaultUnlockBanner.tsx @@ -4,7 +4,7 @@ import Button from 'components/Button' import { ChevronRight } from 'components/Icons' import NotificationBanner from 'components/NotificationBanner' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { SLIPPAGE_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useAccountId from 'hooks/useAccountId' import useLocalStorage from 'hooks/useLocalStorage' import useStore from 'store' @@ -17,7 +17,7 @@ export default function VaultUnlockBanner(props: Props) { const accountId = useAccountId() const [isConfirming, setIsConfirming] = useState(false) const withdrawFromVaults = useStore((s) => s.withdrawFromVaults) - const [slippage] = useLocalStorage(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage) + const [slippage] = useLocalStorage(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage) async function handleWithdraw() { if (!accountId) return diff --git a/src/components/Earn/Farm/Vaults.tsx b/src/components/Earn/Farm/Vaults.tsx index 03d1c77d..ab6b8507 100644 --- a/src/components/Earn/Farm/Vaults.tsx +++ b/src/components/Earn/Farm/Vaults.tsx @@ -1,7 +1,7 @@ import { Suspense, useMemo } from 'react' -import Card from 'components/Card' -import { VaultTable } from 'components/Earn/Farm/VaultTable' +import AvailableVaultsTable from 'components/Earn/Farm/Table/AvailableVaultsTable' +import DepositedVaultsTable from 'components/Earn/Farm/Table/DepositedVaultsTable' import VaultUnlockBanner from 'components/Earn/Farm/VaultUnlockBanner' import { ENV } from 'constants/env' import { BN_ZERO } from 'constants/math' @@ -12,15 +12,10 @@ import useVaults from 'hooks/useVaults' import { NETWORK } from 'types/enums/network' import { VaultStatus } from 'types/enums/vault' -interface Props { - type: 'available' | 'deposited' -} - -function Content(props: Props) { +function Content() { const accountId = useAccountId() const { data: vaults } = useVaults() const { data: depositedVaults } = useDepositedVaults(accountId || '') - const isAvailable = props.type === 'available' const vaultsMetaData = ENV.NETWORK === NETWORK.TESTNET ? TESTNET_VAULTS_META_DATA : VAULTS_META_DATA @@ -44,13 +39,9 @@ function Content(props: Props) { ) }, [vaults, depositedVaults, vaultsMetaData]) - const vaultsToDisplay = isAvailable ? available : deposited - - if (!vaultsToDisplay.length) return null - const unlockedVaults: DepositedVault[] = [] - if (!isAvailable && depositedVaults?.length > 0) { + if (depositedVaults?.length > 0) { depositedVaults.forEach((vault) => { if (vault.status === VaultStatus.UNLOCKED) { unlockedVaults.push(vault) @@ -60,13 +51,11 @@ function Content(props: Props) { return ( <> - {!isAvailable && } - - - + + {deposited.length && ( + + )} + {available.length && } ) } @@ -88,25 +77,13 @@ function Fallback() { }, })) - return ( - - - - ) + return } -export function AvailableVaults() { +export default function Vaults() { return ( }> - - - ) -} - -export function DepositedVaults() { - return ( - - + ) } diff --git a/src/components/Earn/Lend/LendingActionButtons.tsx b/src/components/Earn/Lend/LendingActionButtons.tsx index 36761f36..01cc1870 100644 --- a/src/components/Earn/Lend/LendingActionButtons.tsx +++ b/src/components/Earn/Lend/LendingActionButtons.tsx @@ -3,7 +3,7 @@ import { useCallback } from 'react' import { ACCOUNT_MENU_BUTTON_ID } from 'components/Account/AccountMenuContent' import Button from 'components/Button' import ActionButton from 'components/Button/ActionButton' -import { ArrowDownLine, ArrowUpLine, Enter } from 'components/Icons' +import { ArrowDownLine, ArrowUpLine, Enter, ExclamationMarkCircled } from 'components/Icons' import Text from 'components/Text' import { Tooltip } from 'components/Tooltip' import ConditionalWrapper from 'hocs/ConditionalWrapper' @@ -33,12 +33,13 @@ export default function LendingActionButtons(props: Props) { const accountId = useAccountId() const hasNoDeposit = !!(!assetDepositAmount && address && accountId) - const handleWithdraw = useCallback(() => { + const handleUnlend = useCallback(() => { if (isAutoLendEnabledForCurrentAccount) { showAlertDialog({ + icon: , title: 'Disable Automatically Lend Assets', - description: - "Your auto-lend feature is currently enabled. To recover your funds, please confirm if you'd like to disable this feature in order to continue.", + content: + "Your auto-lend feature is currently enabled. To unlend your funds, please confirm if you'd like to disable this feature in order to continue.", positiveButton: { onClick: () => document.getElementById(ACCOUNT_MENU_BUTTON_ID)?.click(), text: 'Continue to Account Settings', @@ -62,10 +63,10 @@ export default function LendingActionButtons(props: Props) { leftIcon={} iconClassName={iconClassnames} color='secondary' - onClick={handleWithdraw} + onClick={handleUnlend} className={buttonClassnames} > - Withdraw + Unlend )} diff --git a/src/components/Earn/Lend/LendingMarketsTable.tsx b/src/components/Earn/Lend/LendingMarketsTable.tsx deleted file mode 100644 index 63ce1f8a..00000000 --- a/src/components/Earn/Lend/LendingMarketsTable.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import { ColumnDef, Row, Table } from '@tanstack/react-table' -import { useCallback, useMemo } from 'react' - -import AmountAndValue from 'components/AmountAndValue' -import AssetImage from 'components/Asset/AssetImage' -import AssetRate from 'components/Asset/AssetRate' -import LendingActionButtons from 'components/Earn/Lend/LendingActionButtons' -import { FormattedNumber } from 'components/FormattedNumber' -import { ChevronDown, ChevronUp } from 'components/Icons' -import AssetListTable from 'components/MarketAssetTable' -import MarketAssetTableRow from 'components/MarketAssetTable/MarketAssetTableRow' -import MarketDetails from 'components/MarketAssetTable/MarketDetails' -import TitleAndSubCell from 'components/TitleAndSubCell' -import { BN_ZERO } from 'constants/math' -import { convertLiquidityRateToAPR } from 'utils/formatters' -import { BN } from 'utils/helpers' - -interface Props { - title: string - data: LendingMarketTableData[] -} - -export default function LendingMarketsTable(props: Props) { - const { title, data } = props - const shouldShowAccountDeposit = !!data[0]?.accountLentValue - - const rowRenderer = useCallback( - (row: Row, table: Table) => { - return ( - } - expandedDetails={} - /> - ) - }, - [], - ) - - const columns = useMemo[]>( - () => [ - { - accessorKey: 'asset.name', - header: 'Asset', - id: 'symbol', - cell: ({ row }) => { - const asset = row.original.asset - - return ( -
- - -
- ) - }, - }, - ...(shouldShowAccountDeposit - ? [ - { - accessorKey: 'accountDepositValue', - header: 'Deposited', - cell: ({ row }) => { - const amount = row.original.accountLentAmount - - return ( - - ) - }, - } as ColumnDef, - ] - : []), - { - accessorKey: 'marketLiquidityRate', - header: 'APR', - cell: ({ row }) => { - return ( - - ) - }, - }, - { - accessorKey: 'marketDepositCap', - header: 'Depo. Cap', - cell: ({ row }) => { - const { marketDepositCap, marketDepositAmount, asset } = row.original - const remainingCap = row.original.marketDepositCap.minus(marketDepositAmount) - - return ( - - } - sub={ - - } - /> - ) - }, - }, - { - accessorKey: 'manage', - enableSorting: false, - header: 'Manage', - cell: ({ row }) => ( -
-
{row.getIsExpanded() ? : }
-
- ), - }, - ], - [shouldShowAccountDeposit], - ) - - return -} diff --git a/src/components/Earn/Lend/Lends.tsx b/src/components/Earn/Lend/Lends.tsx new file mode 100644 index 00000000..ced4ab25 --- /dev/null +++ b/src/components/Earn/Lend/Lends.tsx @@ -0,0 +1,37 @@ +import React from 'react' + +import AvailableLendsTable from 'components/Earn/Lend/Table/AvailableLendsTable' +import DepositedLendsTable from 'components/Earn/Lend/Table/DepositedLendsTable' +import { BN_ZERO } from 'constants/math' +import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData' +import { getLendEnabledAssets } from 'utils/assets' + +export default function Lends() { + const { accountLentAssets, availableAssets, allAssets } = useLendingMarketAssetsTableData() + + if (!allAssets?.length) { + return + } + return ( + <> + + + + ) +} + +function Fallback() { + const assets = getLendEnabledAssets() + const data: LendingMarketTableData[] = assets.map((asset) => ({ + asset, + marketDepositCap: BN_ZERO, + borrowEnabled: false, + marketMaxLtv: 0, + marketDepositAmount: BN_ZERO, + marketLiquidityRate: 0, + marketLiquidityAmount: BN_ZERO, + marketLiquidationThreshold: 0, + })) + + return +} diff --git a/src/components/Earn/Lend/Table/AvailableLendsTable.tsx b/src/components/Earn/Lend/Table/AvailableLendsTable.tsx new file mode 100644 index 00000000..e93d83b6 --- /dev/null +++ b/src/components/Earn/Lend/Table/AvailableLendsTable.tsx @@ -0,0 +1,42 @@ +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 useAvailableColumns from 'components/Earn/Lend/Table/Columns/useAvailableColumns' +import MarketDetails from 'components/MarketDetails' +import Table from 'components/Table' +import ActionButtonRow from 'components/Table/ActionButtonRow' + +type Props = { + data: LendingMarketTableData[] + isLoading: boolean +} + +export default function AvailableLendsTable(props: Props) { + const columns = useAvailableColumns({ isLoading: props.isLoading }) + + const renderExpanded = useCallback( + (row: Row) => ( + <> + + + + + + ), + [], + ) + + if (!props.data.length) return null + + return ( + + ) +} diff --git a/src/components/Earn/Lend/Table/Columns/Apy.tsx b/src/components/Earn/Lend/Table/Columns/Apy.tsx new file mode 100644 index 00000000..11996422 --- /dev/null +++ b/src/components/Earn/Lend/Table/Columns/Apy.tsx @@ -0,0 +1,24 @@ +import AssetRate from 'components/Asset/AssetRate' +import Loading from 'components/Loading' +import { convertAprToApy } from 'utils/parsers' + +export const APY_META = { accessorKey: 'marketLiquidityRate', header: 'APY' } + +interface Props { + marketLiquidityRate: number + borrowEnabled: boolean + isLoading: boolean +} +export default function Apr(props: Props) { + if (props.isLoading) return + + return ( + + ) +} diff --git a/src/components/Earn/Lend/Table/Columns/DepositCap.tsx b/src/components/Earn/Lend/Table/Columns/DepositCap.tsx new file mode 100644 index 00000000..280cec4a --- /dev/null +++ b/src/components/Earn/Lend/Table/Columns/DepositCap.tsx @@ -0,0 +1,55 @@ +import { Row } from '@tanstack/react-table' + +import { FormattedNumber } from 'components/FormattedNumber' +import Loading from 'components/Loading' +import TitleAndSubCell from 'components/TitleAndSubCell' +import { demagnify } from 'utils/formatters' + +export const DEPOSIT_CAP_META = { + accessorKey: 'marketDepositCap', + header: 'Depo. Cap', + id: 'marketDepositCap', +} + +export const marketDepositCapSortingFn = ( + a: Row, + b: Row, +): number => { + const assetA = a.original.asset + const assetB = b.original.asset + if (!a.original.marketDepositCap || !b.original.marketDepositCap) return 0 + + const marketDepositCapA = demagnify(a.original.marketDepositCap, assetA) + const marketDepositCapB = demagnify(b.original.marketDepositCap, assetB) + return marketDepositCapA - marketDepositCapB +} + +interface Props { + isLoading: boolean + data: LendingMarketTableData +} +export default function DepositCap(props: Props) { + if (props.isLoading) return + const { marketDepositCap, marketDepositAmount, asset } = props.data + const percent = marketDepositAmount.dividedBy(marketDepositCap).multipliedBy(100) + + return ( + + } + sub={ + + } + /> + ) +} diff --git a/src/components/Earn/Lend/Table/Columns/DepositValue.tsx b/src/components/Earn/Lend/Table/Columns/DepositValue.tsx new file mode 100644 index 00000000..3d895ed8 --- /dev/null +++ b/src/components/Earn/Lend/Table/Columns/DepositValue.tsx @@ -0,0 +1,32 @@ +import { Row } from '@tanstack/react-table' + +import AmountAndValue from 'components/AmountAndValue' +import { BN_ZERO } from 'constants/math' +import { BN } from 'utils/helpers' + +export const DEPOSIT_VALUE_META = { + accessorKey: 'accountLentValue', + header: 'Deposited', +} + +export const depositedSortingFn = ( + a: Row, + b: Row, +): number => { + const depositValueA = BN(a.original?.accountLentValue ?? 0) + const depositValueB = BN(b.original?.accountLentValue ?? 0) + return depositValueA.minus(depositValueB).toNumber() +} + +interface Props { + asset: Asset + lentAmount?: string +} +export default function DepositValue(props: Props) { + return ( + + ) +} diff --git a/src/components/Earn/Lend/Table/Columns/Manage.tsx b/src/components/Earn/Lend/Table/Columns/Manage.tsx new file mode 100644 index 00000000..3db5df17 --- /dev/null +++ b/src/components/Earn/Lend/Table/Columns/Manage.tsx @@ -0,0 +1,16 @@ +import React from 'react' + +import { ChevronDown, ChevronUp } from 'components/Icons' + +export const MANAGE_META = { accessorKey: 'manage', enableSorting: false, header: 'Manage' } + +interface Props { + isExpanded: boolean +} +export default function Manage(props: Props) { + return ( +
+
{props.isExpanded ? : }
+
+ ) +} diff --git a/src/components/Earn/Lend/Table/Columns/Name.tsx b/src/components/Earn/Lend/Table/Columns/Name.tsx new file mode 100644 index 00000000..965b0513 --- /dev/null +++ b/src/components/Earn/Lend/Table/Columns/Name.tsx @@ -0,0 +1,16 @@ +import AssetImage from 'components/Asset/AssetImage' +import TitleAndSubCell from 'components/TitleAndSubCell' + +export const NAME_META = { accessorKey: 'asset.symbol', header: 'Asset', id: 'symbol' } +interface Props { + asset: Asset +} +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 new file mode 100644 index 00000000..2fdf2269 --- /dev/null +++ b/src/components/Earn/Lend/Table/Columns/useAvailableColumns.tsx @@ -0,0 +1,44 @@ +import { ColumnDef } from '@tanstack/react-table' +import { useMemo } from 'react' + +import Apy, { APY_META } from 'components/Earn/Lend/Table/Columns/Apy' +import DepositCap, { + DEPOSIT_CAP_META, + marketDepositCapSortingFn, +} from 'components/Earn/Lend/Table/Columns/DepositCap' +import Manage, { MANAGE_META } from 'components/Earn/Lend/Table/Columns/Manage' +import Name, { NAME_META } from 'components/Earn/Lend/Table/Columns/Name' + +interface Props { + isLoading: boolean +} + +export default function useAvailableColumns(props: Props) { + return useMemo[]>(() => { + return [ + { + ...NAME_META, + cell: ({ row }) => , + }, + { + ...APY_META, + cell: ({ row }) => ( + + ), + }, + { + ...DEPOSIT_CAP_META, + cell: ({ row }) => , + sortingFn: marketDepositCapSortingFn, + }, + { + ...MANAGE_META, + cell: ({ row }) => , + }, + ] + }, [props.isLoading]) +} diff --git a/src/components/Earn/Lend/Table/Columns/useDepositedColumns.tsx b/src/components/Earn/Lend/Table/Columns/useDepositedColumns.tsx new file mode 100644 index 00000000..73127ed3 --- /dev/null +++ b/src/components/Earn/Lend/Table/Columns/useDepositedColumns.tsx @@ -0,0 +1,55 @@ +import { ColumnDef } from '@tanstack/react-table' +import { useMemo } from 'react' + +import Apy, { APY_META } from 'components/Earn/Lend/Table/Columns/Apy' +import DepositCap, { + DEPOSIT_CAP_META, + marketDepositCapSortingFn, +} from 'components/Earn/Lend/Table/Columns/DepositCap' +import DepositValue, { + DEPOSIT_VALUE_META, + depositedSortingFn, +} 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' + +interface Props { + isLoading: boolean +} + +export default function useDepositedColumns(props: Props) { + return useMemo[]>(() => { + return [ + { + ...NAME_META, + cell: ({ row }) => , + }, + { + ...DEPOSIT_VALUE_META, + cell: ({ row }) => ( + + ), + sortingFn: depositedSortingFn, + }, + { + ...APY_META, + cell: ({ row }) => ( + + ), + }, + { + ...DEPOSIT_CAP_META, + cell: ({ row }) => , + sortingFn: marketDepositCapSortingFn, + }, + { + ...MANAGE_META, + cell: ({ row }) => , + }, + ] + }, [props.isLoading]) +} diff --git a/src/components/Earn/Lend/Table/DepositedLendsTable.tsx b/src/components/Earn/Lend/Table/DepositedLendsTable.tsx new file mode 100644 index 00000000..ff355480 --- /dev/null +++ b/src/components/Earn/Lend/Table/DepositedLendsTable.tsx @@ -0,0 +1,42 @@ +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/MarketDetails' +import Table from 'components/Table' +import ActionButtonRow from 'components/Table/ActionButtonRow' + +type Props = { + data: LendingMarketTableData[] + isLoading: boolean +} + +export default function DepositedLendsTable(props: Props) { + const columns = useDepositedColumns({ isLoading: props.isLoading }) + + const renderExpanded = useCallback( + (row: Row) => ( + <> + + + + + + ), + [], + ) + + if (!props.data.length) return null + + return ( +
+ ) +} diff --git a/src/components/FormattedNumber.tsx b/src/components/FormattedNumber.tsx index 798cb33c..964c49f8 100644 --- a/src/components/FormattedNumber.tsx +++ b/src/components/FormattedNumber.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useRef } from 'react' import { animated, useSpring } from 'react-spring' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { REDUCE_MOTION_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useLocalStorage from 'hooks/useLocalStorage' import { formatValue } from 'utils/formatters' @@ -19,7 +19,7 @@ interface Props { export const FormattedNumber = React.memo( (props: Props) => { const [reduceMotion] = useLocalStorage( - REDUCE_MOTION_KEY, + LocalStorageKeys.REDUCE_MOTION, DEFAULT_SETTINGS.reduceMotion, ) const prevAmountRef = useRef(0) diff --git a/src/components/Gauge.tsx b/src/components/Gauge.tsx index 0e948498..b0f31e58 100644 --- a/src/components/Gauge.tsx +++ b/src/components/Gauge.tsx @@ -3,7 +3,7 @@ import { ReactElement, ReactNode } from 'react' import { Tooltip } from 'components/Tooltip' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { REDUCE_MOTION_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useLocalStorage from 'hooks/useLocalStorage' interface Props { @@ -27,7 +27,10 @@ export const Gauge = ({ icon, labelClassName, }: Props) => { - const [reduceMotion] = useLocalStorage(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion) + const [reduceMotion] = useLocalStorage( + LocalStorageKeys.REDUCE_MOTION, + DEFAULT_SETTINGS.reduceMotion, + ) const radius = 16 const percentageValue = percentage > 100 ? 100 : percentage < 0 ? 0 : percentage const circlePercent = 100 - percentageValue diff --git a/src/components/HLS/Farm/AvailableHLSVaults.tsx b/src/components/HLS/Farm/AvailableHLSVaults.tsx new file mode 100644 index 00000000..6ab4036d --- /dev/null +++ b/src/components/HLS/Farm/AvailableHLSVaults.tsx @@ -0,0 +1,64 @@ +import { Suspense, useMemo } from 'react' + +import { NAME_META } from 'components/Earn/Farm/Table/Columns/Name' +import Table from 'components/Table' +import { ENV } from 'constants/env' +import { BN_ZERO } from 'constants/math' +import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults' +import useVaults from 'hooks/useVaults' +import { NETWORK } from 'types/enums/network' + +import useAvailableColumns from './Table/Columns/useAvailableColumns' + +const title = 'Available HLS Vaults' + +function Content() { + const { data: vaults } = useVaults() + const hlsVaults: Vault[] = useMemo(() => vaults?.filter((vault) => vault.hls) || [], [vaults]) + const columns = useAvailableColumns({ isLoading: false }) + + return ( +
+ ) +} + +export default function AvailableHlsVaults() { + return ( + }> + + + ) +} + +function Fallback() { + const columns = useAvailableColumns({ isLoading: true }) + + const vaults = ENV.NETWORK === NETWORK.TESTNET ? TESTNET_VAULTS_META_DATA : VAULTS_META_DATA + const mockVaults: Vault[] = vaults.map((vault) => ({ + ...vault, + apy: null, + apr: null, + ltv: { + max: 0, + liq: 0, + }, + cap: { + denom: 'denom', + used: BN_ZERO, + max: BN_ZERO, + }, + })) + return ( +
+ ) +} diff --git a/src/components/HLS/Farm/HLSFarmIntro.tsx b/src/components/HLS/Farm/HLSFarmIntro.tsx new file mode 100644 index 00000000..a8952492 --- /dev/null +++ b/src/components/HLS/Farm/HLSFarmIntro.tsx @@ -0,0 +1,31 @@ +import React from 'react' + +import Button from 'components/Button' +import { PlusSquared } from 'components/Icons' +import Intro from 'components/Intro' +import { DocURL } from 'types/enums/docURL' + +export default function HlsFarmIntro() { + return ( + + Leveraged farming is a strategy where users borrow + funds to increase their yield farming position, aiming to earn more in rewards than the + cost of the borrowed assets. + + } + > + - - - ) - - const renderExpanded = () => { - return ( - <> - {props.expandedActionButtons && - renderFullRow( - `${props.rowData.id}-expanded-actions`, -
- - Details - -
{props.expandedActionButtons}
-
, - )} - {props.expandedDetails && - renderFullRow(`${props.rowData.id}-expanded-details`, props.expandedDetails)} - - ) - } - - return ( - <> - { - const isExpanded = props.rowData.getIsExpanded() - props.resetExpanded() - !isExpanded && props.rowData.toggleExpanded() - }} - > - {props.rowData.getVisibleCells().map((cell) => { - return ( - - ) - })} - - {props.isExpanded && renderExpanded()} - - ) -} - -export default AssetListTableRow diff --git a/src/components/MarketAssetTable/MarketDetails.tsx b/src/components/MarketDetails.tsx similarity index 78% rename from src/components/MarketAssetTable/MarketDetails.tsx rename to src/components/MarketDetails.tsx index 80340eb2..219fe65f 100644 --- a/src/components/MarketAssetTable/MarketDetails.tsx +++ b/src/components/MarketDetails.tsx @@ -1,3 +1,4 @@ +import { Row } from '@tanstack/react-table' import { useMemo } from 'react' import { FormattedNumber } from 'components/FormattedNumber' @@ -5,7 +6,7 @@ import TitleAndSubCell from 'components/TitleAndSubCell' import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice' interface Props { - data: BorrowMarketTableData | LendingMarketTableData + row: Row type: 'borrow' | 'lend' } @@ -15,7 +16,7 @@ interface Detail { title: string } -export default function MarketDetails({ data, type }: Props) { +export default function MarketDetails({ row, type }: Props) { const { convertAmount, getConversionRate, @@ -28,7 +29,7 @@ export default function MarketDetails({ data, type }: Props) { marketDepositAmount, marketLiquidityAmount, marketLiquidationThreshold, - } = data + } = row.original const totalBorrowed = marketDepositAmount.minus(marketLiquidityAmount) @@ -36,7 +37,6 @@ export default function MarketDetails({ data, type }: Props) { const isDollar = displayCurrencySymbol === '$' function getLendingMarketDetails() { - const depositCap = (data as LendingMarketTableData).marketDepositCap return [ { amount: convertAmount(asset, marketDepositAmount).toNumber(), @@ -107,7 +107,6 @@ export default function MarketDetails({ data, type }: Props) { if (type === 'lend') return getLendingMarketDetails() return getBorrowMarketDetails() }, [ - data, type, asset, marketDepositAmount, @@ -120,23 +119,27 @@ export default function MarketDetails({ data, type }: Props) { ]) return ( -
- {details.map((detail, index) => ( - +
+ ) } diff --git a/src/components/MigrationBanner.tsx b/src/components/MigrationBanner.tsx index f3c5e610..6397aef1 100644 --- a/src/components/MigrationBanner.tsx +++ b/src/components/MigrationBanner.tsx @@ -3,14 +3,14 @@ import { Cross, ExclamationMarkCircled } from 'components/Icons' import Text from 'components/Text' import { TextLink } from 'components/TextLink' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { MIGRATION_BANNER_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useLocalStorage from 'hooks/useLocalStorage' import useStore from 'store' import { DocURL } from 'types/enums/docURL' export default function MigrationBanner() { const [_, setMigrationBanner] = useLocalStorage( - MIGRATION_BANNER_KEY, + LocalStorageKeys.MIGRATION_BANNER, DEFAULT_SETTINGS.migrationBanner, ) diff --git a/src/components/Modals/Account/AccountDeleteAlertDialog.tsx b/src/components/Modals/Account/AccountDeleteAlertDialog.tsx index 2c658579..e340fc12 100644 --- a/src/components/Modals/Account/AccountDeleteAlertDialog.tsx +++ b/src/components/Modals/Account/AccountDeleteAlertDialog.tsx @@ -4,25 +4,22 @@ import { Enter, InfoCircle } from 'components/Icons' import useAlertDialog from 'hooks/useAlertDialog' interface Props { + content: string | JSX.Element title: string - description: string | JSX.Element + icon?: JSX.Element closeHandler: () => void positiveButton: AlertDialogButton } export default function AccountDeleteAlertDialog(props: Props) { const { open: showAlertDialog } = useAlertDialog() - const { title, description, closeHandler, positiveButton } = props + const { title, content, closeHandler, positiveButton } = props useEffect(() => { showAlertDialog({ - icon: ( -
- -
- ), + icon: , title, - description, + content, negativeButton: { text: 'Cancel', icon: , @@ -30,7 +27,7 @@ export default function AccountDeleteAlertDialog(props: Props) { }, positiveButton, }) - }, [showAlertDialog, title, description, closeHandler, positiveButton]) + }, [showAlertDialog, closeHandler, positiveButton, title, content]) return null } diff --git a/src/components/Modals/Account/AccountDeleteModal.tsx b/src/components/Modals/Account/AccountDeleteModal.tsx index a436963d..9d1f2168 100644 --- a/src/components/Modals/Account/AccountDeleteModal.tsx +++ b/src/components/Modals/Account/AccountDeleteModal.tsx @@ -2,7 +2,7 @@ import { useCallback, useMemo } from 'react' import { useLocation, useNavigate, useParams } from 'react-router-dom' import AssetBalanceRow from 'components/Asset/AssetBalanceRow' -import { ArrowRight } from 'components/Icons' +import { ArrowRight, ExclamationMarkCircled } from 'components/Icons' import AccountDeleteAlertDialog from 'components/Modals/Account/AccountDeleteAlertDialog' import Text from 'components/Text' import useStore from 'store' @@ -51,7 +51,8 @@ function AccountDeleteModal(props: Props) { return ( } + content='You must repay all borrowings before deleting your account.' closeHandler={closeDeleteAccountModal} positiveButton={{ text: 'Repay Debts', @@ -68,7 +69,7 @@ function AccountDeleteModal(props: Props) { return ( The following assets within your Credit Account will be sent to your wallet. diff --git a/src/components/Modals/AddVaultAssets/AddVaultBorrowAssetsModalContent.tsx b/src/components/Modals/AddVaultAssets/AddVaultBorrowAssetsModalContent.tsx index 1365e644..361b4191 100644 --- a/src/components/Modals/AddVaultAssets/AddVaultBorrowAssetsModalContent.tsx +++ b/src/components/Modals/AddVaultAssets/AddVaultBorrowAssetsModalContent.tsx @@ -75,7 +75,7 @@ export default function AddVaultAssetsModalContent(props: Props) { return ( <> -
+
) : ( - +
+ +
)}
- {content} -
- {flexRender(cell.column.columnDef.cell, cell.getContext())} -
+
+ {details.map((detail, index) => ( + + } + sub={detail.title} /> - } - sub={detail.title} - /> - ))} -
+ ))} + +
- + {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { diff --git a/src/components/Modals/AssetsSelect/useAssetTableColumns.tsx b/src/components/Modals/AssetsSelect/useAssetTableColumns.tsx index fc3384ef..b96e5802 100644 --- a/src/components/Modals/AssetsSelect/useAssetTableColumns.tsx +++ b/src/components/Modals/AssetsSelect/useAssetTableColumns.tsx @@ -17,7 +17,7 @@ function showBorrowRate(data: AssetTableRow[]) { return !!(assetData && assetData?.borrowRate) } -export default function useAssetTableColumns() { +export default function useAssetTableColumns(isBorrow: boolean) { return React.useMemo[]>( () => [ { @@ -29,6 +29,9 @@ export default function useAssetTableColumns() { const market = row.original.market const borrowAsset = row.original.asset as BorrowAsset const showRate = !borrowAsset?.borrowRate + const rate = isBorrow ? market?.borrowRate : market?.liquidityRate + const apy = convertAprToApy((rate ?? 0) * 100, 365) + return (
@@ -39,7 +42,7 @@ export default function useAssetTableColumns() { {showRate && market ? ( -
- + {totalDebt.isGreaterThan(0) && ( + <> +
+
+
+ + +
+ + Borrowed + +
+ + )}
diff --git a/src/components/Modals/FundWithdraw/index.tsx b/src/components/Modals/FundWithdraw/index.tsx index f581146b..9e9d21a2 100644 --- a/src/components/Modals/FundWithdraw/index.tsx +++ b/src/components/Modals/FundWithdraw/index.tsx @@ -33,7 +33,9 @@ export default function FundAndWithdrawModal() { {modal && currentAccount ? ( ) : ( - +
+ +
)} ) diff --git a/src/components/Modals/LendAndReclaim/index.tsx b/src/components/Modals/LendAndReclaim/index.tsx index 8f4c2d3a..1e1aa532 100644 --- a/src/components/Modals/LendAndReclaim/index.tsx +++ b/src/components/Modals/LendAndReclaim/index.tsx @@ -32,7 +32,7 @@ function LendAndReclaimModal({ currentAccount, config }: Props) { const { asset } = data const isLendAction = action === 'lend' - const actionText = isLendAction ? 'Lend' : 'Withdraw' + const actionText = isLendAction ? 'Lend' : 'Unlend' const coinBalances = currentAccount[isLendAction ? 'deposits' : 'lends'] ?? [] const handleAmountChange = useCallback( diff --git a/src/components/Modals/Settings/index.tsx b/src/components/Modals/Settings/index.tsx index 7b984164..11bc4934 100644 --- a/src/components/Modals/Settings/index.tsx +++ b/src/components/Modals/Settings/index.tsx @@ -12,20 +12,13 @@ import Select from 'components/Select' import Text from 'components/Text' import { TextLink } from 'components/TextLink' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { - DISPLAY_CURRENCY_KEY, - LEND_ASSETS_KEY, - PREFERRED_ASSET_KEY, - REDUCE_MOTION_KEY, - SLIPPAGE_KEY, - TUTORIAL_KEY, -} from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import { BN_ZERO } from 'constants/math' import useAlertDialog from 'hooks/useAlertDialog' import useAutoLend from 'hooks/useAutoLend' import useLocalStorage from 'hooks/useLocalStorage' import useStore from 'store' -import { getDisplayCurrencies, getEnabledMarketAssets } from 'utils/assets' +import { getDisplayCurrencies } from 'utils/assets' import { BN } from 'utils/helpers' const slippages = [0.02, 0.03] @@ -34,29 +27,30 @@ export default function SettingsModal() { const modal = useStore((s) => s.settingsModal) const { open: showResetDialog } = useAlertDialog() const displayCurrencies = getDisplayCurrencies() - const assets = getEnabledMarketAssets() const { setAutoLendOnAllAccounts } = useAutoLend() const [customSlippage, setCustomSlippage] = useState(0) const [inputRef, setInputRef] = useState>() const [isCustom, setIsCustom] = useState(false) const [displayCurrency, setDisplayCurrency] = useLocalStorage( - DISPLAY_CURRENCY_KEY, + LocalStorageKeys.DISPLAY_CURRENCY, DEFAULT_SETTINGS.displayCurrency, ) - const [preferredAsset, setPreferredAsset] = useLocalStorage( - PREFERRED_ASSET_KEY, - DEFAULT_SETTINGS.preferredAsset, - ) const [reduceMotion, setReduceMotion] = useLocalStorage( - REDUCE_MOTION_KEY, + LocalStorageKeys.REDUCE_MOTION, DEFAULT_SETTINGS.reduceMotion, ) - const [tutorial, setTutorial] = useLocalStorage(TUTORIAL_KEY, DEFAULT_SETTINGS.tutorial) + const [tutorial, setTutorial] = useLocalStorage( + LocalStorageKeys.TUTORIAL, + DEFAULT_SETTINGS.tutorial, + ) const [lendAssets, setLendAssets] = useLocalStorage( - LEND_ASSETS_KEY, + LocalStorageKeys.LEND_ASSETS, DEFAULT_SETTINGS.lendAssets, ) - const [slippage, setSlippage] = useLocalStorage(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage) + const [slippage, setSlippage] = useLocalStorage( + LocalStorageKeys.SLIPPAGE, + DEFAULT_SETTINGS.slippage, + ) const displayCurrenciesOptions = useMemo( () => @@ -80,22 +74,6 @@ export default function SettingsModal() { [displayCurrencies], ) - const preferredAssetsOptions = useMemo( - () => - assets.map((asset, index) => ({ - label: [ -
- - - {asset.symbol} - -
, - ], - value: asset.denom, - })), - [assets], - ) - const handleReduceMotion = useCallback( (value: boolean) => { setReduceMotion(value) @@ -118,13 +96,6 @@ export default function SettingsModal() { [setTutorial], ) - const handlePreferredAsset = useCallback( - (value: string) => { - setPreferredAsset(value) - }, - [setPreferredAsset], - ) - const handleDisplayCurrency = useCallback( (value: string) => { setDisplayCurrency(value) @@ -175,17 +146,10 @@ export default function SettingsModal() { const handleResetSettings = useCallback(() => { handleDisplayCurrency(DEFAULT_SETTINGS.displayCurrency) - handlePreferredAsset(DEFAULT_SETTINGS.preferredAsset) handleSlippage(DEFAULT_SETTINGS.slippage) handleReduceMotion(DEFAULT_SETTINGS.reduceMotion) handleLendAssets(DEFAULT_SETTINGS.lendAssets) - }, [ - handleDisplayCurrency, - handleReduceMotion, - handleLendAssets, - handlePreferredAsset, - handleSlippage, - ]) + }, [handleDisplayCurrency, handleReduceMotion, handleLendAssets, handleSlippage]) const showResetModal = useCallback(() => { showResetDialog({ @@ -195,7 +159,7 @@ export default function SettingsModal() {
), title: 'Are you sure you want to restore to default?', - description: + content: 'Once you reset to default settings you can’t revert it, and will result in the permanent loss of your current settings', positiveButton: { text: 'Yes', @@ -268,21 +232,11 @@ export default function SettingsModal() { withStatus /> ( - DISPLAY_CURRENCY_KEY, + LocalStorageKeys.DISPLAY_CURRENCY, DEFAULT_SETTINGS.displayCurrency, ) const [isOpen, toggleOpen] = useIsOpenArray(2, false) diff --git a/src/components/Modals/WalletAssets/WalletAssetsModalContent.tsx b/src/components/Modals/WalletAssets/WalletAssetsModalContent.tsx index 9b420437..95309aa0 100644 --- a/src/components/Modals/WalletAssets/WalletAssetsModalContent.tsx +++ b/src/components/Modals/WalletAssets/WalletAssetsModalContent.tsx @@ -34,6 +34,7 @@ export default function WalletAssetsModalContent(props: Props) { ) }, [assets, searchString]) + const isBorrow = useStore((s) => s.walletAssetsModal?.isBorrow ?? false) const currentSelectedDenom = useStore((s) => s.walletAssetsModal?.selectedDenoms ?? []) const [selectedDenoms, setSelectedDenoms] = useState( currentSelectedDenom.filter((denom) => filteredAssets.findIndex(byDenom(denom)) || []), @@ -58,6 +59,7 @@ export default function WalletAssetsModalContent(props: Props) {
s.withdrawFromVaults) const baseCurrency = useStore((s) => s.baseCurrency) - const [slippage] = useLocalStorage(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage) + const [slippage] = useLocalStorage(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage) function onClose() { useStore.setState({ withdrawFromVaultsModal: null }) @@ -88,7 +88,9 @@ export default function WithdrawFromVaultsModal() {
) : ( - +
+ +
)} ) diff --git a/src/components/Portfolio/Account/Balances.tsx b/src/components/Portfolio/Account/Balances.tsx index d1736e94..e02be24a 100644 --- a/src/components/Portfolio/Account/Balances.tsx +++ b/src/components/Portfolio/Account/Balances.tsx @@ -1,4 +1,4 @@ -import React, { Suspense } from 'react' +import React, { Suspense, useMemo } from 'react' import AccountBalancesTable from 'components/Account/AccountBalancesTable' import Card from 'components/Card' @@ -15,7 +15,8 @@ interface Props { function Content(props: Props) { const { data: account } = useAccount(props.accountId, true) - const { allAssets: borrowAssets } = useBorrowMarketAssetsTableData() + const { data } = useBorrowMarketAssetsTableData(false) + const borrowAssets = useMemo(() => data?.allAssets || [], [data]) const { allAssets: lendingAssets } = useLendingMarketAssetsTableData() if (!account || !borrowAssets.length || !lendingAssets.length) { diff --git a/src/components/Portfolio/Account/Summary.tsx b/src/components/Portfolio/Account/Summary.tsx index a5b8f4b0..39b18c1b 100644 --- a/src/components/Portfolio/Account/Summary.tsx +++ b/src/components/Portfolio/Account/Summary.tsx @@ -20,7 +20,8 @@ function Content(props: Props) { const { data: account } = useAccount(props.accountId, true) const { data: prices } = usePrices() const { health } = useHealthComputer(account) - const { allAssets: borrowAssets } = useBorrowMarketAssetsTableData() + const { data } = useBorrowMarketAssetsTableData(false) + const borrowAssets = useMemo(() => data?.allAssets || [], [data]) const { allAssets: lendingAssets } = useLendingMarketAssetsTableData() const stats = useMemo(() => { diff --git a/src/components/Portfolio/Card/index.tsx b/src/components/Portfolio/Card/index.tsx index 1b9d009b..b3cd628e 100644 --- a/src/components/Portfolio/Card/index.tsx +++ b/src/components/Portfolio/Card/index.tsx @@ -6,7 +6,7 @@ import { FormattedNumber } from 'components/FormattedNumber' import Loading from 'components/Loading' import Skeleton from 'components/Portfolio/Card/Skeleton' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { REDUCE_MOTION_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import { BN_ZERO } from 'constants/math' import useAccount from 'hooks/useAccount' import useAccountId from 'hooks/useAccountId' @@ -34,8 +34,12 @@ export default function PortfolioCard(props: Props) { const { data: prices } = usePrices() const currentAccountId = useAccountId() const { allAssets: lendingAssets } = useLendingMarketAssetsTableData() - const { allAssets: borrowAssets } = useBorrowMarketAssetsTableData() - const [reduceMotion] = useLocalStorage(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion) + const { data } = useBorrowMarketAssetsTableData(false) + const borrowAssets = useMemo(() => data?.allAssets || [], [data]) + const [reduceMotion] = useLocalStorage( + LocalStorageKeys.REDUCE_MOTION, + DEFAULT_SETTINGS.reduceMotion, + ) const address = useStore((s) => s.address) const [deposits, lends, debts, vaults] = useMemo(() => { diff --git a/src/components/Portfolio/Overview/Summary.tsx b/src/components/Portfolio/Overview/Summary.tsx index 3f45b79f..c0404f7a 100644 --- a/src/components/Portfolio/Overview/Summary.tsx +++ b/src/components/Portfolio/Overview/Summary.tsx @@ -17,7 +17,8 @@ export default function PortfolioSummary() { const { address: urlAddress } = useParams() const walletAddress = useStore((s) => s.address) const { data: prices } = usePrices() - const { allAssets: borrowAssets } = useBorrowMarketAssetsTableData() + const { data } = useBorrowMarketAssetsTableData(false) + const borrowAssets = useMemo(() => data?.allAssets || [], [data]) const { allAssets: lendingAssets } = useLendingMarketAssetsTableData() const { data: accounts } = useAccounts(urlAddress || walletAddress) diff --git a/src/components/Table/ActionButtonRow.tsx b/src/components/Table/ActionButtonRow.tsx new file mode 100644 index 00000000..16faf1af --- /dev/null +++ b/src/components/Table/ActionButtonRow.tsx @@ -0,0 +1,25 @@ +import { Row as TanstackRow } from '@tanstack/react-table' +import { ReactNode } from 'react' + +import Text from 'components/Text' + +interface Props { + row: TanstackRow + children: ReactNode +} + +export default function ActionButtonRow(props: Props) { + return ( +
+ + + ) +} diff --git a/src/components/Table/Row.tsx b/src/components/Table/Row.tsx new file mode 100644 index 00000000..f7220281 --- /dev/null +++ b/src/components/Table/Row.tsx @@ -0,0 +1,42 @@ +import { flexRender, Row as TanstackRow, Table as TanstackTable } from '@tanstack/react-table' +import classNames from 'classnames' + +interface Props { + row: TanstackRow + table: TanstackTable + renderExpanded?: (row: TanstackRow, table: TanstackTable) => JSX.Element + rowClassName?: string + rowClickHandler?: () => void +} + +export default function Row(props: Props) { + return ( + <> + { + e.preventDefault() + const isExpanded = props.row.getIsExpanded() + props.table.resetExpanded() + !isExpanded && props.row.toggleExpanded() + }} + > + {props.row.getVisibleCells().map((cell) => { + return ( + + ) + })} + + {props.row.getIsExpanded() && + props.renderExpanded && + props.renderExpanded(props.row, props.table)} + + ) +} diff --git a/src/components/MarketAssetTable/index.tsx b/src/components/Table/index.tsx similarity index 67% rename from src/components/MarketAssetTable/index.tsx rename to src/components/Table/index.tsx index c546b7bf..99145ecc 100644 --- a/src/components/MarketAssetTable/index.tsx +++ b/src/components/Table/index.tsx @@ -3,9 +3,9 @@ import { flexRender, getCoreRowModel, getSortedRowModel, - Row, SortingState, - Table, + Row as TanstackRow, + Table as TanstackTable, useReactTable, } from '@tanstack/react-table' import classNames from 'classnames' @@ -13,23 +13,23 @@ import React from 'react' import Card from 'components/Card' import { SortAsc, SortDesc, SortNone } from 'components/Icons' +import Row from 'components/Table/Row' import Text from 'components/Text' -interface Props { +interface Props { title: string - data: TData[] - columns: ColumnDef[] - sorting?: SortingState - rowRenderer: (row: Row, table: Table) => JSX.Element + columns: ColumnDef[] + data: T[] + initialSorting: SortingState + renderExpanded?: (row: TanstackRow, table: TanstackTable) => JSX.Element } -function AssetListTable(props: Props) { - const { title, data, columns } = props - const [sorting, setSorting] = React.useState(props.sorting ?? []) +export default function Table(props: Props) { + const [sorting, setSorting] = React.useState(props.initialSorting) const table = useReactTable({ - data, - columns, + data: props.data, + columns: props.columns, state: { sorting, }, @@ -38,14 +38,10 @@ function AssetListTable(props: Props) { getSortedRowModel: getSortedRowModel(), }) - const _rowRenderer = (row: Row) => props.rowRenderer(row, table) - - if (!data.length) return null - return ( - +
+
+ Details + {props.children} +
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
- + {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { @@ -56,21 +52,19 @@ function AssetListTable(props: Props) { className={classNames( 'px-4 py-3', header.column.getCanSort() && 'hover:cursor-pointer', - header.id === 'symbol' ? 'text-left' : 'text-right', - { - 'w-32': header.id === 'manage', - 'w-48': header.id === 'depositCap', - }, + header.id === 'symbol' || header.id === 'name' ? 'text-left' : 'text-right', )} >
- + {header.column.getCanSort() ? { asc: , @@ -93,10 +87,12 @@ function AssetListTable(props: Props) {
))} - {table.getRowModel().rows.map(_rowRenderer)} + + {table.getRowModel().rows.map((row) => ( + + ))} +
) } - -export default AssetListTable diff --git a/src/components/TableSkeleton.tsx b/src/components/TableSkeleton.tsx index 55a83b76..68b27e8f 100644 --- a/src/components/TableSkeleton.tsx +++ b/src/components/TableSkeleton.tsx @@ -1,5 +1,4 @@ import classNames from 'classnames' -import React from 'react' import { SortNone } from 'components/Icons' import Loading from 'components/Loading' @@ -13,7 +12,7 @@ interface Props { export default function TableSkeleton(props: Props) { return ( - + {props.labels.map((label, index) => { return ( @@ -58,7 +57,7 @@ export default function TableSkeleton(props: Props) {
{ setHasAgreedToTerms(true) diff --git a/src/components/Toaster/index.tsx b/src/components/Toaster/index.tsx index 3383e9a5..2b3ecfd6 100644 --- a/src/components/Toaster/index.tsx +++ b/src/components/Toaster/index.tsx @@ -10,7 +10,7 @@ import Text from 'components/Text' import { TextLink } from 'components/TextLink' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' import { EXPLORER_NAME, EXPLORER_TX_URL } from 'constants/explorer' -import { REDUCE_MOTION_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useLocalStorage from 'hooks/useLocalStorage' import useTransactionStore from 'hooks/useTransactionStore' import useStore from 'store' @@ -52,7 +52,10 @@ export function generateToastContent(content: ToastSuccess['content']): ReactNod } export default function Toaster() { - const [reduceMotion] = useLocalStorage(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion) + const [reduceMotion] = useLocalStorage( + LocalStorageKeys.REDUCE_MOTION, + DEFAULT_SETTINGS.reduceMotion, + ) const toast = useStore((s) => s.toast) const { addTransaction } = useTransactionStore() @@ -209,7 +212,7 @@ export default function Toaster() { closeOnClick={false} transition={reduceMotion ? undefined : Slide} bodyClassName='p-0 m-0 -z-1' - className='mt-[73px]' + className='mt-[81px] p-0' /> ) } diff --git a/src/components/Tooltip/index.tsx b/src/components/Tooltip/index.tsx index 7e3cedce..c80aed2c 100644 --- a/src/components/Tooltip/index.tsx +++ b/src/components/Tooltip/index.tsx @@ -5,7 +5,7 @@ import { ReactNode } from 'react' import { Questionmark } from 'components/Icons' import TooltipContent from 'components/Tooltip/TooltipContent' import { DEFAULT_SETTINGS } from 'constants/defaultSettings' -import { REDUCE_MOTION_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import useLocalStorage from 'hooks/useLocalStorage' interface Props { @@ -19,7 +19,10 @@ interface Props { } export const Tooltip = (props: Props) => { - const [reduceMotion] = useLocalStorage(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion) + const [reduceMotion] = useLocalStorage( + LocalStorageKeys.REDUCE_MOTION, + DEFAULT_SETTINGS.reduceMotion, + ) const isInWalletAssetModal = document.getElementById('wallet-assets-modal') const isInModal = document.getElementById('modal') diff --git a/src/components/Trade/AccountDetailsCard.tsx b/src/components/Trade/AccountDetailsCard.tsx index e3ca51a8..8a6d6831 100644 --- a/src/components/Trade/AccountDetailsCard.tsx +++ b/src/components/Trade/AccountDetailsCard.tsx @@ -8,14 +8,10 @@ import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableDa export default function AccountDetailsCard() { const account = useCurrentAccount() - const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } = - useBorrowMarketAssetsTableData() + const { data } = useBorrowMarketAssetsTableData(false) + const borrowAssetsData = useMemo(() => data?.allAssets || [], [data]) const { availableAssets: lendingAvailableAssets, accountLentAssets } = useLendingMarketAssetsTableData() - const borrowAssetsData = useMemo( - () => [...borrowAvailableAssets, ...accountBorrowedAssets], - [borrowAvailableAssets, accountBorrowedAssets], - ) const lendingAssetsData = useMemo( () => [...lendingAvailableAssets, ...accountLentAssets], [lendingAvailableAssets, accountLentAssets], diff --git a/src/components/Trade/TradeChart/TVChartContainer.tsx b/src/components/Trade/TradeChart/TVChartContainer.tsx index 877b1669..f6520ce3 100644 --- a/src/components/Trade/TradeChart/TVChartContainer.tsx +++ b/src/components/Trade/TradeChart/TVChartContainer.tsx @@ -24,7 +24,7 @@ export const TVChartContainer = (props: Props) => { const chartContainerRef = useRef() as React.MutableRefObject const widgetRef = useRef() const defaultSymbol = useRef( - `${props.buyAsset.mainnetDenom}${PAIR_SEPARATOR}${props.sellAsset.mainnetDenom}`, + `${props.sellAsset.mainnetDenom}${PAIR_SEPARATOR}${props.buyAsset.mainnetDenom}`, ) const baseCurrency = useStore((s) => s.baseCurrency) const dataFeed = useMemo( @@ -66,13 +66,13 @@ export const TVChartContainer = (props: Props) => { 'mainSeriesProperties.candleStyle.wickUpColor': '#3DAE9A', 'mainSeriesProperties.candleStyle.wickDownColor': '#AE3D3D', 'mainSeriesProperties.candleStyle.barColorsOnPrevClose': false, - 'scalesProperties.textColor': 'rgba(255, 255, 255, 0.3)', - 'paneProperties.legendProperties.showSeriesTitle': false, + 'scalesProperties.textColor': 'rgba(255, 255, 255, 0.7)', + 'paneProperties.legendProperties.showSeriesTitle': true, 'paneProperties.legendProperties.showVolume': false, 'paneProperties.legendProperties.showStudyValues': false, 'paneProperties.legendProperties.showStudyTitles': false, 'scalesProperties.axisHighlightColor': '#381730', - 'linetooltrendline.color': 'rgba( 21, 153, 128, 1)', + 'linetooltrendline.color': '#3DAE9A', 'linetooltrendline.linewidth': 10, }, overrides, diff --git a/src/components/Trade/TradeModule/AssetSelector/AssetItem.tsx b/src/components/Trade/TradeModule/AssetSelector/AssetItem.tsx index 3be3405a..a1723efe 100644 --- a/src/components/Trade/TradeModule/AssetSelector/AssetItem.tsx +++ b/src/components/Trade/TradeModule/AssetSelector/AssetItem.tsx @@ -3,7 +3,7 @@ import DisplayCurrency from 'components/DisplayCurrency' import { FormattedNumber } from 'components/FormattedNumber' import { StarFilled, StarOutlined } from 'components/Icons' import Text from 'components/Text' -import { FAVORITE_ASSETS_KEY } from 'constants/localStore' +import { LocalStorageKeys } from 'constants/localStorageKeys' import { BN_ONE, BN_ZERO, MAX_AMOUNT_DECIMALS, MIN_AMOUNT } from 'constants/math' import useLocalStorage from 'hooks/useLocalStorage' import { BNCoin } from 'types/classes/BNCoin' @@ -20,7 +20,7 @@ interface Props { export default function AssetItem(props: Props) { const asset = props.asset const [favoriteAssetsDenoms, setFavoriteAssetsDenoms] = useLocalStorage( - FAVORITE_ASSETS_KEY, + LocalStorageKeys.FAVORITE_ASSETS, [], ) const amount = demagnify(props.balances.find(byDenom(asset.denom))?.amount ?? BN_ZERO, asset) diff --git a/src/components/Trade/TradeModule/AssetSelector/index.tsx b/src/components/Trade/TradeModule/AssetSelector/index.tsx index 4efd562c..ec2ebcc9 100644 --- a/src/components/Trade/TradeModule/AssetSelector/index.tsx +++ b/src/components/Trade/TradeModule/AssetSelector/index.tsx @@ -1,54 +1,51 @@ -import { useCallback, useEffect } from 'react' +import { useCallback } from 'react' import { SwapIcon } from 'components/Icons' import Text from 'components/Text' import AssetButton from 'components/Trade/TradeModule/AssetSelector/AssetButton' import AssetOverlay from 'components/Trade/TradeModule/AssetSelector/AssetOverlay' +import { DEFAULT_SETTINGS } from 'constants/defaultSettings' +import { LocalStorageKeys } from 'constants/localStorageKeys' +import useLocalStorage from 'hooks/useLocalStorage' import useStore from 'store' interface Props { buyAsset: Asset sellAsset: Asset - onChangeBuyAsset: (asset: Asset) => void - onChangeSellAsset: (asset: Asset) => void } export default function AssetSelector(props: Props) { - const { buyAsset, sellAsset, onChangeBuyAsset, onChangeSellAsset } = props + const [tradingPair, setTradingPair] = useLocalStorage( + LocalStorageKeys.TRADING_PAIR, + DEFAULT_SETTINGS.tradingPair, + ) + const { buyAsset, sellAsset } = props const assetOverlayState = useStore((s) => s.assetOverlayState) const handleSwapAssets = useCallback(() => { - onChangeBuyAsset(sellAsset) - onChangeSellAsset(buyAsset) - }, [onChangeBuyAsset, onChangeSellAsset, sellAsset, buyAsset]) + setTradingPair({ buy: sellAsset.denom, sell: buyAsset.denom }) + }, [setTradingPair, sellAsset, buyAsset]) const handleChangeBuyAsset = useCallback( (asset: Asset) => { - onChangeBuyAsset(asset) + setTradingPair({ buy: asset.denom, sell: sellAsset.denom }) useStore.setState({ assetOverlayState: 'sell' }) }, - [onChangeBuyAsset], + [setTradingPair, sellAsset], ) const handleChangeSellAsset = useCallback( (asset: Asset) => { - onChangeSellAsset(asset) + setTradingPair({ buy: buyAsset.denom, sell: asset.denom }) useStore.setState({ assetOverlayState: 'closed' }) }, - [onChangeSellAsset], + [setTradingPair, buyAsset], ) const handleChangeState = useCallback((state: OverlayState) => { useStore.setState({ assetOverlayState: state }) }, []) - useEffect(() => { - if (assetOverlayState === 'closed') { - onChangeBuyAsset(buyAsset) - onChangeSellAsset(sellAsset) - } - }, [onChangeBuyAsset, onChangeSellAsset, assetOverlayState, buyAsset, sellAsset]) - return (
Buy diff --git a/src/components/Trade/TradeModule/SwapForm/AssetAmountInput.tsx b/src/components/Trade/TradeModule/SwapForm/AssetAmountInput.tsx index 4a9e441c..87a082c9 100644 --- a/src/components/Trade/TradeModule/SwapForm/AssetAmountInput.tsx +++ b/src/components/Trade/TradeModule/SwapForm/AssetAmountInput.tsx @@ -45,7 +45,7 @@ export default function AssetAmountInput(props: Props) {