v2.0.1 (#579)
* 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] <support@github.com> 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] <support@github.com> 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] <support@github.com> 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 <linkielink.dev@gmail.com> * 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] <support@github.com> 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] <support@github.com> 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] <support@github.com> 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] <support@github.com> 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] <support@github.com> 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] <support@github.com> 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] <support@github.com> 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>
This commit is contained in:
parent
7272b1eee0
commit
3dc1752ae2
65
.env.example
65
.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
|
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
@ -1,3 +1,4 @@
|
||||
src/utils/charting_library
|
||||
src/utils/datafeeds
|
||||
src/types/generated
|
||||
src/types/generated
|
||||
.husky
|
@ -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('<CircularProgress />', () => {
|
||||
afterAll(() => {
|
||||
localStorage.removeItem(REDUCE_MOTION_KEY)
|
||||
localStorage.removeItem(LocalStorageKeys.REDUCE_MOTION)
|
||||
})
|
||||
|
||||
it('should render', () => {
|
||||
@ -15,7 +15,7 @@ describe('<CircularProgress />', () => {
|
||||
})
|
||||
|
||||
it('should render `...` when animations disabled', () => {
|
||||
localStorage.setItem(REDUCE_MOTION_KEY, 'true')
|
||||
localStorage.setItem(LocalStorageKeys.REDUCE_MOTION, 'true')
|
||||
|
||||
const { getByText } = render(<CircularProgress />)
|
||||
const threeDots = getByText('...')
|
||||
@ -24,7 +24,7 @@ describe('<CircularProgress />', () => {
|
||||
})
|
||||
|
||||
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(<CircularProgress />)
|
||||
const progressWithAnimations = container.querySelector('.animate-progress')
|
||||
|
@ -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('<LendingDetails />', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('hooks/usePrices')
|
||||
})
|
||||
|
||||
it('should render', () => {
|
||||
const { container } = render(<MarketDetails type='lend' data={data} />)
|
||||
expect(container).toBeInTheDocument()
|
||||
})
|
||||
})
|
@ -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,
|
||||
|
@ -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(() => <div>Display currency</div>)
|
||||
|
||||
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('<VaultBorrowings />', () => {
|
||||
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(<VaultBorrowings {...defaultProps} />)
|
||||
expect(container).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render DisplayCurrency correctly', () => {
|
||||
expect(mockedDisplayCurrency).toHaveBeenCalledTimes(1)
|
||||
expect(mockedDisplayCurrency).toHaveBeenCalledWith(
|
||||
{ coin: new BNCoin({ denom: 'usd', amount: '0' }) },
|
||||
expect.anything(),
|
||||
)
|
||||
})
|
||||
})
|
@ -1,6 +1,5 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import { TooltipType } from 'components/Tooltip'
|
||||
import TooltipContent from 'components/Tooltip/TooltipContent'
|
||||
|
||||
describe('<Tooltip />', () => {
|
||||
|
27
package.json
27
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"
|
||||
|
BIN
public/images/bridges/squid.png
Normal file
BIN
public/images/bridges/squid.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
@ -1,25 +0,0 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 120 120" style="enable-background:new 0 0 120 120;" xml:space="preserve">
|
||||
<path fill="#2775CA" d="M60,120c33.2,0,60-26.8,60-60S93.2,0,60,0S0,26.8,0,60S26.8,120,60,120z"/>
|
||||
<path fill="#FFFFFF" d="M76.5,69.5c0-8.8-5.3-11.8-15.7-13c-7.5-1-9-3-9-6.5c0-3.5,2.5-5.8,7.5-5.8c4.5,0,7,1.5,8.2,5.3
|
||||
c0.2,0.8,1,1.3,1.7,1.3h4c1,0,1.7-0.8,1.7-1.7v-0.2c-1-5.5-5.5-9.7-11.2-10.3v-6c0-1-0.8-1.7-2-2H58c-1,0-1.7,0.7-2,2v5.8
|
||||
c-7.5,1-12.2,6-12.2,12.2c0,8.2,5,11.5,15.5,12.7c7,1.3,9.2,2.8,9.2,6.7c0,4-3.5,6.7-8.2,6.7c-6.5,0-8.8-2.8-9.5-6.5
|
||||
c-0.2-1-1-1.5-1.7-1.5h-4.3c-1,0-1.7,0.8-1.7,1.7v0.2c1,6.2,5,10.7,13.3,12v6c0,1,0.8,1.7,2,2H62c1,0,1.7-0.8,2-2v-6
|
||||
C71.5,81.5,76.5,76.2,76.5,69.5L76.5,69.5z"/>
|
||||
<path fill="#FFFFFF" d="M47.3,95.8c-19.5-7-29.5-28.7-22.3-48c3.8-10.5,12-18.5,22.3-22.3c1-0.5,1.5-1.3,1.5-2.5v-3.5
|
||||
c0-1-0.5-1.7-1.5-2c-0.2,0-0.8,0-1,0.2C22.5,25.3,9.5,50.5,17,74.3c4.5,14,15.2,24.7,29.2,29.2c1,0.5,2,0,2.3-1
|
||||
c0.2-0.2,0.2-0.5,0.2-1V98C48.7,97.2,48,96.2,47.3,95.8L47.3,95.8z M73.8,17.8c-1-0.5-2,0-2.3,1c-0.2,0.2-0.2,0.5-0.2,1v3.5
|
||||
c0,1,0.8,2,1.5,2.5c19.5,7,29.5,28.7,22.3,48C91.2,84.3,83,92.3,72.7,96c-1,0.5-1.5,1.3-1.5,2.5v3.5c0,1,0.5,1.7,1.5,2
|
||||
c0.2,0,0.8,0,1-0.2C97.5,96.3,110.5,71,103,47.3C98.5,33,87.5,22.3,73.8,17.8L73.8,17.8z"/>
|
||||
<path d="M100,120c11,0,20-9,20-20s-9-20-20-20s-20,9-20,20S89,120,100,120z"/>
|
||||
<path fill="#FFFFFF" d="M109.3,87c-2,0-3.5,1.6-3.5,3.6c0,0.5,0,1.1,0,1.6c-0.3-0.2-0.5-0.5-0.6-0.7
|
||||
c-2.3-3.5-6.5-5.2-10.5-4.2c-4.3,1.1-7.4,4.5-7.6,8.8c-0.2,4.5-0.1,9.1,0,13.6c0,1.9,1.8,3.4,3.7,3.3c1.9-0.1,3.4-1.6,3.5-3.5
|
||||
c0-0.6,0-1.1,0-1.7c0,0,0.1,0,0.1,0c0.2,0.3,0.3,0.5,0.5,0.8c2.3,3.6,6.8,5.2,10.9,4.1c4.1-1.1,7.1-4.9,7.2-9c0.1-4.3,0-8.7,0-13
|
||||
C113,88.6,111.3,87,109.3,87z M93.4,95.9c-0.1,0.4-0.1,0.9-0.1,1.3c0,4,0,7.9,0,11.9c0,1.6-1,2.8-2.5,2.8c-1.6,0.1-2.7-1-2.7-2.7
|
||||
c0-2.1,0-4.2,0-6.3c0,0,0,0,0.1,0c0-2.3-0.1-4.7,0-7c0.2-4,3.9-7.5,7.9-7.9c5.4-0.5,9.9,3.7,9.8,9.2c-0.1,2,0,3.9,0,5.9
|
||||
c0,1.3-1,2.4-2.3,2.6c-1.2,0.2-2.4-0.6-2.8-1.8c-0.1-0.3-0.1-0.6-0.1-1c0-2,0-4.1,0-6.1c0-1.8-1.4-3.3-3-3.5
|
||||
C95.6,93.1,93.9,94.1,93.4,95.9z M111.9,104c-0.1,4-3.5,7.4-7.5,7.9c-4.7,0.6-8.9-2.3-9.9-6.9c-0.2-1.1-0.2-2.2-0.2-3.3
|
||||
c0-1.6,0-3.1,0-4.7c0-1.6,1.1-2.8,2.6-2.8c1.5,0,2.6,1.2,2.6,2.8c0,2,0,4,0,6.1c0,1.7,1,3,2.7,3.5c1.5,0.4,3.2-0.2,4-1.6
|
||||
c0.3-0.6,0.5-1.3,0.5-1.9c0.1-2,0-4,0-6c0-2.1,0-4.2,0-6.3c0-1.1,0.5-2,1.5-2.5c1-0.4,1.9-0.3,2.7,0.3c0.7,0.5,1,1.3,1,2.1
|
||||
C112,95.1,112.1,99.5,111.9,104z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
28
public/images/tokens/usdc.svg
Normal file
28
public/images/tokens/usdc.svg
Normal file
@ -0,0 +1,28 @@
|
||||
<svg
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 120 120"
|
||||
style="enable-background: new 0 0 120 120"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<path fill="#2775CA" d="M60,120c33.2,0,60-26.8,60-60S93.2,0,60,0S0,26.8,0,60S26.8,120,60,120z" />
|
||||
<path
|
||||
fill="#FFFFFF"
|
||||
d="M76.5,69.5c0-8.8-5.3-11.8-15.7-13c-7.5-1-9-3-9-6.5c0-3.5,2.5-5.8,7.5-5.8c4.5,0,7,1.5,8.2,5.3
|
||||
c0.2,0.8,1,1.3,1.7,1.3h4c1,0,1.7-0.8,1.7-1.7v-0.2c-1-5.5-5.5-9.7-11.2-10.3v-6c0-1-0.8-1.7-2-2H58c-1,0-1.7,0.7-2,2v5.8
|
||||
c-7.5,1-12.2,6-12.2,12.2c0,8.2,5,11.5,15.5,12.7c7,1.3,9.2,2.8,9.2,6.7c0,4-3.5,6.7-8.2,6.7c-6.5,0-8.8-2.8-9.5-6.5
|
||||
c-0.2-1-1-1.5-1.7-1.5h-4.3c-1,0-1.7,0.8-1.7,1.7v0.2c1,6.2,5,10.7,13.3,12v6c0,1,0.8,1.7,2,2H62c1,0,1.7-0.8,2-2v-6
|
||||
C71.5,81.5,76.5,76.2,76.5,69.5L76.5,69.5z"
|
||||
/>
|
||||
<path
|
||||
fill="#FFFFFF"
|
||||
d="M47.3,95.8c-19.5-7-29.5-28.7-22.3-48c3.8-10.5,12-18.5,22.3-22.3c1-0.5,1.5-1.3,1.5-2.5v-3.5
|
||||
c0-1-0.5-1.7-1.5-2c-0.2,0-0.8,0-1,0.2C22.5,25.3,9.5,50.5,17,74.3c4.5,14,15.2,24.7,29.2,29.2c1,0.5,2,0,2.3-1
|
||||
c0.2-0.2,0.2-0.5,0.2-1V98C48.7,97.2,48,96.2,47.3,95.8L47.3,95.8z M73.8,17.8c-1-0.5-2,0-2.3,1c-0.2,0.2-0.2,0.5-0.2,1v3.5
|
||||
c0,1,0.8,2,1.5,2.5c19.5,7,29.5,28.7,22.3,48C91.2,84.3,83,92.3,72.7,96c-1,0.5-1.5,1.3-1.5,2.5v3.5c0,1,0.5,1.7,1.5,2
|
||||
c0.2,0,0.8,0,1-0.2C97.5,96.3,110.5,71,103,47.3C98.5,33,87.5,22.3,73.8,17.8L73.8,17.8z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
28
src/api/hls/getHLSStakingAssets.ts
Normal file
28
src/api/hls/getHLSStakingAssets.ts
Normal file
@ -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
|
||||
})
|
||||
})
|
||||
}
|
@ -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<Vault[]> {
|
||||
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<Vault[]> {
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
@ -20,14 +20,13 @@ export default function AccordionContent(props: Props) {
|
||||
const { title, renderContent, isOpen, renderSubTitle, toggleOpen } = props.item
|
||||
|
||||
return (
|
||||
<div key={title} className='group border-b-white/10 [&:not(:last-child)]:border-b'>
|
||||
<div key={title} className='border-b border-collapse group border-white/20 last:border-b-0'>
|
||||
<div
|
||||
onClick={() => 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',
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
|
@ -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<SortingState>([])
|
||||
const updatedAccount = useStore((s) => s.updatedAccount)
|
||||
const accountBalanceData = useAccountBalanceData({
|
||||
@ -65,6 +64,7 @@ export default function Index(props: Props) {
|
||||
return (
|
||||
<Text size='xs'>
|
||||
{row.original.symbol}
|
||||
{row.original.type === 'borrowing' && <span className='ml-1 text-loss'>(debt)</span>}
|
||||
{row.original.type === 'lending' && <span className='ml-1 text-profit'>(lent)</span>}
|
||||
{row.original.type === 'vault' && <span className='ml-1 text-profit'>(farm)</span>}
|
||||
</Text>
|
||||
@ -186,7 +186,7 @@ export default function Index(props: Props) {
|
||||
|
||||
return (
|
||||
<table className='w-full'>
|
||||
<thead className='border-b border-white/5'>
|
||||
<thead className='border-b border-white/10'>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
|
@ -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],
|
||||
|
@ -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<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
||||
const [reduceMotion] = useLocalStorage<boolean>(
|
||||
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],
|
||||
|
@ -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<boolean>(
|
||||
LEND_ASSETS_KEY,
|
||||
LocalStorageKeys.LEND_ASSETS,
|
||||
DEFAULT_SETTINGS.lendAssets,
|
||||
)
|
||||
const [fundingAssets, setFundingAssets] = useState<BNCoin[]>([])
|
||||
|
@ -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],
|
||||
|
@ -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<boolean>(LEND_ASSETS_KEY, DEFAULT_SETTINGS.lendAssets)
|
||||
const [lendAssets] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.LEND_ASSETS,
|
||||
DEFAULT_SETTINGS.lendAssets,
|
||||
)
|
||||
const { enableAutoLendAccountId } = useAutoLend()
|
||||
|
||||
const hasCreditAccounts = !!accountIds?.length
|
||||
|
@ -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<boolean[]>(
|
||||
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 (
|
||||
<div className='h-[546px] min-w-92.5 basis-92.5 max-w-screen overflow-y-scroll scrollbar-hide'>
|
||||
@ -110,8 +118,8 @@ export default function AccountSummary(props: Props) {
|
||||
title: `Credit Account ${props.account.id} Composition`,
|
||||
renderContent: () =>
|
||||
props.account ? <AccountComposition account={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: () => <></>,
|
||||
},
|
||||
]}
|
||||
|
@ -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<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
||||
const [reduceMotion] = useLocalStorage<boolean>(
|
||||
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) {
|
||||
<rect x='46' fill='#FFFFFF' width='47' height='4' />
|
||||
<path fill='#FFFFFF' d='M95.5,0H182c1.1,0,2,0.9,2,2s-0.9,2-2,2H95.5V0z' />
|
||||
</mask>
|
||||
<mask id='backgroundHealthBarMask'>
|
||||
<rect x='62.1' fill='white' width='2.4' height='4' />
|
||||
<rect x='48' fill='white' width='2' height='4' />
|
||||
<rect x='57.3' fill='white' width='2.4' height='4' />
|
||||
<rect x='52.5' fill='white' width='2.4' height='4' />
|
||||
<rect x='66.9' fill='white' width='2.4' height='4' />
|
||||
<rect x='86.1' fill='white' width='2.4' height='4' />
|
||||
<rect x='81.3' fill='white' width='2.4' height='4' />
|
||||
<rect x='71.7' fill='white' width='2.4' height='4' />
|
||||
<rect x='90.9' fill='white' width='2.1' height='4' />
|
||||
<rect x='76.5' fill='white' width='2.4' height='4' />
|
||||
<rect x='119.2' fill='white' width='2.4' height='4' />
|
||||
<rect x='143.2' fill='white' width='2.4' height='4' />
|
||||
<rect x='138.4' fill='white' width='2.4' height='4' />
|
||||
<rect x='133.6' fill='white' width='2.4' height='4' />
|
||||
<rect x='124' fill='white' width='2.4' height='4' />
|
||||
<rect x='100' fill='white' width='2.4' height='4' />
|
||||
<rect x='104.8' fill='white' width='2.4' height='4' />
|
||||
<rect x='109.6' fill='white' width='2.4' height='4' />
|
||||
<rect x='114.4' fill='white' width='2.4' height='4' />
|
||||
<rect x='128.8' fill='white' width='2.4' height='4' />
|
||||
<rect x='172' fill='white' width='2.4' height='4' />
|
||||
<rect x='176.8' fill='white' width='2.4' height='4' />
|
||||
<rect x='95.5' fill='white' width='2.1' height='4' />
|
||||
<path fill='white' d='M182,0h-0.4v4h0.4c1.1,0,2-0.9,2-2S183.1,0,182,0z' />
|
||||
<rect x='162.4' fill='white' width='2.4' height='4' />
|
||||
<rect x='152.8' fill='white' width='2.4' height='4' />
|
||||
<rect x='157.6' fill='white' width='2.4' height='4' />
|
||||
<rect x='167.2' fill='white' width='2.4' height='4' />
|
||||
<rect x='148' fill='white' width='2.4' height='4' />
|
||||
<rect x='17.2' fill='white' width='2.4' height='4' />
|
||||
<rect x='12.4' fill='white' width='2.4' height='4' />
|
||||
<rect x='3.1' fill='white' width='2.1' height='4' />
|
||||
<rect x='7.6' fill='white' width='2.4' height='4' />
|
||||
<rect x='22' fill='white' width='2.4' height='4' />
|
||||
<rect x='41.2' fill='white' width='2.4' height='4' />
|
||||
<rect x='36.4' fill='white' width='2.4' height='4' />
|
||||
<rect x='26.8' fill='white' width='2.4' height='4' />
|
||||
<path fill='white' d='M0.7,0.5C0.3,0.9,0,1.4,0,2s0.3,1.1,0.7,1.5V0.5z' />
|
||||
<rect x='31.6' fill='white' width='2.4' height='4' />
|
||||
</mask>
|
||||
<rect className='fill-white/10' width='184' height='4' mask='url(#healthBarMask)' />
|
||||
<rect
|
||||
className={classNames(backgroundColor, !reduceMotion && 'transition-all duration-500')}
|
||||
width={isUpdated && isIncrease ? updatedWidth : width}
|
||||
height='4'
|
||||
mask='url(#healthBarMask)'
|
||||
mask={isUpdated ? 'url(#backgroundHealthBarMask)' : 'url(#healthBarMask)'}
|
||||
/>
|
||||
{isUpdated && (
|
||||
<rect
|
||||
|
@ -12,13 +12,16 @@ import {
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import Text from 'components/Text'
|
||||
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 useLocalStorage from 'hooks/useLocalStorage'
|
||||
import { formatValue } from 'utils/formatters'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
|
||||
export const RiskChart = ({ data }: RiskChartProps) => {
|
||||
const [reduceMotion] = useLocalStorage<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
||||
const [reduceMotion] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.REDUCE_MOTION,
|
||||
DEFAULT_SETTINGS.reduceMotion,
|
||||
)
|
||||
const currentRisk = BN_ZERO
|
||||
|
||||
return (
|
||||
|
@ -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 (
|
||||
<div className='flex flex-col gap-[0.5] text-xs'>
|
||||
<FormattedNumber
|
||||
amount={amount < MIN_AMOUNT ? MIN_AMOUNT : amount}
|
||||
smallerThanThreshold={amount < MIN_AMOUNT}
|
||||
amount={isZero ? 0 : displayAmount}
|
||||
smallerThanThreshold={!isZero && isBelowMinAmount}
|
||||
options={{ abbreviated: true, maxDecimals: MAX_AMOUNT_DECIMALS }}
|
||||
animate
|
||||
/>
|
||||
|
@ -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<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
||||
const [reduceMotion] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.REDUCE_MOTION,
|
||||
DEFAULT_SETTINGS.reduceMotion,
|
||||
)
|
||||
|
||||
return (
|
||||
<div className='background pointer-events-none fixed inset-0 h-full w-full overflow-hidden bg-body'>
|
||||
|
@ -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<BorrowMarketTableData>, table: Table<BorrowMarketTableData>) => {
|
||||
return (
|
||||
<MarketAssetTableRow
|
||||
key={`borrow-asset-${row.id}`}
|
||||
isExpanded={row.getIsExpanded()}
|
||||
resetExpanded={table.resetExpanded}
|
||||
rowData={row}
|
||||
expandedActionButtons={<BorrowActionButtons data={row.original} />}
|
||||
expandedDetails={<MarketDetails data={row.original} type='borrow' />}
|
||||
/>
|
||||
)
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
const columns = useMemo<ColumnDef<BorrowMarketTableData>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: 'asset.name',
|
||||
header: 'Asset',
|
||||
id: 'symbol',
|
||||
cell: ({ row }) => {
|
||||
const asset = row.original.asset
|
||||
|
||||
return (
|
||||
<div className='flex items-center flex-1 gap-3'>
|
||||
<AssetImage asset={asset} size={32} />
|
||||
<TitleAndSubCell
|
||||
title={asset.symbol}
|
||||
sub={asset.name}
|
||||
className='text-left min-w-15'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
...(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 <AmountAndValue asset={asset} amount={borrowAsset?.debt ?? BN_ZERO} />
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
accessorKey: 'borrowRate',
|
||||
header: 'Borrow Rate',
|
||||
cell: ({ row }) => {
|
||||
if (row.original.borrowRate === null) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
return (
|
||||
<FormattedNumber
|
||||
className='justify-end text-xs'
|
||||
amount={row.original.borrowRate * 100}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: '%' }}
|
||||
animate
|
||||
/>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
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 <Loading />
|
||||
}
|
||||
|
||||
return <AmountAndValue asset={asset} amount={liquidity.amount ?? BN_ZERO} />
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'manage',
|
||||
enableSorting: false,
|
||||
header: 'Manage',
|
||||
cell: ({ row }) => (
|
||||
<div className='flex items-center justify-end'>
|
||||
<div className='w-4'>{row.getIsExpanded() ? <ChevronUp /> : <ChevronDown />}</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
],
|
||||
[shouldShowAccountBorrowed, marketAssets],
|
||||
)
|
||||
|
||||
return <AssetListTable title={title} rowRenderer={rowRenderer} columns={columns} data={data} />
|
||||
}
|
37
src/components/Borrow/Borrowings.tsx
Normal file
37
src/components/Borrow/Borrowings.tsx
Normal file
@ -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 <Fallback />
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<DepositedBorrowingsTable data={data.accountBorrowedAssets} isLoading={false} />
|
||||
<AvailableBorrowingsTable data={data.availableAssets} isLoading={false} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
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 <AvailableBorrowingsTable data={data} isLoading />
|
||||
}
|
46
src/components/Borrow/Table/AvailableBorrowingsTable.tsx
Normal file
46
src/components/Borrow/Table/AvailableBorrowingsTable.tsx
Normal file
@ -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<BorrowMarketTableData>, _: TanstackTable<BorrowMarketTableData>) => {
|
||||
const currentRow = row as Row<BorrowMarketTableData>
|
||||
return (
|
||||
<>
|
||||
<ActionButtonRow row={currentRow}>
|
||||
<BorrowActionButtons data={row.original} />
|
||||
</ActionButtonRow>
|
||||
<MarketDetails row={currentRow} type='borrow' />
|
||||
</>
|
||||
)
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
if (!props.data.length) return null
|
||||
|
||||
return (
|
||||
<Table
|
||||
title='Available to Borrow'
|
||||
columns={columns}
|
||||
data={props.data}
|
||||
initialSorting={[{ id: NAME_META.id, desc: false }]}
|
||||
renderExpanded={renderExpanded}
|
||||
/>
|
||||
)
|
||||
}
|
25
src/components/Borrow/Table/Columns/BorrowRate.tsx
Normal file
25
src/components/Borrow/Table/Columns/BorrowRate.tsx
Normal file
@ -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 <Loading />
|
||||
}
|
||||
|
||||
return (
|
||||
<FormattedNumber
|
||||
className='justify-end text-xs'
|
||||
amount={props.borrowRate * 100}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: '%' }}
|
||||
animate
|
||||
/>
|
||||
)
|
||||
}
|
42
src/components/Borrow/Table/Columns/Debt.tsx
Normal file
42
src/components/Borrow/Table/Columns/Debt.tsx
Normal file
@ -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<BorrowMarketTableData>,
|
||||
b: Row<BorrowMarketTableData>,
|
||||
): 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 <AmountAndValue asset={asset} amount={props.data?.debt ?? BN_ZERO} />
|
||||
}
|
44
src/components/Borrow/Table/Columns/Liquidity.tsx
Normal file
44
src/components/Borrow/Table/Columns/Liquidity.tsx
Normal file
@ -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<BorrowMarketTableData>,
|
||||
b: Row<BorrowMarketTableData>,
|
||||
): 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 <Loading />
|
||||
}
|
||||
|
||||
return <AmountAndValue asset={asset} amount={liquidity.amount ?? BN_ZERO} />
|
||||
}
|
17
src/components/Borrow/Table/Columns/Manage.tsx
Normal file
17
src/components/Borrow/Table/Columns/Manage.tsx
Normal file
@ -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 (
|
||||
<div className='flex items-center justify-end'>
|
||||
<div className='w-4'>{props.isExpanded ? <ChevronUp /> : <ChevronDown />}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
18
src/components/Borrow/Table/Columns/Name.tsx
Normal file
18
src/components/Borrow/Table/Columns/Name.tsx
Normal file
@ -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 (
|
||||
<div className='flex items-center flex-1 gap-3'>
|
||||
<AssetImage asset={asset} size={32} />
|
||||
<TitleAndSubCell title={asset.symbol} sub={asset.name} className='text-left min-w-15' />
|
||||
</div>
|
||||
)
|
||||
}
|
34
src/components/Borrow/Table/Columns/useAvailableColumns.tsx
Normal file
34
src/components/Borrow/Table/Columns/useAvailableColumns.tsx
Normal file
@ -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<ColumnDef<BorrowMarketTableData>[]>(() => {
|
||||
return [
|
||||
{
|
||||
...NAME_META,
|
||||
cell: ({ row }) => <Name data={row.original} />,
|
||||
},
|
||||
{
|
||||
...BORROW_RATE_META,
|
||||
cell: ({ row }) => <BorrowRate borrowRate={row.original.borrowRate} />,
|
||||
},
|
||||
{
|
||||
...LIQUIDITY_META,
|
||||
cell: ({ row }) => <Liquidity data={row.original} />,
|
||||
sortingFn: liquiditySortingFn,
|
||||
},
|
||||
{
|
||||
...MANAGE_META,
|
||||
cell: ({ row }) => <Manage isExpanded={row.getIsExpanded()} />,
|
||||
},
|
||||
]
|
||||
}, [])
|
||||
}
|
40
src/components/Borrow/Table/Columns/useDepositedColumns.tsx
Normal file
40
src/components/Borrow/Table/Columns/useDepositedColumns.tsx
Normal file
@ -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<ColumnDef<BorrowMarketTableData>[]>(() => {
|
||||
return [
|
||||
{
|
||||
...NAME_META,
|
||||
cell: ({ row }) => <Name data={row.original} />,
|
||||
},
|
||||
{
|
||||
...DEBT_META,
|
||||
cell: ({ row }) => <Debt data={row.original} />,
|
||||
sortingFn: debtSortingFn,
|
||||
},
|
||||
{
|
||||
...BORROW_RATE_META,
|
||||
cell: ({ row }) => <BorrowRate borrowRate={row.original.borrowRate} />,
|
||||
},
|
||||
{
|
||||
...LIQUIDITY_META,
|
||||
cell: ({ row }) => <Liquidity data={row.original} />,
|
||||
sortingFn: liquiditySortingFn,
|
||||
},
|
||||
{
|
||||
...MANAGE_META,
|
||||
cell: ({ row }) => <Manage isExpanded={row.getIsExpanded()} />,
|
||||
},
|
||||
]
|
||||
}, [])
|
||||
}
|
42
src/components/Borrow/Table/DepositedBorrowingsTable.tsx
Normal file
42
src/components/Borrow/Table/DepositedBorrowingsTable.tsx
Normal file
@ -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<BorrowMarketTableData>) => {
|
||||
const currentRow = row as Row<BorrowMarketTableData>
|
||||
return (
|
||||
<>
|
||||
<ActionButtonRow row={currentRow}>
|
||||
<BorrowActionButtons data={row.original} />
|
||||
</ActionButtonRow>
|
||||
<MarketDetails row={row} type='borrow' />
|
||||
</>
|
||||
)
|
||||
}, [])
|
||||
|
||||
if (!props.data.length) return null
|
||||
|
||||
return (
|
||||
<Table
|
||||
title='Borrowed Assets'
|
||||
columns={columns}
|
||||
data={props.data}
|
||||
initialSorting={[{ id: NAME_META.id, desc: false }]}
|
||||
renderExpanded={renderExpanded}
|
||||
/>
|
||||
)
|
||||
}
|
@ -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<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
||||
const [reduceMotion] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.REDUCE_MOTION,
|
||||
DEFAULT_SETTINGS.reduceMotion,
|
||||
)
|
||||
const [percentOfMaxRound, setPercentOfMaxRound] = useState(0)
|
||||
const [percentOfMaxRange, setPercentOfMaxRange] = useState(0)
|
||||
const [limitPercentOfMax, setLimitPercentOfMax] = useState(0)
|
||||
|
@ -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<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
||||
const [reduceMotion] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.REDUCE_MOTION,
|
||||
DEFAULT_SETTINGS.reduceMotion,
|
||||
)
|
||||
const isDisabled = disabled || showProgressIndicator
|
||||
const shouldShowText = text && !children
|
||||
const shouldShowGlowElement = variant === 'solid' && !isDisabled && !reduceMotion
|
||||
|
@ -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<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
||||
const [reduceMotion] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.REDUCE_MOTION,
|
||||
DEFAULT_SETTINGS.reduceMotion,
|
||||
)
|
||||
const classes = classNames('inline-block relative', className)
|
||||
|
||||
if (reduceMotion)
|
||||
|
@ -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 (
|
||||
<button
|
||||
onClick={() => props.onChange(props.checked)}
|
||||
role='checkbox'
|
||||
aria-checked={props.checked}
|
||||
className={classNames(
|
||||
'h-5 w-5 rounded-sm p-0.5',
|
||||
props.checked && 'relative isolate overflow-hidden rounded-sm',
|
||||
props.checked &&
|
||||
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-sm before:p-[1px] before:border-glas',
|
||||
props.checked ? 'bg-white/20' : 'border border-white/60',
|
||||
<label className='flex gap-2 items-center cursor-pointer'>
|
||||
<input
|
||||
onChange={() => props.onChange(props.checked)}
|
||||
checked={props.checked}
|
||||
type='checkbox'
|
||||
className='opacity-0 absolute'
|
||||
/>
|
||||
<div
|
||||
className={classNames(
|
||||
'h-5 w-5 rounded-sm p-0.5',
|
||||
props.checked && 'relative isolate overflow-hidden rounded-sm',
|
||||
props.checked &&
|
||||
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-sm before:p-[1px] before:border-glas',
|
||||
props.checked ? 'bg-white/20' : 'border border-white/60',
|
||||
)}
|
||||
>
|
||||
{props.checked && <Check />}
|
||||
</div>
|
||||
{props.text && (
|
||||
<Text size='xs' className='text-white/60'>
|
||||
{props.text}
|
||||
</Text>
|
||||
)}
|
||||
>
|
||||
{props.checked && <Check />}
|
||||
</button>
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
@ -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<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
||||
const [reduceMotion] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.REDUCE_MOTION,
|
||||
DEFAULT_SETTINGS.reduceMotion,
|
||||
)
|
||||
const borderWidth = `${size / 10}px`
|
||||
const borderColor = `${color} transparent transparent transparent`
|
||||
const loaderClasses = classNames('inline-block relative', className)
|
||||
|
@ -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<string>(
|
||||
DISPLAY_CURRENCY_KEY,
|
||||
LocalStorageKeys.DISPLAY_CURRENCY,
|
||||
DEFAULT_SETTINGS.displayCurrency,
|
||||
)
|
||||
const { data: prices } = usePrices()
|
||||
|
22
src/components/Earn/Farm/Table/AvailableVaultsTable.tsx
Normal file
22
src/components/Earn/Farm/Table/AvailableVaultsTable.tsx
Normal file
@ -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 (
|
||||
<Table
|
||||
title='Available vaults'
|
||||
columns={columns}
|
||||
data={props.data}
|
||||
initialSorting={[{ id: 'name', desc: true }]}
|
||||
/>
|
||||
)
|
||||
}
|
25
src/components/Earn/Farm/Table/Columns/Apy.tsx
Normal file
25
src/components/Earn/Farm/Table/Columns/Apy.tsx
Normal file
@ -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 <Loading />
|
||||
|
||||
return (
|
||||
<FormattedNumber
|
||||
amount={vault.apy ?? 0}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: '%' }}
|
||||
className='text-xs'
|
||||
animate
|
||||
/>
|
||||
)
|
||||
}
|
32
src/components/Earn/Farm/Table/Columns/Deposit.tsx
Normal file
32
src/components/Earn/Farm/Table/Columns/Deposit.tsx
Normal file
@ -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 <Loading />
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-end'>
|
||||
<ActionButton onClick={enterVaultHandler} color='tertiary' text='Deposit' />
|
||||
</div>
|
||||
)
|
||||
}
|
57
src/components/Earn/Farm/Table/Columns/DepositCap.tsx
Normal file
57
src/components/Earn/Farm/Table/Columns/DepositCap.tsx
Normal file
@ -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<Vault> | Row<DepositedVault>,
|
||||
b: Row<Vault> | Row<DepositedVault>,
|
||||
): 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 <Loading />
|
||||
|
||||
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 (
|
||||
<TitleAndSubCell
|
||||
title={
|
||||
<FormattedNumber
|
||||
amount={vault.cap.max.toNumber()}
|
||||
options={{ minDecimals: 2, abbreviated: true, decimals }}
|
||||
className='text-xs'
|
||||
animate
|
||||
/>
|
||||
}
|
||||
sub={
|
||||
<FormattedNumber
|
||||
amount={percent.toNumber()}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: '% Filled' }}
|
||||
className='text-xs'
|
||||
animate
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
24
src/components/Earn/Farm/Table/Columns/Details.tsx
Normal file
24
src/components/Earn/Farm/Table/Columns/Details.tsx
Normal file
@ -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 <Loading />
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-end'>
|
||||
<div className={classNames('w-4', props.isExpanded && 'rotate-180')}>
|
||||
<ChevronDown />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
23
src/components/Earn/Farm/Table/Columns/MaxLTV.tsx
Normal file
23
src/components/Earn/Farm/Table/Columns/MaxLTV.tsx
Normal file
@ -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 <Loading />
|
||||
return (
|
||||
<FormattedNumber
|
||||
amount={vault.ltv.max * 100}
|
||||
options={{ minDecimals: 0, maxDecimals: 0, suffix: '%' }}
|
||||
className='text-xs'
|
||||
animate
|
||||
/>
|
||||
)
|
||||
}
|
72
src/components/Earn/Farm/Table/Columns/Name.tsx
Normal file
72
src/components/Earn/Farm/Table/Columns/Name.tsx
Normal file
@ -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 (
|
||||
<div className='flex'>
|
||||
<VaultLogo vault={vault} />
|
||||
<TitleAndSubCell
|
||||
className='ml-2 mr-2 text-left'
|
||||
title={`${vault.name}${unlockDuration}`}
|
||||
sub={vault.provider}
|
||||
/>
|
||||
{status === VaultStatus.UNLOCKING && (
|
||||
<Text
|
||||
className='group/label relative h-5 w-[84px] rounded-sm bg-green text-center leading-5 text-white'
|
||||
size='xs'
|
||||
>
|
||||
<span
|
||||
className={classNames(
|
||||
'absolute inset-0 text-center',
|
||||
'opacity-100 transition-opacity duration-500',
|
||||
'group-hover/label:opacity-0 group-[.is-expanded]/row:opacity-0',
|
||||
)}
|
||||
>
|
||||
Unlocking
|
||||
</span>
|
||||
<span
|
||||
className={classNames(
|
||||
'absolute inset-0 text-center',
|
||||
'opacity-0 transition-opacity duration-500',
|
||||
'group-hover/label:opacity-100 group-[.is-expanded]/row:opacity-100',
|
||||
)}
|
||||
>
|
||||
{produceCountdown(remainingTime)}
|
||||
</span>
|
||||
</Text>
|
||||
)}
|
||||
{status === VaultStatus.UNLOCKED && (
|
||||
<Text
|
||||
className='h-5 w-[84px] rounded-sm bg-green text-center leading-5 text-white'
|
||||
size='xs'
|
||||
>
|
||||
Unlocked
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
23
src/components/Earn/Farm/Table/Columns/PositionValue.tsx
Normal file
23
src/components/Earn/Farm/Table/Columns/PositionValue.tsx
Normal file
@ -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 <DisplayCurrency coin={coin} className='text-xs' />
|
||||
}
|
20
src/components/Earn/Farm/Table/Columns/TVL.tsx
Normal file
20
src/components/Earn/Farm/Table/Columns/TVL.tsx
Normal file
@ -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 <Loading />
|
||||
const coin = BNCoin.fromDenomAndBigNumber(vault.cap.denom, vault.cap.used)
|
||||
|
||||
return <DisplayCurrency coin={coin} className='text-xs' />
|
||||
}
|
@ -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<ColumnDef<Vault | DepositedVault>[]>(() => {
|
||||
return [
|
||||
{
|
||||
...NAME_META,
|
||||
cell: ({ row }) => <Name vault={row.original as Vault} />,
|
||||
},
|
||||
{
|
||||
...APY_META,
|
||||
cell: ({ row }) => <Apy vault={row.original as Vault} />,
|
||||
},
|
||||
{
|
||||
...TVL_META,
|
||||
cell: ({ row }) => <TVL vault={row.original as Vault} isLoading={props.isLoading} />,
|
||||
},
|
||||
{
|
||||
...DEPOSIT_CAP_META,
|
||||
cell: ({ row }) => <DepositCap vault={row.original as Vault} isLoading={props.isLoading} />,
|
||||
sortingFn: depositCapSortingFn,
|
||||
},
|
||||
{
|
||||
...LTV_MAX_META,
|
||||
cell: ({ row }) => <MaxLTV vault={row.original as Vault} isLoading={props.isLoading} />,
|
||||
},
|
||||
{
|
||||
...DETAILS_META,
|
||||
cell: ({ row }) => <Deposit vault={row.original as Vault} isLoading={props.isLoading} />,
|
||||
},
|
||||
]
|
||||
}, [props.isLoading])
|
||||
}
|
@ -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<ColumnDef<DepositedVault>[]>(() => {
|
||||
return [
|
||||
{
|
||||
...NAME_META,
|
||||
cell: ({ row }) => <Name vault={row.original as DepositedVault} />,
|
||||
},
|
||||
{
|
||||
...POSITION_VALUE_META,
|
||||
cell: ({ row }: { row: Row<DepositedVault> }) => (
|
||||
<PositionValue vault={row.original as DepositedVault} isLoading={props.isLoading} />
|
||||
),
|
||||
},
|
||||
{
|
||||
...APY_META,
|
||||
cell: ({ row }) => <Apy vault={row.original as DepositedVault} />,
|
||||
},
|
||||
{
|
||||
...TVL_META,
|
||||
cell: ({ row }) => (
|
||||
<TVL vault={row.original as DepositedVault} isLoading={props.isLoading} />
|
||||
),
|
||||
},
|
||||
{
|
||||
...DEPOSIT_CAP_META,
|
||||
cell: ({ row }) => (
|
||||
<DepositCap vault={row.original as DepositedVault} isLoading={props.isLoading} />
|
||||
),
|
||||
sortingFn: depositCapSortingFn,
|
||||
},
|
||||
{
|
||||
...LTV_MAX_META,
|
||||
cell: ({ row }) => (
|
||||
<MaxLTV vault={row.original as DepositedVault} isLoading={props.isLoading} />
|
||||
),
|
||||
},
|
||||
{
|
||||
...DETAILS_META,
|
||||
cell: ({ row }) => <Details isLoading={props.isLoading} isExpanded={row.getIsExpanded()} />,
|
||||
},
|
||||
]
|
||||
}, [props.isLoading])
|
||||
}
|
33
src/components/Earn/Farm/Table/DepositedVaultsTable.tsx
Normal file
33
src/components/Earn/Farm/Table/DepositedVaultsTable.tsx
Normal file
@ -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<DepositedVault>, table: TanStackTable<DepositedVault>) => (
|
||||
<VaultExpanded row={row} resetExpanded={table.resetExpanded} />
|
||||
),
|
||||
[],
|
||||
)
|
||||
|
||||
return (
|
||||
<Table
|
||||
title='Deposited Vaults'
|
||||
columns={columns}
|
||||
data={props.data}
|
||||
initialSorting={[{ id: 'name', desc: true }]}
|
||||
renderExpanded={renderExpanded}
|
||||
/>
|
||||
)
|
||||
}
|
@ -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<Vault | DepositedVault>
|
||||
row: Row<DepositedVault>
|
||||
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<number>(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
|
||||
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
|
||||
|
||||
function depositMoreHandler() {
|
||||
useStore.setState({
|
||||
@ -105,7 +105,7 @@ export default function VaultExpanded(props: Props) {
|
||||
return (
|
||||
<tr
|
||||
key={props.row.id}
|
||||
className='hover:cursor-pointer bg-black/20 transition-colors'
|
||||
className='transition-colors hover:cursor-pointer bg-black/20'
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
const isExpanded = props.row.getIsExpanded()
|
||||
@ -113,7 +113,7 @@ export default function VaultExpanded(props: Props) {
|
||||
!isExpanded && props.row.toggleExpanded()
|
||||
}}
|
||||
>
|
||||
<td colSpan={!status ? 7 : 8}>
|
||||
<td colSpan={props.row.getAllCells().length} className='p-0'>
|
||||
<div className='flex justify-end gap-3 p-4 align-center'>
|
||||
{status && <DepositMoreButton />}
|
||||
{status === VaultStatus.ACTIVE && <UnlockButton />}
|
||||
|
@ -12,9 +12,9 @@ export const VaultRow = (props: AssetRowProps) => {
|
||||
<tr
|
||||
key={props.row.id}
|
||||
className={classNames(
|
||||
'bg-white/3 group/row border-b border-t border-white/5 transition-colors hover:bg-white/5',
|
||||
'group/row border-t border-white/10 transition-colors hover:bg-white/5',
|
||||
vault.status && 'hover:cursor-pointer',
|
||||
props.row.getIsExpanded() && 'is-expanded',
|
||||
props.row.getIsExpanded() ? 'is-expanded bg-black/20' : 'border-b',
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
|
@ -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<SortingState>([{ id: 'name', desc: true }])
|
||||
|
||||
const columns = React.useMemo<ColumnDef<Vault | DepositedVault>[]>(() => {
|
||||
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 (
|
||||
<div className='flex'>
|
||||
<VaultLogo vault={vault} />
|
||||
<TitleAndSubCell
|
||||
className='ml-2 mr-2 text-left'
|
||||
title={`${vault.name}${unlockDuration}`}
|
||||
sub={vault.provider}
|
||||
/>
|
||||
{status === VaultStatus.UNLOCKING && (
|
||||
<Text
|
||||
className='group/label relative h-5 w-[84px] rounded-sm bg-green text-center leading-5 text-white'
|
||||
size='xs'
|
||||
>
|
||||
<span
|
||||
className={classNames(
|
||||
'absolute inset-0 text-center',
|
||||
'opacity-100 transition-opacity duration-500',
|
||||
'group-hover/label:opacity-0 group-[.is-expanded]/row:opacity-0',
|
||||
)}
|
||||
>
|
||||
Unlocking
|
||||
</span>
|
||||
<span
|
||||
className={classNames(
|
||||
'absolute inset-0 text-center',
|
||||
'opacity-0 transition-opacity duration-500',
|
||||
'group-hover/label:opacity-100 group-[.is-expanded]/row:opacity-100',
|
||||
)}
|
||||
>
|
||||
{produceCountdown(remainingTime)}
|
||||
</span>
|
||||
</Text>
|
||||
)}
|
||||
{status === VaultStatus.UNLOCKED && (
|
||||
<Text
|
||||
className='h-5 w-[84px] rounded-sm bg-green text-center leading-5 text-white'
|
||||
size='xs'
|
||||
>
|
||||
Unlocked
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
...((props.data[0] as DepositedVault)?.values
|
||||
? [
|
||||
{
|
||||
header: 'Pos. Value',
|
||||
cell: ({ row }: { row: Row<DepositedVault | Vault> }) => {
|
||||
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 <DisplayCurrency coin={coin} className='text-xs' />
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
accessorKey: 'apy',
|
||||
header: 'APY',
|
||||
cell: ({ row }) => {
|
||||
const vault = row.original as DepositedVault
|
||||
if (vault.apy === null) return <Loading />
|
||||
return (
|
||||
<FormattedNumber
|
||||
amount={vault.apy ?? 0}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: '%' }}
|
||||
className='text-xs'
|
||||
animate
|
||||
/>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'tvl',
|
||||
header: 'TVL',
|
||||
cell: ({ row }) => {
|
||||
const vault = row.original as DepositedVault
|
||||
if (props.isLoading) return <Loading />
|
||||
const coin = new BNCoin({
|
||||
denom: vault.cap.denom,
|
||||
amount: vault.cap.used.toString(),
|
||||
})
|
||||
|
||||
return <DisplayCurrency coin={coin} className='text-xs' />
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'cap',
|
||||
header: 'Depo. Cap',
|
||||
cell: ({ row }) => {
|
||||
const vault = row.original as DepositedVault
|
||||
if (props.isLoading) return <Loading />
|
||||
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 (
|
||||
<TitleAndSubCell
|
||||
title={
|
||||
<FormattedNumber
|
||||
amount={vault.cap.max.toNumber()}
|
||||
options={{ minDecimals: 2, abbreviated: true, decimals }}
|
||||
className='text-xs'
|
||||
animate
|
||||
/>
|
||||
}
|
||||
sub={
|
||||
<FormattedNumber
|
||||
amount={percent.toNumber()}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: '% Filled' }}
|
||||
className='text-xs'
|
||||
animate
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'ltv.max',
|
||||
header: 'Max LTV',
|
||||
cell: ({ row }) => {
|
||||
if (props.isLoading) return <Loading />
|
||||
return <Text className='text-xs'>{formatPercent(row.original.ltv.max)}</Text>
|
||||
},
|
||||
},
|
||||
{
|
||||
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 <Loading />
|
||||
return (
|
||||
<div className='flex items-center justify-end'>
|
||||
{vault.status ? (
|
||||
<div className={classNames('w-4', row.getIsExpanded() && 'rotate-180')}>
|
||||
<ChevronDown />
|
||||
</div>
|
||||
) : (
|
||||
<ActionButton onClick={enterVaultHandler} color='tertiary' text='Deposit' />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
}, [props.data, props.isLoading])
|
||||
|
||||
const table = useReactTable({
|
||||
data: props.data,
|
||||
columns: columns,
|
||||
state: {
|
||||
sorting,
|
||||
},
|
||||
onSortingChange: setSorting,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
})
|
||||
|
||||
return (
|
||||
<table className='w-full'>
|
||||
<thead className='bg-black/20'>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<th
|
||||
key={header.id}
|
||||
onClick={header.column.getToggleSortingHandler()}
|
||||
className={classNames(
|
||||
'px-4 py-3',
|
||||
header.column.getCanSort() && 'hover:cursor-pointer',
|
||||
header.id === 'symbol' ? 'text-left' : 'text-right',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
'flex',
|
||||
header.id === 'name' ? 'justify-start' : 'justify-end',
|
||||
'align-center',
|
||||
)}
|
||||
>
|
||||
<span className='w-6 h-6 text-white'>
|
||||
{header.column.getCanSort()
|
||||
? {
|
||||
asc: <SortAsc />,
|
||||
desc: <SortDesc />,
|
||||
false: <SortNone />,
|
||||
}[header.column.getIsSorted() as string] ?? null
|
||||
: null}
|
||||
</span>
|
||||
<Text
|
||||
tag='span'
|
||||
size='xs'
|
||||
className='flex items-center font-normal text-white/40'
|
||||
>
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</Text>
|
||||
</div>
|
||||
</th>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{table.getRowModel().rows.map((row) => {
|
||||
if (row.getIsExpanded()) {
|
||||
return (
|
||||
<React.Fragment key={`${row.id}_subrow`}>
|
||||
<VaultRow row={row} resetExpanded={table.resetExpanded} />
|
||||
<VaultExpanded row={row} resetExpanded={table.resetExpanded} />
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<VaultRow key={row.original.address} row={row} resetExpanded={table.resetExpanded} />
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
@ -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<number>(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
|
||||
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
|
||||
|
||||
async function handleWithdraw() {
|
||||
if (!accountId) return
|
||||
|
@ -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 && <VaultUnlockBanner vaults={unlockedVaults} />}
|
||||
<Card
|
||||
className='w-full h-fit bg-white/5'
|
||||
title={isAvailable ? 'Available vaults' : 'Deposited'}
|
||||
>
|
||||
<VaultTable data={vaultsToDisplay} />
|
||||
</Card>
|
||||
<VaultUnlockBanner vaults={unlockedVaults} />
|
||||
{deposited.length && (
|
||||
<DepositedVaultsTable data={deposited as DepositedVault[]} isLoading={false} />
|
||||
)}
|
||||
{available.length && <AvailableVaultsTable data={available as Vault[]} isLoading={false} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -88,25 +77,13 @@ function Fallback() {
|
||||
},
|
||||
}))
|
||||
|
||||
return (
|
||||
<Card className='w-full h-fit bg-white/5' title='Available vaults'>
|
||||
<VaultTable data={mockVaults} isLoading />
|
||||
</Card>
|
||||
)
|
||||
return <AvailableVaultsTable data={mockVaults} isLoading />
|
||||
}
|
||||
|
||||
export function AvailableVaults() {
|
||||
export default function Vaults() {
|
||||
return (
|
||||
<Suspense fallback={<Fallback />}>
|
||||
<Content type='available' />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
export function DepositedVaults() {
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<Content type='deposited' />
|
||||
<Content />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
@ -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: <ExclamationMarkCircled width={18} />,
|
||||
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={<ArrowDownLine />}
|
||||
iconClassName={iconClassnames}
|
||||
color='secondary'
|
||||
onClick={handleWithdraw}
|
||||
onClick={handleUnlend}
|
||||
className={buttonClassnames}
|
||||
>
|
||||
Withdraw
|
||||
Unlend
|
||||
</Button>
|
||||
)}
|
||||
|
||||
|
@ -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<LendingMarketTableData>, table: Table<LendingMarketTableData>) => {
|
||||
return (
|
||||
<MarketAssetTableRow
|
||||
key={`lend-asset-${row.id}`}
|
||||
isExpanded={row.getIsExpanded()}
|
||||
resetExpanded={table.resetExpanded}
|
||||
rowData={row}
|
||||
expandedActionButtons={<LendingActionButtons data={row.original} />}
|
||||
expandedDetails={<MarketDetails data={row.original} type='lend' />}
|
||||
/>
|
||||
)
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
const columns = useMemo<ColumnDef<LendingMarketTableData>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: 'asset.name',
|
||||
header: 'Asset',
|
||||
id: 'symbol',
|
||||
cell: ({ row }) => {
|
||||
const asset = row.original.asset
|
||||
|
||||
return (
|
||||
<div className='flex items-center flex-1 gap-3'>
|
||||
<AssetImage asset={asset} size={32} />
|
||||
<TitleAndSubCell
|
||||
title={asset.symbol}
|
||||
sub={asset.name}
|
||||
className='text-left min-w-15'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
...(shouldShowAccountDeposit
|
||||
? [
|
||||
{
|
||||
accessorKey: 'accountDepositValue',
|
||||
header: 'Deposited',
|
||||
cell: ({ row }) => {
|
||||
const amount = row.original.accountLentAmount
|
||||
|
||||
return (
|
||||
<AmountAndValue
|
||||
asset={row.original.asset}
|
||||
amount={amount ? BN(amount) : BN_ZERO}
|
||||
/>
|
||||
)
|
||||
},
|
||||
} as ColumnDef<LendingMarketTableData>,
|
||||
]
|
||||
: []),
|
||||
{
|
||||
accessorKey: 'marketLiquidityRate',
|
||||
header: 'APR',
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<AssetRate
|
||||
rate={convertLiquidityRateToAPR(row.original.marketLiquidityRate)}
|
||||
isEnabled={row.original.borrowEnabled}
|
||||
className='justify-end text-xs'
|
||||
type='apr'
|
||||
orientation='ltr'
|
||||
/>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'marketDepositCap',
|
||||
header: 'Depo. Cap',
|
||||
cell: ({ row }) => {
|
||||
const { marketDepositCap, marketDepositAmount, asset } = row.original
|
||||
const remainingCap = row.original.marketDepositCap.minus(marketDepositAmount)
|
||||
|
||||
return (
|
||||
<TitleAndSubCell
|
||||
className='text-xs'
|
||||
title={
|
||||
<FormattedNumber
|
||||
amount={marketDepositCap.toNumber()}
|
||||
options={{ abbreviated: true, decimals: asset.decimals }}
|
||||
animate
|
||||
/>
|
||||
}
|
||||
sub={
|
||||
<FormattedNumber
|
||||
amount={remainingCap.toNumber()}
|
||||
options={{ abbreviated: true, decimals: asset.decimals, suffix: ` left` }}
|
||||
animate
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'manage',
|
||||
enableSorting: false,
|
||||
header: 'Manage',
|
||||
cell: ({ row }) => (
|
||||
<div className='flex items-center justify-end'>
|
||||
<div className='w-4'>{row.getIsExpanded() ? <ChevronUp /> : <ChevronDown />}</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
],
|
||||
[shouldShowAccountDeposit],
|
||||
)
|
||||
|
||||
return <AssetListTable title={title} rowRenderer={rowRenderer} columns={columns} data={data} />
|
||||
}
|
37
src/components/Earn/Lend/Lends.tsx
Normal file
37
src/components/Earn/Lend/Lends.tsx
Normal file
@ -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 <Fallback />
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<DepositedLendsTable data={accountLentAssets} isLoading={false} />
|
||||
<AvailableLendsTable data={availableAssets} isLoading={false} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
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 <AvailableLendsTable data={data} isLoading />
|
||||
}
|
42
src/components/Earn/Lend/Table/AvailableLendsTable.tsx
Normal file
42
src/components/Earn/Lend/Table/AvailableLendsTable.tsx
Normal file
@ -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<LendingMarketTableData>) => (
|
||||
<>
|
||||
<ActionButtonRow row={row}>
|
||||
<LendingActionButtons data={row.original} />
|
||||
</ActionButtonRow>
|
||||
<MarketDetails row={row} type='lend' />
|
||||
</>
|
||||
),
|
||||
[],
|
||||
)
|
||||
|
||||
if (!props.data.length) return null
|
||||
|
||||
return (
|
||||
<Table
|
||||
title='Available Markets'
|
||||
columns={columns}
|
||||
data={props.data}
|
||||
initialSorting={[{ id: NAME_META.id, desc: false }]}
|
||||
renderExpanded={renderExpanded}
|
||||
/>
|
||||
)
|
||||
}
|
24
src/components/Earn/Lend/Table/Columns/Apy.tsx
Normal file
24
src/components/Earn/Lend/Table/Columns/Apy.tsx
Normal file
@ -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 <Loading />
|
||||
|
||||
return (
|
||||
<AssetRate
|
||||
rate={convertAprToApy((props.marketLiquidityRate ?? 0) * 100, 365)}
|
||||
isEnabled={props.borrowEnabled}
|
||||
className='justify-end text-xs'
|
||||
type='apy'
|
||||
orientation='ltr'
|
||||
/>
|
||||
)
|
||||
}
|
55
src/components/Earn/Lend/Table/Columns/DepositCap.tsx
Normal file
55
src/components/Earn/Lend/Table/Columns/DepositCap.tsx
Normal file
@ -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<LendingMarketTableData>,
|
||||
b: Row<LendingMarketTableData>,
|
||||
): 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 <Loading />
|
||||
const { marketDepositCap, marketDepositAmount, asset } = props.data
|
||||
const percent = marketDepositAmount.dividedBy(marketDepositCap).multipliedBy(100)
|
||||
|
||||
return (
|
||||
<TitleAndSubCell
|
||||
className='text-xs'
|
||||
title={
|
||||
<FormattedNumber
|
||||
amount={marketDepositCap.toNumber()}
|
||||
options={{ abbreviated: true, decimals: asset.decimals }}
|
||||
animate
|
||||
/>
|
||||
}
|
||||
sub={
|
||||
<FormattedNumber
|
||||
amount={percent.toNumber()}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: '% used' }}
|
||||
animate
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
32
src/components/Earn/Lend/Table/Columns/DepositValue.tsx
Normal file
32
src/components/Earn/Lend/Table/Columns/DepositValue.tsx
Normal file
@ -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<LendingMarketTableData>,
|
||||
b: Row<LendingMarketTableData>,
|
||||
): 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 (
|
||||
<AmountAndValue
|
||||
asset={props.asset}
|
||||
amount={props.lentAmount ? BN(props.lentAmount) : BN_ZERO}
|
||||
/>
|
||||
)
|
||||
}
|
16
src/components/Earn/Lend/Table/Columns/Manage.tsx
Normal file
16
src/components/Earn/Lend/Table/Columns/Manage.tsx
Normal file
@ -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 (
|
||||
<div className='flex items-center justify-end'>
|
||||
<div className='w-4'>{props.isExpanded ? <ChevronUp /> : <ChevronDown />}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
16
src/components/Earn/Lend/Table/Columns/Name.tsx
Normal file
16
src/components/Earn/Lend/Table/Columns/Name.tsx
Normal file
@ -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 (
|
||||
<div className='flex items-center flex-1 gap-3'>
|
||||
<AssetImage asset={asset} size={32} />
|
||||
<TitleAndSubCell title={asset.symbol} sub={asset.name} className='text-left min-w-15' />
|
||||
</div>
|
||||
)
|
||||
}
|
@ -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<ColumnDef<LendingMarketTableData>[]>(() => {
|
||||
return [
|
||||
{
|
||||
...NAME_META,
|
||||
cell: ({ row }) => <Name asset={row.original.asset} />,
|
||||
},
|
||||
{
|
||||
...APY_META,
|
||||
cell: ({ row }) => (
|
||||
<Apy
|
||||
isLoading={props.isLoading}
|
||||
borrowEnabled={row.original.borrowEnabled}
|
||||
marketLiquidityRate={row.original.marketLiquidityRate}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
...DEPOSIT_CAP_META,
|
||||
cell: ({ row }) => <DepositCap isLoading={props.isLoading} data={row.original} />,
|
||||
sortingFn: marketDepositCapSortingFn,
|
||||
},
|
||||
{
|
||||
...MANAGE_META,
|
||||
cell: ({ row }) => <Manage isExpanded={row.getIsExpanded()} />,
|
||||
},
|
||||
]
|
||||
}, [props.isLoading])
|
||||
}
|
@ -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<ColumnDef<LendingMarketTableData>[]>(() => {
|
||||
return [
|
||||
{
|
||||
...NAME_META,
|
||||
cell: ({ row }) => <Name asset={row.original.asset} />,
|
||||
},
|
||||
{
|
||||
...DEPOSIT_VALUE_META,
|
||||
cell: ({ row }) => (
|
||||
<DepositValue asset={row.original.asset} lentAmount={row.original.accountLentAmount} />
|
||||
),
|
||||
sortingFn: depositedSortingFn,
|
||||
},
|
||||
{
|
||||
...APY_META,
|
||||
cell: ({ row }) => (
|
||||
<Apy
|
||||
isLoading={props.isLoading}
|
||||
borrowEnabled={row.original.borrowEnabled}
|
||||
marketLiquidityRate={row.original.marketLiquidityRate}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
...DEPOSIT_CAP_META,
|
||||
cell: ({ row }) => <DepositCap isLoading={props.isLoading} data={row.original} />,
|
||||
sortingFn: marketDepositCapSortingFn,
|
||||
},
|
||||
{
|
||||
...MANAGE_META,
|
||||
cell: ({ row }) => <Manage isExpanded={row.getIsExpanded()} />,
|
||||
},
|
||||
]
|
||||
}, [props.isLoading])
|
||||
}
|
42
src/components/Earn/Lend/Table/DepositedLendsTable.tsx
Normal file
42
src/components/Earn/Lend/Table/DepositedLendsTable.tsx
Normal file
@ -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<LendingMarketTableData>) => (
|
||||
<>
|
||||
<ActionButtonRow row={row}>
|
||||
<LendingActionButtons data={row.original} />
|
||||
</ActionButtonRow>
|
||||
<MarketDetails row={row} type='lend' />
|
||||
</>
|
||||
),
|
||||
[],
|
||||
)
|
||||
|
||||
if (!props.data.length) return null
|
||||
|
||||
return (
|
||||
<Table
|
||||
title='Lent Assets'
|
||||
columns={columns}
|
||||
data={props.data}
|
||||
initialSorting={[{ id: NAME_META.id, desc: false }]}
|
||||
renderExpanded={renderExpanded}
|
||||
/>
|
||||
)
|
||||
}
|
@ -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<boolean>(
|
||||
REDUCE_MOTION_KEY,
|
||||
LocalStorageKeys.REDUCE_MOTION,
|
||||
DEFAULT_SETTINGS.reduceMotion,
|
||||
)
|
||||
const prevAmountRef = useRef<number>(0)
|
||||
|
@ -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<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
||||
const [reduceMotion] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.REDUCE_MOTION,
|
||||
DEFAULT_SETTINGS.reduceMotion,
|
||||
)
|
||||
const radius = 16
|
||||
const percentageValue = percentage > 100 ? 100 : percentage < 0 ? 0 : percentage
|
||||
const circlePercent = 100 - percentageValue
|
||||
|
64
src/components/HLS/Farm/AvailableHLSVaults.tsx
Normal file
64
src/components/HLS/Farm/AvailableHLSVaults.tsx
Normal file
@ -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 (
|
||||
<Table
|
||||
title={title}
|
||||
columns={columns}
|
||||
data={hlsVaults}
|
||||
initialSorting={[{ id: NAME_META.id, desc: true }]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default function AvailableHlsVaults() {
|
||||
return (
|
||||
<Suspense fallback={<Fallback />}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<Table
|
||||
title={title}
|
||||
columns={columns}
|
||||
data={mockVaults}
|
||||
initialSorting={[{ id: NAME_META.id, desc: true }]}
|
||||
/>
|
||||
)
|
||||
}
|
31
src/components/HLS/Farm/HLSFarmIntro.tsx
Normal file
31
src/components/HLS/Farm/HLSFarmIntro.tsx
Normal file
@ -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 (
|
||||
<Intro
|
||||
bg='farm'
|
||||
text={
|
||||
<>
|
||||
<span className='text-white'>Leveraged farming</span> 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.
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
text='Learn how to Farm'
|
||||
leftIcon={<PlusSquared />}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
window.open(DocURL.FARM_INTRO_URL, '_blank')
|
||||
}}
|
||||
color='secondary'
|
||||
/>
|
||||
</Intro>
|
||||
)
|
||||
}
|
40
src/components/HLS/Farm/Table/Columns/APY.tsx
Normal file
40
src/components/HLS/Farm/Table/Columns/APY.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import React from 'react'
|
||||
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import Loading from 'components/Loading'
|
||||
import useMarketBorrowings from 'hooks/useMarketBorrowings'
|
||||
|
||||
export const APY_META = { accessorKey: 'apy', header: 'APY Range' }
|
||||
|
||||
interface Props {
|
||||
vault: Vault
|
||||
}
|
||||
|
||||
export default function Apy(props: Props) {
|
||||
const { vault } = props
|
||||
const { data: marketBorrowings } = useMarketBorrowings()
|
||||
|
||||
const borrowRate = marketBorrowings.find((asset) => asset.denom === vault.hls?.borrowDenom)
|
||||
?.borrowRate
|
||||
|
||||
if (vault.apy === null || borrowRate === null) return <Loading />
|
||||
|
||||
const APYs = [vault.apy, vault.apy * (vault.hls?.maxLeverage || 1) - (borrowRate || 0) * 100]
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormattedNumber
|
||||
amount={Math.min(...APYs)}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: '-' }}
|
||||
className='text-xs inline'
|
||||
animate
|
||||
/>
|
||||
<FormattedNumber
|
||||
amount={Math.max(...APYs)}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: '%' }}
|
||||
className='text-xs inline'
|
||||
animate
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
104
src/components/HLS/Farm/Table/Columns/Deposit.tsx
Normal file
104
src/components/HLS/Farm/Table/Columns/Deposit.tsx
Normal file
@ -0,0 +1,104 @@
|
||||
import classNames from 'classnames'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import ActionButton from 'components/Button/ActionButton'
|
||||
import { Enter } from 'components/Icons'
|
||||
import Loading from 'components/Loading'
|
||||
import Text from 'components/Text'
|
||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||
import useAlertDialog from 'hooks/useAlertDialog'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
|
||||
export const DEPOSIT_META = { accessorKey: 'deposit', header: 'Deposit' }
|
||||
|
||||
interface Props {
|
||||
vault: Vault
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export default function Deposit(props: Props) {
|
||||
const { vault } = props
|
||||
|
||||
const [showHlsInfo, setShowHlsInfo] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.HLS_INFORMATION,
|
||||
true,
|
||||
)
|
||||
|
||||
const { open: openAlertDialog, close } = useAlertDialog()
|
||||
|
||||
const showHlsInfoModal = useCallback(() => {
|
||||
if (!showHlsInfo) return
|
||||
openAlertDialog({
|
||||
title: 'Understanding HLS Positions',
|
||||
content: (
|
||||
<div className='flex flex-col gap-8 pt-2 pb-8 pr-10'>
|
||||
{INFO_ITEMS.map((item) => (
|
||||
<div key={item.title} className='grid grid-cols-[min-content,auto]'>
|
||||
<span
|
||||
className={classNames(
|
||||
'rounded-sm relative h-10 w-10 p-3 bg-white/10 mr-6 grid place-items-center',
|
||||
'before:content-[" "] before:absolute before:inset-0 before:rounded-sm before:p-[1px] before:border-glas before:-z-1',
|
||||
)}
|
||||
>
|
||||
{item.icon}
|
||||
</span>
|
||||
<span className='flex flex-col'>
|
||||
<Text>{item.title}</Text>
|
||||
<Text className='text-sm text-white/60'>{item.description}</Text>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
positiveButton: {
|
||||
text: 'Continue',
|
||||
icon: <Enter />,
|
||||
onClick: enterVaultHandler,
|
||||
},
|
||||
negativeButton: {
|
||||
text: 'Cancel',
|
||||
onClick: () => {
|
||||
setShowHlsInfo(true)
|
||||
close()
|
||||
},
|
||||
},
|
||||
checkbox: {
|
||||
text: "Don't show again",
|
||||
onClick: (isChecked: boolean) => setShowHlsInfo(!isChecked),
|
||||
},
|
||||
})
|
||||
}, [close, enterVaultHandler, openAlertDialog, setShowHlsInfo, showHlsInfo])
|
||||
|
||||
function enterVaultHandler() {
|
||||
showHlsInfoModal()
|
||||
return
|
||||
}
|
||||
|
||||
if (props.isLoading) return <Loading />
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-end'>
|
||||
<ActionButton onClick={enterVaultHandler} color='tertiary' text='Deposit' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const INFO_ITEMS = [
|
||||
{
|
||||
icon: <Enter width={16} height={16} />,
|
||||
title: 'One account, one position',
|
||||
description:
|
||||
'A minted HLS account can only have a single position tied to it, in order to limit risk.',
|
||||
},
|
||||
{
|
||||
icon: <Enter />,
|
||||
title: 'Funded from your wallet',
|
||||
description: 'To fund your HLS position, funds will have to come directly from your wallet.',
|
||||
},
|
||||
{
|
||||
icon: <Enter />,
|
||||
title: 'Accounts are reusable',
|
||||
description:
|
||||
'If you exited a position from a minted account, this account can be reused for a new position.',
|
||||
},
|
||||
]
|
19
src/components/HLS/Farm/Table/Columns/MaxLeverage.tsx
Normal file
19
src/components/HLS/Farm/Table/Columns/MaxLeverage.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React from 'react'
|
||||
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
|
||||
export const MAX_LEV_META = { accessorKey: 'hls.maxLeverage', header: 'Max Leverage' }
|
||||
interface Props {
|
||||
vault: Vault
|
||||
}
|
||||
|
||||
export default function MaxLeverage(props: Props) {
|
||||
return (
|
||||
<FormattedNumber
|
||||
amount={props.vault.hls?.maxLeverage || 1}
|
||||
options={{ minDecimals: 2, maxDecimals: 2, suffix: 'x' }}
|
||||
className='text-xs'
|
||||
animate
|
||||
/>
|
||||
)
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import { ColumnDef } from '@tanstack/react-table'
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
import DepositCap, { DEPOSIT_CAP_META } 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 Apy, { APY_META } from 'components/HLS/Farm/Table/Columns/APY'
|
||||
import Deposit, { DEPOSIT_META } from 'components/HLS/Farm/Table/Columns/Deposit'
|
||||
import MaxLeverage, { MAX_LEV_META } from 'components/HLS/Farm/Table/Columns/MaxLeverage'
|
||||
|
||||
interface Props {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export default function useAvailableColumns(props: Props) {
|
||||
// const
|
||||
return useMemo<ColumnDef<Vault>[]>(
|
||||
() => [
|
||||
{
|
||||
...NAME_META,
|
||||
cell: ({ row }) => <Name vault={row.original as Vault} />,
|
||||
},
|
||||
{
|
||||
...APY_META,
|
||||
cell: ({ row }) => <Apy vault={row.original as Vault} />,
|
||||
},
|
||||
{
|
||||
...MAX_LEV_META,
|
||||
cell: ({ row }) => <MaxLeverage vault={row.original} />,
|
||||
},
|
||||
{
|
||||
...TVL_META,
|
||||
cell: ({ row }) => <TVL vault={row.original as Vault} isLoading={props.isLoading} />,
|
||||
},
|
||||
{
|
||||
...DEPOSIT_CAP_META,
|
||||
cell: ({ row }) => <DepositCap vault={row.original as Vault} isLoading={props.isLoading} />,
|
||||
},
|
||||
{
|
||||
...LTV_MAX_META,
|
||||
cell: ({ row }) => <MaxLTV vault={row.original as Vault} isLoading={props.isLoading} />,
|
||||
},
|
||||
{
|
||||
...DEPOSIT_META,
|
||||
cell: ({ row }) => <Deposit vault={row.original as Vault} isLoading={props.isLoading} />,
|
||||
},
|
||||
],
|
||||
[props.isLoading],
|
||||
)
|
||||
}
|
7
src/components/HLS/Farm/Table/index.tsx
Normal file
7
src/components/HLS/Farm/Table/index.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
interface Props {
|
||||
data: HLSStrategy[] | DepositedHLSStrategy[]
|
||||
}
|
||||
|
||||
export default function Index(props: Props) {
|
||||
return null
|
||||
}
|
3
src/components/HLS/Staking/AvailableHLSStakingAssets.tsx
Normal file
3
src/components/HLS/Staking/AvailableHLSStakingAssets.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function AvailableHlsStakingAssets() {
|
||||
return null
|
||||
}
|
31
src/components/HLS/Staking/HLSStakingIntro.tsx
Normal file
31
src/components/HLS/Staking/HLSStakingIntro.tsx
Normal file
@ -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 HLSStakingIntro() {
|
||||
return (
|
||||
<Intro
|
||||
bg='borrow'
|
||||
text={
|
||||
<>
|
||||
<span className='text-white'>Leverage staking</span> is a strategy where users borrow
|
||||
funds to increase their staking position, aiming to earn more in rewards than the cost of
|
||||
the borrowed assets.
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
text='Learn how to Stake'
|
||||
leftIcon={<PlusSquared />}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
window.open(DocURL.FARM_INTRO_URL, '_blank')
|
||||
}}
|
||||
color='secondary'
|
||||
/>
|
||||
</Intro>
|
||||
)
|
||||
}
|
@ -4,7 +4,7 @@ import { useMemo } from 'react'
|
||||
import { Heart } from 'components/Icons'
|
||||
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 { computeHealthGaugePercentage } from 'utils/accounts'
|
||||
@ -25,7 +25,10 @@ const ROTATION = {
|
||||
export const HealthGauge = ({ diameter = 40, health = 0, updatedHealth = 0 }: Props) => {
|
||||
const [color, label] = useHealthColorAndLabel(health, 'text')
|
||||
const [updatedColor, updatedLabel] = useHealthColorAndLabel(updatedHealth ?? 0, 'text')
|
||||
const [reduceMotion] = useLocalStorage<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
|
||||
const [reduceMotion] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.REDUCE_MOTION,
|
||||
DEFAULT_SETTINGS.reduceMotion,
|
||||
)
|
||||
const percentage = useMemo(() => computeHealthGaugePercentage(health), [health])
|
||||
const updatedPercentage = useMemo(
|
||||
() => computeHealthGaugePercentage(updatedHealth),
|
||||
@ -77,6 +80,140 @@ export const HealthGauge = ({ diameter = 40, health = 0, updatedHealth = 0 }: Pr
|
||||
/>
|
||||
</g>
|
||||
</mask>
|
||||
<mask id='backgroundMask'>
|
||||
<g>
|
||||
<path
|
||||
fill='white'
|
||||
d='M137.1,538.3c-5.8-6.5-11.3-13.3-16.5-20.4l-57.8,31.9c7.4,10.5,15.3,20.7,23.8,30.3L137.1,538.3z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M179.4,577.1c-8.1-6-15.8-12.4-23.1-19.2L105.5,600c10.3,10,21.3,19.5,32.9,28.3L179.4,577.1z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M79.5,437.5c-5.6-17.2-24.1-26.6-41.3-21.1c-7.9,2.6-14.3,8-18.2,14.8c-4.5,7.7-5.8,17.2-2.8,26.5c0.3,1,0.6,1.9,1,2.9
|
||||
l62.7-19.4C80.3,440,79.9,438.7,79.5,437.5z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M105.6,495.1c-5.4-9.2-10.4-18.7-14.8-28.5l-63,19.5c5.9,14.1,12.8,27.7,20.4,40.7L105.6,495.1z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M223.5,604.3c-7.3-3.6-14.5-7.6-21.5-11.9L160.8,644c10.4,6.7,21.1,12.8,32,18.3L223.5,604.3z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M275.6,624c-9.2-2.5-18.2-5.4-27.1-8.8l-30.8,58.4c13,5.3,26.2,9.8,39.8,13.5L275.6,624z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M352.5,633.9l-6.1,65.4c1.1,0,2.1,0,3.2,0c18.1,0,32.8-14.7,32.8-32.8C382.4,649.4,369.3,635.4,352.5,633.9z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M325.3,632.7c-7.7-0.6-15.4-1.6-22.9-2.9l-18.3,63.4c11.6,2.2,23.3,3.8,35.1,4.8L325.3,632.7z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M519.5,577.5c-5.8,4.3-11.8,8.4-17.9,12.3l31.7,57.4c9.7-6,19.1-12.4,28.1-19.3L519.5,577.5z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M559.1,541.7c-5.9,6.5-12.1,12.6-18.5,18.4l41.9,50.5c9.8-8.8,19.1-18.1,27.9-28L559.1,541.7z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M477.9,603.2c-7.8,3.9-15.9,7.5-24.1,10.8l19.4,62.7c12.5-4.7,24.7-10.1,36.4-16.1L477.9,603.2z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M589,502.6c-3,4.7-6.2,9.4-9.5,14c-1,1.4-2,2.7-3,4.1l51.2,40.9c1.6-2.1,3.3-4.3,4.9-6.5c5.2-7.1,10-14.4,14.6-21.7
|
||||
L589,502.6z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M666.6,316.8c-18.1,0-32.8,14.7-32.8,32.8c0,10.1-0.5,20-1.6,29.9l65.3,6.1c1.2-11.9,1.8-23.9,1.8-36
|
||||
C699.4,331.4,684.7,316.7,666.6,316.8z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M428.4,624.4c-3.8,2.7-6.9,6.2-9.2,10.2c-4.5,7.8-5.7,17.4-2.8,26.4c4.4,13.6,16.8,22.3,30.3,22.6L428.4,624.4z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M613.8,454.5c-3.3,8.3-7,16.5-11.1,24.5l58,30.6c6.2-12,11.7-24.3,16.4-36.9L613.8,454.5z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M628.1,406.5c-1.5,7.5-3.3,14.9-5.5,22.2l63.1,18.2c3.3-11.3,5.9-22.7,8-34.3L628.1,406.5z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M458.8,87.2c10.3,4.3,20.3,9.2,30.1,14.7l30.6-58c-13.7-7.6-27.9-14.3-42.4-20L458.8,87.2z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M512,116.4c7.5,5.3,14.8,10.9,21.8,16.8l40.9-51.2c-10.1-8.5-20.8-16.5-31.9-23.9L512,116.4z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M343.4,65.5c11.6-0.3,23.1,0.2,34.6,1.4L384,1.6c-15.9-1.6-31.9-2.1-48-1.4L343.4,65.5z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M574.3,175.6l50.5-41.9c-9.2-11.8-19.2-22.9-29.9-33.4l-41.2,51.6C561,159.4,567.9,167.3,574.3,175.6z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M32.7,382.2c16.6,0,30.2-12.2,32.5-28.1L0,348c0,0.5,0,0.9,0,1.4C0,367.6,14.7,382.2,32.7,382.2z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M590,198c4.6,7.3,8.8,14.7,12.8,22.4l57.4-31.7c-5.9-11.3-12.3-22.3-19.4-32.9L590,198z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M619.8,261.7c5.6,17.2,24.1,26.6,41.3,21.1c17.2-5.6,26.6-24.1,21.1-41.3c-3.1-9.5-6.6-18.9-10.5-28L614,245.4
|
||||
C616.1,250.8,618.1,256.2,619.8,261.7z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M404.9,70.8c9.5,1.9,18.9,4.3,28.1,7.1L451.2,15C438,11,424.6,7.8,411,5.3L404.9,70.8z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M286.5,72.5c6.2-1.4,12.4-2.6,18.7-3.6c3.6-0.6,7.3-1.1,10.9-1.5l-7.3-65.1c-4.6,0.5-9.2,1.2-13.8,1.9
|
||||
c-9.4,1.5-18.7,3.4-27.8,5.6L286.5,72.5z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M66.5,326.8c0.8-9.8,2.1-19.4,3.8-29L7.1,279.6c-2.8,13.6-4.8,27.3-6,41.1L66.5,326.8z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M132.5,166.2c0.4-0.4,0.7-0.9,1.1-1.3c7.4-8.6,15.2-16.7,23.5-24.3l-41.8-50.4c-11.1,10-21.6,20.8-31.4,32.2
|
||||
c-0.8,1-1.6,2-2.5,3L132.5,166.2z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M100.1,213.6c4.8-8.8,10.1-17.4,15.8-25.7L64.8,147c-6.4,9-12.3,18.2-17.8,27.7c-1.6,2.8-3.1,5.6-4.7,8.4L100.1,213.6z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M195.5,35.8l31.7,57.4c10.7-5.1,21.8-9.6,33.1-13.3l-19.3-62.6C225.4,22.4,210.2,28.6,195.5,35.8z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M76.5,271.2c3.2-11.2,7.1-22.3,11.7-33l-58-30.6c-6.6,14.8-12.1,30-16.6,45.5L76.5,271.2z'
|
||||
/>
|
||||
<path
|
||||
fill='white'
|
||||
d='M178,123.1c8.1-6.1,16.5-11.8,25.2-17.1l-31.7-57.3c-12.2,7.3-24.1,15.3-35.4,24L178,123.1z'
|
||||
/>
|
||||
</g>
|
||||
</mask>
|
||||
<circle
|
||||
r={RADIUS}
|
||||
cx={RADIUS}
|
||||
@ -97,7 +234,7 @@ export const HealthGauge = ({ diameter = 40, health = 0, updatedHealth = 0 }: Pr
|
||||
strokeDashoffset={isUpdated && isIncrease ? updatedPercentage : percentage}
|
||||
strokeLinecap='round'
|
||||
style={ROTATION}
|
||||
mask='url(#mask)'
|
||||
mask={isUpdated ? 'url(#backgroundMask)' : 'url(#mask)'}
|
||||
className={classNames(
|
||||
backgroundColor,
|
||||
'stroke-current',
|
||||
|
@ -1,73 +0,0 @@
|
||||
import { flexRender, Row } from '@tanstack/react-table'
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Text from 'components/Text'
|
||||
|
||||
type Props<TData> = {
|
||||
rowData: Row<TData>
|
||||
resetExpanded: (defaultState?: boolean | undefined) => void
|
||||
isExpanded: boolean
|
||||
expandedActionButtons?: JSX.Element
|
||||
expandedDetails?: JSX.Element
|
||||
}
|
||||
|
||||
function AssetListTableRow<TData>(props: Props<TData>) {
|
||||
const renderFullRow = (key: string, content: JSX.Element) => (
|
||||
<tr key={key} className='bg-black/20'>
|
||||
<td
|
||||
className='border-b border-white border-opacity-10 p-4'
|
||||
colSpan={props.rowData.getAllCells().length}
|
||||
>
|
||||
{content}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
const renderExpanded = () => {
|
||||
return (
|
||||
<>
|
||||
{props.expandedActionButtons &&
|
||||
renderFullRow(
|
||||
`${props.rowData.id}-expanded-actions`,
|
||||
<div className='flex flex-1 flex-row justify-between'>
|
||||
<Text className='mt-1 flex p-0 font-bold' size='base'>
|
||||
Details
|
||||
</Text>
|
||||
<div>{props.expandedActionButtons}</div>
|
||||
</div>,
|
||||
)}
|
||||
{props.expandedDetails &&
|
||||
renderFullRow(`${props.rowData.id}-expanded-details`, props.expandedDetails)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<tr
|
||||
key={props.rowData.id}
|
||||
className={classNames(
|
||||
'hover:cursor-pointer transition-colors',
|
||||
|
||||
props.rowData.getIsExpanded() ? 'bg-black/20' : 'bg-white/0 hover:bg-white/5',
|
||||
)}
|
||||
onClick={() => {
|
||||
const isExpanded = props.rowData.getIsExpanded()
|
||||
props.resetExpanded()
|
||||
!isExpanded && props.rowData.toggleExpanded()
|
||||
}}
|
||||
>
|
||||
{props.rowData.getVisibleCells().map((cell) => {
|
||||
return (
|
||||
<td key={cell.id} className={'p-4 text-right'}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
{props.isExpanded && renderExpanded()}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AssetListTableRow
|
@ -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<BorrowMarketTableData | LendingMarketTableData>
|
||||
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 (
|
||||
<div className='flex justify-between flex-1 bg-white rounded-md bg-opacity-5'>
|
||||
{details.map((detail, index) => (
|
||||
<TitleAndSubCell
|
||||
key={index}
|
||||
className='text-center'
|
||||
containerClassName='m-5 ml-10 mr-10 space-y-1'
|
||||
title={
|
||||
<FormattedNumber
|
||||
className='text-xs text-center'
|
||||
amount={detail.amount}
|
||||
options={detail.options}
|
||||
animate
|
||||
<tr>
|
||||
<td colSpan={row.getAllCells().length} className='p-4 border-b bg-black/20 border-white/10'>
|
||||
<div className='flex justify-between flex-1 rounded-md bg-white/5'>
|
||||
{details.map((detail, index) => (
|
||||
<TitleAndSubCell
|
||||
key={index}
|
||||
className='text-center'
|
||||
containerClassName='m-5 ml-10 mr-10 space-y-1'
|
||||
title={
|
||||
<FormattedNumber
|
||||
className='text-xs text-center'
|
||||
amount={detail.amount}
|
||||
options={detail.options}
|
||||
animate
|
||||
/>
|
||||
}
|
||||
sub={detail.title}
|
||||
/>
|
||||
}
|
||||
sub={detail.title}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
@ -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<boolean>(
|
||||
MIGRATION_BANNER_KEY,
|
||||
LocalStorageKeys.MIGRATION_BANNER,
|
||||
DEFAULT_SETTINGS.migrationBanner,
|
||||
)
|
||||
|
||||
|
@ -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: (
|
||||
<div className='flex w-full h-full p-3'>
|
||||
<InfoCircle />
|
||||
</div>
|
||||
),
|
||||
icon: <InfoCircle />,
|
||||
title,
|
||||
description,
|
||||
content,
|
||||
negativeButton: {
|
||||
text: 'Cancel',
|
||||
icon: <Enter />,
|
||||
@ -30,7 +27,7 @@ export default function AccountDeleteAlertDialog(props: Props) {
|
||||
},
|
||||
positiveButton,
|
||||
})
|
||||
}, [showAlertDialog, title, description, closeHandler, positiveButton])
|
||||
}, [showAlertDialog, closeHandler, positiveButton, title, content])
|
||||
|
||||
return null
|
||||
}
|
||||
|
@ -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 (
|
||||
<AccountDeleteAlertDialog
|
||||
title='Repay your Debts to delete your account'
|
||||
description='You must repay all borrowings before deleting your account.'
|
||||
icon={<ExclamationMarkCircled width={18} />}
|
||||
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 (
|
||||
<AccountDeleteAlertDialog
|
||||
title='Close your positions to delete your account'
|
||||
description='You must first close your farming positions before deleting your account.'
|
||||
content='You must first close your farming positions before deleting your account.'
|
||||
closeHandler={closeDeleteAccountModal}
|
||||
positiveButton={{
|
||||
text: 'Close Positions',
|
||||
@ -85,7 +86,7 @@ function AccountDeleteModal(props: Props) {
|
||||
return (
|
||||
<AccountDeleteAlertDialog
|
||||
title={`Delete Credit Account ${accountId}`}
|
||||
description='Deleting your Credit Account is irreversible.'
|
||||
content='Deleting your Credit Account is irreversible.'
|
||||
closeHandler={closeDeleteAccountModal}
|
||||
positiveButton={{
|
||||
text: 'Delete Account',
|
||||
@ -98,7 +99,7 @@ function AccountDeleteModal(props: Props) {
|
||||
return (
|
||||
<AccountDeleteAlertDialog
|
||||
title={`Delete Credit Account ${accountId}`}
|
||||
description={
|
||||
content={
|
||||
<>
|
||||
<Text className='mt-2 text-white/50' size='sm'>
|
||||
The following assets within your Credit Account will be sent to your wallet.
|
||||
|
@ -75,7 +75,7 @@ export default function AddVaultAssetsModalContent(props: Props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='border-b border-white/5 bg-white/10 px-4 py-3'>
|
||||
<div className='px-4 py-3 border-b border-white/5 bg-white/10'>
|
||||
<SearchBar
|
||||
value={searchString}
|
||||
placeholder={`Search for e.g. "ETH" or "Ethereum"`}
|
||||
@ -90,6 +90,7 @@ export default function AddVaultAssetsModalContent(props: Props) {
|
||||
</Text>
|
||||
</div>
|
||||
<AssetSelectTable
|
||||
isBorrow={true}
|
||||
assets={poolAssets}
|
||||
onChangeSelected={onChangePoolDenoms}
|
||||
selectedDenoms={selectedPoolDenoms}
|
||||
@ -102,6 +103,7 @@ export default function AddVaultAssetsModalContent(props: Props) {
|
||||
</Text>
|
||||
</div>
|
||||
<AssetSelectTable
|
||||
isBorrow={true}
|
||||
assets={stableAssets}
|
||||
onChangeSelected={onChangeOtherDenoms}
|
||||
selectedDenoms={selectedOtherDenoms}
|
||||
|
@ -40,7 +40,9 @@ export default function AddVaultBorrowAssetsModal() {
|
||||
onChangeBorrowDenoms={updateSelectedDenoms}
|
||||
/>
|
||||
) : (
|
||||
<CircularProgress />
|
||||
<div className='flex items-center justify-center w-full h-[380px]'>
|
||||
<CircularProgress />
|
||||
</div>
|
||||
)}
|
||||
<div className='flex w-full p-4'>
|
||||
<Button className='w-full' onClick={onClose} color='tertiary' text='Select Assets' />
|
||||
|
@ -1,12 +1,12 @@
|
||||
import classNames from 'classnames'
|
||||
import { useState } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import { ExclamationMarkCircled } from 'components/Icons'
|
||||
import Checkbox from 'components/Checkbox'
|
||||
import Modal from 'components/Modal'
|
||||
import { NoIcon, YesIcon } from 'components/Modals/AlertDialog/ButtonIcons'
|
||||
import Text from 'components/Text'
|
||||
import useAlertDialog from 'hooks/useAlertDialog'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
|
||||
export default function AlertDialogController() {
|
||||
const { config, close } = useAlertDialog()
|
||||
@ -22,19 +22,32 @@ interface Props {
|
||||
}
|
||||
|
||||
function AlertDialog(props: Props) {
|
||||
const { title, icon, description, negativeButton, positiveButton } = props.config
|
||||
const { title, icon, content, negativeButton, positiveButton, checkbox } = props.config
|
||||
|
||||
const [toggle, handleToggle] = useToggle()
|
||||
|
||||
const handleButtonClick = (button?: AlertDialogButton) => {
|
||||
button?.onClick && button.onClick()
|
||||
props.close()
|
||||
}
|
||||
|
||||
function handleCheckboxClick() {
|
||||
handleToggle()
|
||||
checkbox?.onClick(!toggle)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onClose={props.close}
|
||||
hideTxLoader
|
||||
header={
|
||||
<div className='grid w-12 h-12 rounded-sm place-items-center bg-white/5'>
|
||||
{icon ?? <ExclamationMarkCircled width={18} />}
|
||||
<div className='flex flex-col'>
|
||||
{icon && (
|
||||
<div className='grid w-12 h-12 rounded-sm place-items-center bg-white/5 mb-4'>
|
||||
{icon}
|
||||
</div>
|
||||
)}
|
||||
<Text size='2xl'>{title}</Text>
|
||||
</div>
|
||||
}
|
||||
modalClassName='max-w-modal-sm'
|
||||
@ -42,24 +55,28 @@ function AlertDialog(props: Props) {
|
||||
contentClassName='px-8 pb-8'
|
||||
hideCloseBtn
|
||||
>
|
||||
<Text size='xl'>{title}</Text>
|
||||
{typeof description === 'string' ? (
|
||||
<Text className='mt-2 text-white/60'>{description}</Text>
|
||||
{typeof content === 'string' ? (
|
||||
<Text className='mt-2 text-white/60'>{content}</Text>
|
||||
) : (
|
||||
description
|
||||
content
|
||||
)}
|
||||
<div
|
||||
className={classNames('mt-10 flex justify-between', positiveButton && 'flex-row-reverse')}
|
||||
>
|
||||
{positiveButton && (
|
||||
<Button
|
||||
text={positiveButton.text ?? 'Yes'}
|
||||
color='primary'
|
||||
className='px-6'
|
||||
rightIcon={positiveButton.icon ?? <YesIcon />}
|
||||
onClick={() => handleButtonClick(positiveButton)}
|
||||
/>
|
||||
)}
|
||||
<div className='flex flex-row-reverse gap-4'>
|
||||
{positiveButton && (
|
||||
<Button
|
||||
text={positiveButton.text ?? 'Yes'}
|
||||
color='tertiary'
|
||||
className='px-6'
|
||||
rightIcon={positiveButton.icon ?? <YesIcon />}
|
||||
onClick={() => handleButtonClick(positiveButton)}
|
||||
/>
|
||||
)}
|
||||
{checkbox && (
|
||||
<Checkbox checked={toggle} onChange={handleCheckboxClick} text={checkbox.text} />
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
text={negativeButton?.text ?? 'No'}
|
||||
color='secondary'
|
||||
|
@ -20,6 +20,7 @@ interface Props {
|
||||
assets: Asset[] | BorrowAsset[]
|
||||
selectedDenoms: string[]
|
||||
onChangeSelected: (denoms: string[]) => void
|
||||
isBorrow: boolean
|
||||
}
|
||||
|
||||
export default function AssetSelectTable(props: Props) {
|
||||
@ -39,7 +40,7 @@ export default function AssetSelectTable(props: Props) {
|
||||
const [sorting, setSorting] = useState<SortingState>([{ id: 'symbol', desc: false }])
|
||||
const [selected, setSelected] = useState<RowSelectionState>(defaultSelected)
|
||||
const balances = useStore((s) => s.balances)
|
||||
const columns = useAssetTableColumns()
|
||||
const columns = useAssetTableColumns(props.isBorrow)
|
||||
const tableData: AssetTableRow[] = useMemo(() => {
|
||||
return props.assets.map((asset) => {
|
||||
const balancesForAsset = balances.find(byDenom(asset.denom))
|
||||
@ -79,7 +80,7 @@ export default function AssetSelectTable(props: Props) {
|
||||
|
||||
return (
|
||||
<table className='w-full'>
|
||||
<thead className='border-b border-white/5'>
|
||||
<thead className='border-b border-white/10'>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
|
@ -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<ColumnDef<AssetTableRow>[]>(
|
||||
() => [
|
||||
{
|
||||
@ -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 (
|
||||
<div className='flex items-center'>
|
||||
<Checkbox checked={row.getIsSelected()} onChange={row.getToggleSelectedHandler()} />
|
||||
@ -39,7 +42,7 @@ export default function useAssetTableColumns() {
|
||||
</Text>
|
||||
{showRate && market ? (
|
||||
<AssetRate
|
||||
rate={convertAprToApy(market.borrowRate * 100, 365)}
|
||||
rate={apy}
|
||||
isEnabled={market.borrowEnabled}
|
||||
className='text-xs'
|
||||
type='apy'
|
||||
@ -85,6 +88,6 @@ export default function useAssetTableColumns() {
|
||||
},
|
||||
},
|
||||
],
|
||||
[],
|
||||
[isBorrow],
|
||||
)
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import { getDepositAndLendCoinsToSpend } from 'hooks/useUpdatedAccount/functions
|
||||
import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { formatPercent, formatValue } from 'utils/formatters'
|
||||
import { formatPercent } from 'utils/formatters'
|
||||
import { BN } from 'utils/helpers'
|
||||
import { getDebtAmountWithInterest } from 'utils/tokens'
|
||||
|
||||
@ -204,15 +204,32 @@ function BorrowModal(props: Props) {
|
||||
title={formatPercent(modal.marketData.borrowRate || '0')}
|
||||
sub={'Borrow rate'}
|
||||
/>
|
||||
<div className='h-100 w-[1px] bg-white/10' />
|
||||
<TitleAndSubCell
|
||||
title={formatValue(getDebtAmount(modal), {
|
||||
abbreviated: false,
|
||||
decimals: asset.decimals,
|
||||
maxDecimals: asset.decimals,
|
||||
})}
|
||||
sub={'Borrowed'}
|
||||
/>
|
||||
{totalDebt.isGreaterThan(0) && (
|
||||
<>
|
||||
<div className='h-100 w-[1px] bg-white/10' />
|
||||
<div className='flex flex-col gap-0.5'>
|
||||
<div className='flex gap-2'>
|
||||
<FormattedNumber
|
||||
className='text-xs'
|
||||
amount={totalDebt.toNumber()}
|
||||
options={{
|
||||
decimals: asset.decimals,
|
||||
abbreviated: false,
|
||||
suffix: ` ${asset.symbol}`,
|
||||
}}
|
||||
/>
|
||||
<DisplayCurrency
|
||||
className='text-xs'
|
||||
coin={BNCoin.fromDenomAndBigNumber(asset.denom, totalDebt)}
|
||||
parentheses
|
||||
/>
|
||||
</div>
|
||||
<Text size='xs' className='text-white/50' tag='span'>
|
||||
Borrowed
|
||||
</Text>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className='h-100 w-[1px] bg-white/10' />
|
||||
<div className='flex flex-col gap-0.5'>
|
||||
<div className='flex gap-2'>
|
||||
|
@ -33,7 +33,9 @@ export default function FundAndWithdrawModal() {
|
||||
{modal && currentAccount ? (
|
||||
<FundWithdrawModalContent account={currentAccount} isFunding={isFunding} />
|
||||
) : (
|
||||
<CircularProgress />
|
||||
<div className='flex items-center justify-center w-full h-[380px]'>
|
||||
<CircularProgress />
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
)
|
||||
|
@ -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(
|
||||
|
@ -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<number>(0)
|
||||
const [inputRef, setInputRef] = useState<React.RefObject<HTMLInputElement>>()
|
||||
const [isCustom, setIsCustom] = useState(false)
|
||||
const [displayCurrency, setDisplayCurrency] = useLocalStorage<string>(
|
||||
DISPLAY_CURRENCY_KEY,
|
||||
LocalStorageKeys.DISPLAY_CURRENCY,
|
||||
DEFAULT_SETTINGS.displayCurrency,
|
||||
)
|
||||
const [preferredAsset, setPreferredAsset] = useLocalStorage<string>(
|
||||
PREFERRED_ASSET_KEY,
|
||||
DEFAULT_SETTINGS.preferredAsset,
|
||||
)
|
||||
const [reduceMotion, setReduceMotion] = useLocalStorage<boolean>(
|
||||
REDUCE_MOTION_KEY,
|
||||
LocalStorageKeys.REDUCE_MOTION,
|
||||
DEFAULT_SETTINGS.reduceMotion,
|
||||
)
|
||||
const [tutorial, setTutorial] = useLocalStorage<boolean>(TUTORIAL_KEY, DEFAULT_SETTINGS.tutorial)
|
||||
const [tutorial, setTutorial] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.TUTORIAL,
|
||||
DEFAULT_SETTINGS.tutorial,
|
||||
)
|
||||
const [lendAssets, setLendAssets] = useLocalStorage<boolean>(
|
||||
LEND_ASSETS_KEY,
|
||||
LocalStorageKeys.LEND_ASSETS,
|
||||
DEFAULT_SETTINGS.lendAssets,
|
||||
)
|
||||
const [slippage, setSlippage] = useLocalStorage<number>(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
|
||||
const [slippage, setSlippage] = useLocalStorage<number>(
|
||||
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: [
|
||||
<div className='flex w-full gap-2' key={index}>
|
||||
<AssetImage asset={asset} size={16} />
|
||||
<Text size='sm' className='leading-4'>
|
||||
{asset.symbol}
|
||||
</Text>
|
||||
</div>,
|
||||
],
|
||||
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() {
|
||||
</div>
|
||||
),
|
||||
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
|
||||
/>
|
||||
<SettingsOptions
|
||||
label='Preferred asset'
|
||||
description='By selecting a different asset you always have the trading pair or asset selector
|
||||
pre-filled with this asset.'
|
||||
label='Display Currency'
|
||||
description='Convert all values to the selected asset/currency.'
|
||||
className='pb-6'
|
||||
>
|
||||
<Select
|
||||
label='Global'
|
||||
options={preferredAssetsOptions}
|
||||
defaultValue={preferredAsset}
|
||||
onChange={handlePreferredAsset}
|
||||
className='relative border w-60 rounded-base border-white/10'
|
||||
containerClassName='justify-end mb-4'
|
||||
/>
|
||||
<Select
|
||||
label='Display Currency'
|
||||
options={displayCurrenciesOptions}
|
||||
defaultValue={displayCurrency}
|
||||
onChange={handleDisplayCurrency}
|
||||
|
@ -8,7 +8,7 @@ import VaultDeposit from 'components/Modals/Vault/VaultDeposits'
|
||||
import VaultDepositSubTitle from 'components/Modals/Vault/VaultDepositsSubTitle'
|
||||
import Text from 'components/Text'
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { DISPLAY_CURRENCY_KEY } from 'constants/localStore'
|
||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useDepositVault from 'hooks/broadcast/useDepositVault'
|
||||
import useDisplayAsset from 'hooks/useDisplayAsset'
|
||||
@ -36,7 +36,7 @@ export default function VaultModalContent(props: Props) {
|
||||
|
||||
const { data: prices } = usePrices()
|
||||
const [displayCurrency] = useLocalStorage<string>(
|
||||
DISPLAY_CURRENCY_KEY,
|
||||
LocalStorageKeys.DISPLAY_CURRENCY,
|
||||
DEFAULT_SETTINGS.displayCurrency,
|
||||
)
|
||||
const [isOpen, toggleOpen] = useIsOpenArray(2, false)
|
||||
|
@ -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<string[]>(
|
||||
currentSelectedDenom.filter((denom) => filteredAssets.findIndex(byDenom(denom)) || []),
|
||||
@ -58,6 +59,7 @@ export default function WalletAssetsModalContent(props: Props) {
|
||||
</div>
|
||||
<div className='max-h-[446px] overflow-y-scroll scrollbar-hide'>
|
||||
<AssetSelectTable
|
||||
isBorrow={isBorrow}
|
||||
assets={filteredAssets}
|
||||
onChangeSelected={onChangeSelect}
|
||||
selectedDenoms={selectedDenoms}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user