Compare commits

..
Author SHA1 Message Date
Linkie Link 2c7c0e99e0 fix: fixe portfolio and intro wording 2024-02-21 08:08:40 +01:00
Linkie Link 1c3e4de4dd fix: fix interactions 2024-02-19 15:01:04 +01:00
Linkie Link 65ff38f117 feat: implemented wallet-mode 2024-02-19 14:46:15 +01:00
Linkie Link 1a45d64bac fix: fix getUrl 2024-02-19 13:10:24 +01:00
Linkie Link 89d41fa5cf fix: isNaN Leverage 2024-02-19 12:38:04 +01:00
Linkie Link df7ef30a43 Merge branch 'main' of https://github.com/mars-protocol/mars-v2-frontend into v1-implementation 2024-02-19 09:28:25 +01:00
Linkie Link 40224ad201 feat: list DYDX (#815) 2024-02-16 08:58:12 +01:00
Linkie Link 21bedd7905 feat: added borrow and repay 2024-02-15 21:51:00 +01:00
Linkie Link 37be3240eb feat: finished deposit and withdraw 2024-02-15 20:40:22 +01:00
Linkie Link f609540809 feat: prepared everything for modal interaction 2024-02-15 16:22:20 +01:00
Linkie Link 106de661a8 feat: added v1 based buttons and a portfolio summary 2024-02-15 16:08:04 +01:00
Linkie Link 40593ae987 Merge branch 'develop' of https://github.com/mars-protocol/mars-v2-frontend into v1-implementation
# Conflicts:
#	src/components/borrow/Table/Columns/useDepositedColumns.tsx
#	src/components/borrow/Table/DepositedBorrowingsTable.tsx
#	src/components/earn/lend/Table/DepositedLendsTable.tsx
2024-02-15 14:11:12 +01:00
Linkie LinkandBob van der Helm 0e6f0edb5d Docker update (#814)
* feat: handle URLs with or without trailing slash (#803)

* feat: handle URLs with or without trailing slash

* tidy: cleanup slashes

* Fix docker build (#805)

* fix: fixed the docker build

* tidy: cleanup

* env: remove env contents (#808)

* Portfolio fix (#809)

* fix: fixed the portfolio account detail page layout

* fix: fixed portfolio cards

* tidy: refactor

* align buttons, perps row clickable (#807)

* align buttons, perps row clickable

* fix comments

* update to v2.2.4

* fix borrowbutton logic, add vault deposit manage btn, fix icon size vault modal

---------

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>

* Update .env.production

* Update Dockerbuild (#813)

* v2.2.4 (#810)

* feat: handle URLs with or without trailing slash (#803)

* feat: handle URLs with or without trailing slash

* tidy: cleanup slashes

* Fix docker build (#805)

* fix: fixed the docker build

* tidy: cleanup

* env: remove env contents (#808)

* Portfolio fix (#809)

* fix: fixed the portfolio account detail page layout

* fix: fixed portfolio cards

* tidy: refactor

* align buttons, perps row clickable (#807)

* align buttons, perps row clickable

* fix comments

* update to v2.2.4

* fix borrowbutton logic, add vault deposit manage btn, fix icon size vault modal

---------

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>

---------

Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com>

* Update Dockerfile

* remove   .env files

* tidy: format

---------

Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com>

---------

Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com>
2024-02-15 10:49:14 +01:00
Linkie Link e3b5e330ee Merge branch 'main' of https://github.com/mars-protocol/mars-v2-frontend into develop
# Conflicts:
#	.env
#	src/components/perps/BalancesTable/index.tsx
2024-02-14 19:30:55 +01:00
Linkie LinkandBob van der Helm 7eac8e7a35 Update Dockerbuild (#813)
* v2.2.4 (#810)

* feat: handle URLs with or without trailing slash (#803)

* feat: handle URLs with or without trailing slash

* tidy: cleanup slashes

* Fix docker build (#805)

* fix: fixed the docker build

* tidy: cleanup

* env: remove env contents (#808)

* Portfolio fix (#809)

* fix: fixed the portfolio account detail page layout

* fix: fixed portfolio cards

* tidy: refactor

* align buttons, perps row clickable (#807)

* align buttons, perps row clickable

* fix comments

* update to v2.2.4

* fix borrowbutton logic, add vault deposit manage btn, fix icon size vault modal

---------

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>

---------

Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com>

* Update Dockerfile

* remove   .env files

* tidy: format

---------

Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com>
2024-02-14 19:29:41 +01:00
Linkie Link af478c56d5 Update .env.production 2024-02-14 16:43:57 +01:00
Linkie LinkandBob van der Helm c08ee627fc v2.2.4 (#810)
* feat: handle URLs with or without trailing slash (#803)

* feat: handle URLs with or without trailing slash

* tidy: cleanup slashes

* Fix docker build (#805)

* fix: fixed the docker build

* tidy: cleanup

* env: remove env contents (#808)

* Portfolio fix (#809)

* fix: fixed the portfolio account detail page layout

* fix: fixed portfolio cards

* tidy: refactor

* align buttons, perps row clickable (#807)

* align buttons, perps row clickable

* fix comments

* update to v2.2.4

* fix borrowbutton logic, add vault deposit manage btn, fix icon size vault modal

---------

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>

---------

Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com>
2024-02-14 15:01:25 +01:00
Bob van der HelmandLinkie Link a6f4c24f15 align buttons, perps row clickable (#807)
* align buttons, perps row clickable

* fix comments

* update to v2.2.4

* fix borrowbutton logic, add vault deposit manage btn, fix icon size vault modal

---------

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>
2024-02-14 14:49:15 +01:00
Linkie Link 6946ceddfc Portfolio fix (#809)
* fix: fixed the portfolio account detail page layout

* fix: fixed portfolio cards

* tidy: refactor
2024-02-14 14:18:10 +01:00
Linkie Link 0154065ffb env: remove env contents (#808) 2024-02-14 12:33:10 +01:00
Linkie Link 4f64234a75 Merge branch 'main' into develop 2024-02-14 12:01:10 +01:00
Linkie Link 26f1ef4a2c Fix docker build (#805)
* fix: fixed the docker build

* tidy: cleanup
2024-02-14 11:58:48 +01:00
Linkie Link f7f8136b38 feat: handle URLs with or without trailing slash (#803) (#804)
* feat: handle URLs with or without trailing slash

* tidy: cleanup slashes
2024-02-14 11:16:06 +01:00
Linkie Link 34db8aea1a tidy: streamline borrow 2024-02-14 11:14:40 +01:00
Linkie Link 75edb21c02 Merge branch 'develop' of https://github.com/mars-protocol/mars-v2-frontend into v1-implementation 2024-02-14 11:11:42 +01:00
Linkie Link 442b7a3a8c feat: implement v1 tables into v2 with data fetching 2024-02-14 11:11:34 +01:00
143 changed files with 2296 additions and 741 deletions
View File
-13
View File
@@ -1,13 +0,0 @@
# DO NOT EDIT THIS FILE WHEN USING DOCKER
# These values are used to replace the values in the built app,
# you should pass environment variables as defined in README.md
# CONFIG #
NEXT_PUBLIC_NETWORK=mainnet
# OSMOSIS-1 #
NEXT_PUBLIC_OSMOSIS_RPC=APP_NEXT_OSMOSIS_RPC
NEXT_PUBLIC_OSMOSIS_REST=APP_NEXT_OSMOSIS_REST
# WALLET CONNECT #
NEXT_PUBLIC_WALLET_CONNECT_ID=APP_NEXT_WALLET_CONNECT_ID
+8 -2
View File
@@ -6,18 +6,24 @@ RUN yarn install
COPY . .
RUN apk --update add patch
RUN patch next.config.js next-config.patch
ENV NEXT_PUBLIC_NETWORK=mainnet
ENV NEXT_PUBLIC_OSMOSIS_RPC=APP_NEXT_OSMOSIS_RPC
ENV NEXT_PUBLIC_OSMOSIS_REST=APP_NEXT_OSMOSIS_REST
ENV NEXT_PUBLIC_WALLET_CONNECT_ID=APP_NEXT_WALLET_CONNECT_ID
ENV NODE_ENV=production
RUN yarn build
FROM node:20-alpine as runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/package.json .
COPY --from=builder /app/yarn.lock .
COPY --from=builder /app/next.config.js .
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY entrypoint.sh .
RUN apk add --no-cache --upgrade bash
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mars-v2-frontend",
"version": "2.2.3",
"version": "2.2.4",
"homepage": "./",
"private": false,
"license": "SEE LICENSE IN LICENSE FILE",
+36
View File
@@ -0,0 +1,36 @@
<svg viewBox="0 0 240 240" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="240" height="240" rx="120" fill="#E50571" />
<path d="M149.107 60.9396L63.3276 181.702H89.6637L175.888 60.9396H149.107Z" fill="white" />
<path
d="M92.4053 60.9396L117.644 96.5239L104.476 115.934L65.5205 60.9396H92.4053Z"
fill="url(#paint0_linear_6269_3439)"
/>
<path
d="M151.664 181.712L123.682 142.354L136.851 123.483L178 181.712H151.664Z"
fill="url(#paint1_linear_6269_3439)"
/>
<defs>
<linearGradient
id="paint0_linear_6269_3439"
x1="86.3698"
y1="68.4878"
x2="121.753"
y2="111.957"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="white" />
<stop offset="1" stop-color="white" stop-opacity="0.75" />
</linearGradient>
<linearGradient
id="paint1_linear_6269_3439"
x1="157.7"
y1="172.547"
x2="114.305"
y2="113.014"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="white" stop-opacity="0.68" />
<stop offset="1" stop-color="white" stop-opacity="0.1" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+8 -1
View File
@@ -10,7 +10,12 @@ import {
TotalDepositResponse,
VaultConfigBaseForAddr,
} from 'types/generated/mars-params/MarsParams.types'
import { ArrayOfMarket } from 'types/generated/mars-red-bank/MarsRedBank.types'
import {
ArrayOfMarket,
ArrayOfUserCollateralResponse,
ArrayOfUserDebtResponse,
UserCollateralResponse,
} from 'types/generated/mars-red-bank/MarsRedBank.types'
interface Cache<T> extends Map<string, { data: T | null; timestamp: number }> {}
@@ -62,3 +67,5 @@ export const underlyingDebtCache: Cache<string> = new Map()
export const previewDepositCache: Cache<{ vaultAddress: string; amount: string }> = new Map()
export const stakingAprCache: Cache<StakingApr[]> = new Map()
export const assetParamsCache: Cache<AssetParamsBaseForAddr[]> = new Map()
export const userCollateralCache: Cache<ArrayOfUserCollateralResponse> = new Map()
export const userDebtCache: Cache<ArrayOfUserDebtResponse> = new Map()
+29 -8
View File
@@ -6,7 +6,9 @@ import { MarsMockVaultQueryClient } from 'types/generated/mars-mock-vault/MarsMo
import { MarsOracleOsmosisQueryClient } from 'types/generated/mars-oracle-osmosis/MarsOracleOsmosis.client'
import { MarsParamsQueryClient } from 'types/generated/mars-params/MarsParams.client'
import { MarsPerpsQueryClient } from 'types/generated/mars-perps/MarsPerps.client'
import { MarsRedBankQueryClient } from 'types/generated/mars-red-bank/MarsRedBank.client'
import { MarsSwapperOsmosisQueryClient } from 'types/generated/mars-swapper-osmosis/MarsSwapperOsmosis.client'
import { getUrl } from 'utils/url'
let _cosmWasmClient: Map<string, CosmWasmClient> = new Map()
let _creditManagerQueryClient: Map<string, MarsCreditManagerQueryClient> = new Map()
@@ -15,6 +17,7 @@ let _paramsQueryClient: Map<string, MarsParamsQueryClient> = new Map()
let _incentivesQueryClient: Map<string, MarsIncentivesQueryClient> = new Map()
let _swapperOsmosisClient: Map<string, MarsSwapperOsmosisQueryClient> = new Map()
let _perpsClient: Map<string, MarsPerpsQueryClient> = new Map()
let _redBankQueryClient: Map<string, MarsRedBankQueryClient> = new Map()
const getClient = async (rpc: string) => {
try {
@@ -32,7 +35,7 @@ const getClient = async (rpc: string) => {
const getCreditManagerQueryClient = async (chainConfig: ChainConfig) => {
try {
const contract = chainConfig.contracts.creditManager
const rpc = chainConfig.endpoints.rpc
const rpc = getUrl(chainConfig.endpoints.rpc)
const key = rpc + contract
if (!_creditManagerQueryClient.get(key)) {
@@ -49,7 +52,7 @@ const getCreditManagerQueryClient = async (chainConfig: ChainConfig) => {
const getParamsQueryClient = async (chainConfig: ChainConfig) => {
try {
const contract = chainConfig.contracts.params
const rpc = chainConfig.endpoints.rpc
const rpc = getUrl(chainConfig.endpoints.rpc)
const key = rpc + contract
if (!_paramsQueryClient.get(key)) {
@@ -66,7 +69,7 @@ const getParamsQueryClient = async (chainConfig: ChainConfig) => {
const getOracleQueryClient = async (chainConfig: ChainConfig) => {
try {
const contract = chainConfig.contracts.oracle
const rpc = chainConfig.endpoints.rpc
const rpc = getUrl(chainConfig.endpoints.rpc)
const key = rpc + contract
if (!_oracleQueryClient.get(key)) {
@@ -82,7 +85,7 @@ const getOracleQueryClient = async (chainConfig: ChainConfig) => {
const getVaultQueryClient = async (chainConfig: ChainConfig, address: string) => {
try {
const client = await getClient(chainConfig.endpoints.rpc)
const client = await getClient(getUrl(chainConfig.endpoints.rpc))
return new MarsMockVaultQueryClient(client, address)
} catch (error) {
throw error
@@ -92,7 +95,7 @@ const getVaultQueryClient = async (chainConfig: ChainConfig, address: string) =>
const getIncentivesQueryClient = async (chainConfig: ChainConfig) => {
try {
const contract = chainConfig.contracts.incentives
const rpc = chainConfig.endpoints.rpc
const rpc = getUrl(chainConfig.endpoints.rpc)
const key = rpc + contract
if (!_incentivesQueryClient.get(key)) {
const client = await getClient(rpc)
@@ -108,7 +111,7 @@ const getIncentivesQueryClient = async (chainConfig: ChainConfig) => {
const getSwapperQueryClient = async (chainConfig: ChainConfig) => {
try {
const contract = chainConfig.contracts.swapper
const rpc = chainConfig.endpoints.rpc
const rpc = getUrl(chainConfig.endpoints.rpc)
const key = rpc + contract
if (!_swapperOsmosisClient.get(key)) {
const client = await getClient(rpc)
@@ -124,7 +127,7 @@ const getSwapperQueryClient = async (chainConfig: ChainConfig) => {
const getPerpsQueryClient = async (chainConfig: ChainConfig) => {
try {
const contract = chainConfig.contracts.perps
const rpc = chainConfig.endpoints.rpc
const rpc = getUrl(chainConfig.endpoints.rpc)
const key = rpc + contract
if (!_perpsClient.get(key)) {
const client = await getClient(rpc)
@@ -137,13 +140,31 @@ const getPerpsQueryClient = async (chainConfig: ChainConfig) => {
}
}
const getRedBankQueryClient = async (chainConfig: ChainConfig) => {
try {
const contract = chainConfig.contracts.redBank
const rpc = getUrl(chainConfig.endpoints.rpc)
const key = rpc + contract
if (!_redBankQueryClient.get(key)) {
const client = await getClient(rpc)
_redBankQueryClient.set(key, new MarsRedBankQueryClient(client, contract))
}
return _redBankQueryClient.get(key)!
} catch (error) {
throw error
}
}
export {
getClient,
getCreditManagerQueryClient,
getIncentivesQueryClient,
getOracleQueryClient,
getParamsQueryClient,
getPerpsQueryClient,
getRedBankQueryClient,
getSwapperQueryClient,
getVaultQueryClient,
getPerpsQueryClient,
}
+41
View File
@@ -0,0 +1,41 @@
import { cacheFn, userCollateralCache, userDebtCache } from 'api/cache'
import { getRedBankQueryClient } from 'api/cosmwasm-client'
import { BNCoin } from 'types/classes/BNCoin'
import {
ArrayOfUserCollateralResponse,
ArrayOfUserDebtResponse,
} from 'types/generated/mars-red-bank/MarsRedBank.types'
export default async function getV1Positions(
chainConfig: ChainConfig,
user?: string,
): Promise<Account> {
if (!user) return new Promise((_, reject) => reject('No account Wallet ID found'))
const redBankQueryClient = await getRedBankQueryClient(chainConfig)
const userCollateral: ArrayOfUserCollateralResponse = await cacheFn(
() => redBankQueryClient.userCollaterals({ user: user, limit: 100 }),
userCollateralCache,
`${chainConfig.id}/v1/deposits/${user}`,
)
const userDebt: ArrayOfUserDebtResponse = await cacheFn(
() => redBankQueryClient.userDebts({ user: user, limit: 100 }),
userDebtCache,
`${chainConfig.id}/v1/debts/${user}`,
)
if (userCollateral && userDebt) {
return {
id: user,
debts: userDebt.map((debt) => new BNCoin(debt)),
lends: userCollateral.map((lend) => new BNCoin(lend)),
deposits: [],
vaults: [],
perps: [],
kind: 'default',
}
}
return new Promise((_, reject) => reject('No account found'))
}
@@ -10,12 +10,12 @@ import Text from 'components/common/Text'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import AssetImage from 'components/common/assets/AssetImage'
import { BN_ZERO } from 'constants/math'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { BN } from 'utils/helpers'
interface Props {
account: Account
asset: Asset
title: string
coinBalances: BNCoin[]
@@ -29,6 +29,7 @@ interface Props {
export default function AssetAmountSelectActionModal(props: Props) {
const {
account,
asset,
title,
coinBalances,
@@ -41,7 +42,6 @@ export default function AssetAmountSelectActionModal(props: Props) {
} = props
const [amount, setAmount] = useState(BN_ZERO)
const maxAmount = BN(coinBalances.find(byDenom(asset.denom))?.amount ?? 0)
const account = useCurrentAccount()
const handleAmountChange = useCallback(
(value: BigNumber) => {
setAmount(value)
@@ -54,7 +54,6 @@ export default function AssetAmountSelectActionModal(props: Props) {
onAction(amount, amount.isEqualTo(maxAmount))
}, [amount, maxAmount, onAction])
if (!account) return
return (
<Modal
onClose={onClose}
+43 -55
View File
@@ -35,18 +35,9 @@ interface Props {
modal: BorrowModal
}
function getDebtAmount(modal: BorrowModal) {
return BN((modal.marketData as BorrowMarketTableData)?.debt ?? 0).toString()
}
function getAssetLogo(modal: BorrowModal) {
if (!modal.asset) return null
return <AssetImage asset={modal.asset} size={24} />
}
function RepayNotAvailable(props: { asset: Asset; repayFromWallet: boolean }) {
return (
<Card className='mt-6'>
<Card className='w-full'>
<div className='flex items-start p-4'>
<InfoCircle className='w-6 mr-2 flex-0' />
<div className='flex flex-col flex-1 gap-1'>
@@ -90,7 +81,6 @@ function BorrowModal(props: Props) {
const apy = modal.marketData.apy.borrow
const isAutoLendEnabled = autoLendEnabledAccountIds.includes(account.id)
const { computeMaxBorrowAmount } = useHealthComputer(account)
const totalDebt = BN(getDebtAmount(modal))
const accountDebt = account.debts.find(byDenom(asset.denom))?.amount ?? BN_ZERO
const markets = useMarkets()
@@ -237,7 +227,7 @@ function BorrowModal(props: Props) {
onClose={onClose}
header={
<span className='flex items-center gap-4 px-4'>
{getAssetLogo(modal)}
<AssetImage asset={asset} size={24} />
<Text>
{isRepay ? 'Repay' : 'Borrow'} {asset.symbol}
</Text>
@@ -251,14 +241,14 @@ function BorrowModal(props: Props) {
title={formatPercent(modal.marketData.apy.borrow)}
sub={'Borrow Rate APY'}
/>
{totalDebt.isGreaterThan(0) && (
{accountDebt.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()}
amount={accountDebt.toNumber()}
options={{
decimals: asset.decimals,
abbreviated: false,
@@ -267,7 +257,7 @@ function BorrowModal(props: Props) {
/>
<DisplayCurrency
className='text-xs'
coin={BNCoin.fromDenomAndBigNumber(asset.denom, totalDebt)}
coin={BNCoin.fromDenomAndBigNumber(asset.denom, accountDebt)}
parentheses
/>
</div>
@@ -303,59 +293,57 @@ function BorrowModal(props: Props) {
<div className='flex items-start flex-1 gap-6 p-6'>
<Card
className='flex flex-1 p-4 bg-white/5'
contentClassName='gap-6 flex flex-col justify-between h-full min-h-[380px]'
contentClassName='gap-6 flex flex-col justify-between h-full'
>
<div className='flex flex-wrap w-full'>
<TokenInputWithSlider
asset={asset}
onChange={handleChange}
onDebounce={onDebounce}
amount={amount}
max={max}
disabled={max.isZero()}
className='w-full'
maxText='Max'
warningMessages={[]}
/>
{isRepay && maxRepayAmount.isZero() && (
<RepayNotAvailable asset={asset} repayFromWallet={repayFromWallet} />
)}
{isRepay ? (
<>
<Divider className='my-6' />
<TokenInputWithSlider
asset={asset}
onChange={handleChange}
onDebounce={onDebounce}
amount={amount}
max={max}
disabled={max.isZero()}
className='w-full'
maxText='Max'
warningMessages={[]}
/>
{isRepay && maxRepayAmount.isZero() && (
<RepayNotAvailable asset={asset} repayFromWallet={repayFromWallet} />
)}
{isRepay ? (
<>
<Divider />
<div className='flex items-center w-full'>
<div className='flex flex-wrap flex-1'>
<Text className='w-full mb-1'>Repay from Wallet</Text>
<Text size='xs' className='text-white/50'>
Repay your debt directly from your wallet
</Text>
</div>
<div className='flex flex-wrap items-center justify-end'>
<Switch
name='borrow-to-wallet'
checked={repayFromWallet}
onChange={setRepayFromWallet}
/>
</div>
</>
) : (
<>
<Divider className='my-6' />
<Switch
name='borrow-to-wallet'
checked={repayFromWallet}
onChange={setRepayFromWallet}
/>
</div>
</>
) : (
<>
<Divider />
<div className='flex items-center w-full'>
<div className='flex flex-wrap flex-1'>
<Text className='w-full mb-1'>Receive funds to Wallet</Text>
<Text size='xs' className='text-white/50'>
Your borrowed funds will directly go to your wallet
</Text>
</div>
<div className='flex flex-wrap items-center justify-end'>
<Switch
name='borrow-to-wallet'
checked={borrowToWallet}
onChange={setBorrowToWallet}
/>
</div>
</>
)}
</div>
<Switch
name='borrow-to-wallet'
checked={borrowToWallet}
onChange={setBorrowToWallet}
/>
</div>
</>
)}
<Button
onClick={onConfirmClick}
className='w-full'
@@ -23,6 +23,7 @@ interface Props {
}
function LendAndReclaimModal({ currentAccount, config }: Props) {
const account = useCurrentAccount()
const lend = useStore((s) => s.lend)
const reclaim = useStore((s) => s.reclaim)
const { close } = useLendAndReclaimModal()
@@ -65,8 +66,11 @@ function LendAndReclaimModal({ currentAccount, config }: Props) {
},
[asset.denom, close, currentAccount.id, isLendAction, lend, reclaim],
)
if (!account) return null
return (
<AssetAmountSelectActionModal
account={account}
asset={asset}
contentHeader={<DetailsHeader data={data} />}
coinBalances={coinBalances}
@@ -10,6 +10,8 @@ import {
LendAndReclaimModalController,
SettingsModal,
UnlockModal,
V1BorrowAndRepay,
V1DepositAndWithdraw,
VaultModal,
WalletAssets,
WithdrawFromVaultsModal,
@@ -32,6 +34,8 @@ export default function ModalsContainer() {
<AlertDialogController />
<HlsModal />
<HlsManageModal />
<V1DepositAndWithdraw />
<V1BorrowAndRepay />
</>
)
}
+4 -2
View File
@@ -4,11 +4,13 @@ export { default as AlertDialogController } from 'components/Modals/AlertDialog'
export { default as BorrowModal } from 'components/Modals/BorrowModal'
export { default as FundAndWithdrawModal } from 'components/Modals/FundWithdraw'
export { default as GetStartedModal } from 'components/Modals/GetStartedModal'
export { default as HlsModal } from 'components/Modals/HLS'
export { default as HlsManageModal } from 'components/Modals/HLS/Manage'
export { default as LendAndReclaimModalController } from 'components/Modals/LendAndReclaim'
export { default as SettingsModal } from 'components/Modals/Settings'
export { default as UnlockModal } from 'components/Modals/Unlock'
export { default as VaultModal } from 'components/Modals/Vault'
export { default as WalletAssets } from 'components/Modals/WalletAssets'
export { default as WithdrawFromVaultsModal } from 'components/Modals/WithdrawFromVaultsModal'
export { default as HlsModal } from 'components/Modals/HLS'
export { default as HlsManageModal } from 'components/Modals/HLS/Manage'
export { default as V1BorrowAndRepay } from 'components/Modals/v1/V1BorrowAndRepay'
export { default as V1DepositAndWithdraw } from 'components/Modals/v1/V1DepositAndWithdraw'
+179
View File
@@ -0,0 +1,179 @@
import BigNumber from 'bignumber.js'
import { useCallback, useEffect, useMemo, useState } from 'react'
import Modal from 'components/Modals/Modal'
import AccountSummaryInModal from 'components/account/AccountSummary/AccountSummaryInModal'
import Button from 'components/common/Button'
import Card from 'components/common/Card'
import DisplayCurrency from 'components/common/DisplayCurrency'
import Divider from 'components/common/Divider'
import { FormattedNumber } from 'components/common/FormattedNumber'
import { ArrowRight } from 'components/common/Icons'
import Text from 'components/common/Text'
import TitleAndSubCell from 'components/common/TitleAndSubCell'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import AssetImage from 'components/common/assets/AssetImage'
import { BN_ZERO } from 'constants/math'
import useBaseAsset from 'hooks/assets/useBasetAsset'
import useHealthComputer from 'hooks/useHealthComputer'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { formatPercent } from 'utils/formatters'
import { getDebtAmountWithInterest } from 'utils/tokens'
interface Props {
account: Account
}
export default function Borrow(props: Props) {
const { account } = props
const modal = useStore((s) => s.v1BorrowAndRepayModal)
const baseAsset = useBaseAsset()
const [amount, setAmount] = useState(BN_ZERO)
const v1Action = useStore((s) => s.v1Action)
const asset = modal?.data.asset ?? baseAsset
const [max, setMax] = useState(BN_ZERO)
const { simulateBorrow } = useUpdatedAccount(account)
const apy = modal?.data.apy.borrow ?? 0
const { computeMaxBorrowAmount } = useHealthComputer(account)
const accountDebt = modal?.data.accountDebtAmount ?? BN_ZERO
const accountDebtWithInterest = useMemo(
() => getDebtAmountWithInterest(accountDebt, apy),
[accountDebt, apy],
)
const close = useCallback(() => {
setAmount(BN_ZERO)
useStore.setState({ v1BorrowAndRepayModal: null })
}, [setAmount])
const onConfirmClick = useCallback(() => {
v1Action('borrow', BNCoin.fromDenomAndBigNumber(asset.denom, amount))
close()
}, [v1Action, asset, amount, close])
const handleChange = useCallback(
(newAmount: BigNumber) => {
if (!amount.isEqualTo(newAmount)) setAmount(newAmount)
},
[amount, setAmount],
)
const onDebounce = useCallback(() => {
const borrowCoin = BNCoin.fromDenomAndBigNumber(
asset.denom,
amount.isGreaterThan(max) ? max : amount,
)
simulateBorrow('wallet', borrowCoin)
}, [amount, max, asset, simulateBorrow])
const maxBorrow = useMemo(() => {
const maxBorrowAmount = computeMaxBorrowAmount(asset.denom, 'wallet')
return BigNumber.min(maxBorrowAmount, modal?.data.liquidity || 0)
}, [asset.denom, computeMaxBorrowAmount, modal?.data.liquidity])
useEffect(() => {
if (maxBorrow.isEqualTo(max)) return
setMax(maxBorrow)
}, [account, maxBorrow, max])
useEffect(() => {
if (amount.isLessThanOrEqualTo(max)) return
handleChange(max)
setAmount(max)
}, [amount, max, handleChange])
if (!modal) return null
return (
<Modal
onClose={close}
header={
<span className='flex items-center gap-4 px-4'>
<AssetImage asset={modal.data.asset} size={24} />
<Text>{`Borrow ${asset.symbol} from the Red Bank`}</Text>
</span>
}
headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b'
contentClassName='flex flex-col'
>
<div className='flex gap-3 px-6 py-4 border-b border-white/5 gradient-header'>
<TitleAndSubCell title={formatPercent(apy)} sub={'Borrow Rate APY'} />
{accountDebt.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={accountDebt.toNumber()}
options={{
decimals: asset.decimals,
abbreviated: false,
suffix: ` ${asset.symbol}`,
}}
/>
<DisplayCurrency
className='text-xs'
coin={BNCoin.fromDenomAndBigNumber(asset.denom, accountDebt)}
parentheses
/>
</div>
<Text size='xs' className='text-white/50' tag='span'>
Total 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'>
<FormattedNumber
className='text-xs'
amount={modal.data.liquidity.toNumber() ?? 0}
options={{ decimals: asset.decimals, abbreviated: true, suffix: ` ${asset.symbol}` }}
animate
/>
<DisplayCurrency
className='text-xs'
coin={BNCoin.fromDenomAndBigNumber(asset.denom, modal.data.liquidity ?? BN_ZERO)}
parentheses
/>
</div>
<Text size='xs' className='text-white/50' tag='span'>
Liquidity available
</Text>
</div>
</div>
<div className='flex items-start flex-1 gap-6 p-6'>
<Card
className='flex flex-1 p-4 bg-white/5'
contentClassName='gap-6 flex flex-col justify-between h-full'
>
<TokenInputWithSlider
asset={asset}
onChange={handleChange}
onDebounce={onDebounce}
amount={amount}
max={max}
disabled={max.isZero()}
className='w-full'
maxText='Max'
warningMessages={[]}
/>
<Divider />
<Button
onClick={onConfirmClick}
className='w-full'
disabled={amount.isZero()}
text={`Borrow ${asset.symbol}`}
rightIcon={<ArrowRight />}
/>
</Card>
<AccountSummaryInModal account={account} />
</div>
</Modal>
)
}
+82
View File
@@ -0,0 +1,82 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import WalletBridges from 'components/Wallet/WalletBridges'
import { BN_ZERO } from 'constants/math'
import useBaseAsset from 'hooks/assets/useBasetAsset'
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import useWalletBalances from 'hooks/useWalletBalances'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { defaultFee } from 'utils/constants'
import { BN } from 'utils/helpers'
import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal'
import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader'
interface Props {
account: Account
}
export default function Deposit(props: Props) {
const { account } = props
const baseAsset = useBaseAsset()
const modal = useStore((s) => s.v1DepositAndWithdrawModal)
const address = useStore((s) => s.address)
const asset = modal?.data.asset ?? baseAsset
const [fundingAsset, setFundingAsset] = useState<BNCoin>(
BNCoin.fromDenomAndBigNumber(modal?.data.asset.denom ?? baseAsset.denom, BN_ZERO),
)
const { data: walletBalances } = useWalletBalances(address)
const { simulateDeposits } = useUpdatedAccount(account)
const balance = useCurrentWalletBalance(asset.denom)
const v1Action = useStore((s) => s.v1Action)
const baseBalance = useMemo(
() => walletBalances.find(byDenom(baseAsset.denom))?.amount ?? '0',
[walletBalances, baseAsset],
)
const close = useCallback(() => {
useStore.setState({ v1DepositAndWithdrawModal: null })
}, [])
const handleClick = useCallback(async () => {
v1Action('deposit', fundingAsset)
close()
}, [v1Action, fundingAsset, close])
useEffect(() => {
if (BN(baseBalance).isLessThan(defaultFee.amount[0].amount)) {
useStore.setState({ focusComponent: { component: <WalletBridges /> } })
}
}, [baseBalance])
const onDebounce = useCallback(() => {
simulateDeposits('lend', [fundingAsset])
}, [fundingAsset, simulateDeposits])
const handleAmountChange = useCallback(
(value: BigNumber) => {
setFundingAsset(BNCoin.fromDenomAndBigNumber(asset.denom, value))
},
[asset.denom],
)
if (!modal) return
return (
<AssetAmountSelectActionModal
account={account}
asset={asset}
contentHeader={<DetailsHeader data={modal.data} />}
coinBalances={balance ? [BNCoin.fromCoin(balance)] : []}
actionButtonText={`Deposit ${asset.symbol}`}
title={`Deposit ${asset.symbol} into the Red Bank`}
onClose={close}
onAction={handleClick}
onChange={handleAmountChange}
onDebounce={onDebounce}
/>
)
}
+208
View File
@@ -0,0 +1,208 @@
import BigNumber from 'bignumber.js'
import { useCallback, useEffect, useMemo, useState } from 'react'
import Modal from 'components/Modals/Modal'
import AccountSummaryInModal from 'components/account/AccountSummary/AccountSummaryInModal'
import Button from 'components/common/Button'
import Card from 'components/common/Card'
import DisplayCurrency from 'components/common/DisplayCurrency'
import Divider from 'components/common/Divider'
import { FormattedNumber } from 'components/common/FormattedNumber'
import { ArrowRight, InfoCircle } from 'components/common/Icons'
import Text from 'components/common/Text'
import TitleAndSubCell from 'components/common/TitleAndSubCell'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import AssetImage from 'components/common/assets/AssetImage'
import { BN_ZERO } from 'constants/math'
import useBaseAsset from 'hooks/assets/useBasetAsset'
import useMarkets from 'hooks/markets/useMarkets'
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { formatPercent } from 'utils/formatters'
import { BN } from 'utils/helpers'
import { getDebtAmountWithInterest } from 'utils/tokens'
interface Props {
account: Account
}
function RepayNotAvailable(props: { asset: Asset }) {
return (
<Card className='w-full'>
<div className='flex items-start p-4'>
<InfoCircle className='w-6 mr-2 flex-0' />
<div className='flex flex-col flex-1 gap-1'>
<Text size='sm'>No funds for repay</Text>
<Text
size='xs'
className='text-white/40'
>{`Unfortunately you don't have any ${props.asset.symbol} in your Wallet to repay the debt.`}</Text>
</div>
</div>
</Card>
)
}
export default function Repay(props: Props) {
const { account } = props
const modal = useStore((s) => s.v1BorrowAndRepayModal)
const baseAsset = useBaseAsset()
const asset = modal?.data.asset ?? baseAsset
const [amount, setAmount] = useState(BN_ZERO)
const balance = useCurrentWalletBalance(asset.denom)
const v1Action = useStore((s) => s.v1Action)
const [max, setMax] = useState(BN_ZERO)
const { simulateRepay } = useUpdatedAccount(account)
const apy = modal?.data.apy.borrow ?? 0
const accountDebt = modal?.data.accountDebtAmount ?? BN_ZERO
const markets = useMarkets()
const accountDebtWithInterest = useMemo(
() => getDebtAmountWithInterest(accountDebt, apy),
[accountDebt, apy],
)
const overpayExeedsCap = useMemo(() => {
const marketAsset = markets.find((market) => market.asset.denom === asset.denom)
if (!marketAsset) return
const overpayAmount = accountDebtWithInterest.minus(accountDebt)
const marketCapAfterOverpay = marketAsset.cap.used.plus(overpayAmount)
return marketAsset.cap.max.isLessThanOrEqualTo(marketCapAfterOverpay)
}, [markets, asset.denom, accountDebt, accountDebtWithInterest])
const maxRepayAmount = useMemo(() => {
const maxBalance = BN(balance?.amount ?? 0)
return BigNumber.min(maxBalance, overpayExeedsCap ? accountDebt : accountDebtWithInterest)
}, [accountDebtWithInterest, overpayExeedsCap, accountDebt, balance?.amount])
const close = useCallback(() => {
setAmount(BN_ZERO)
useStore.setState({ v1BorrowAndRepayModal: null })
}, [setAmount])
const onConfirmClick = useCallback(() => {
v1Action('repay', BNCoin.fromDenomAndBigNumber(asset.denom, amount))
close()
}, [v1Action, asset, amount, close])
const handleChange = useCallback(
(newAmount: BigNumber) => {
if (!amount.isEqualTo(newAmount)) setAmount(newAmount)
},
[amount, setAmount],
)
const onDebounce = useCallback(() => {
const repayCoin = BNCoin.fromDenomAndBigNumber(
asset.denom,
amount.isGreaterThan(accountDebt) ? accountDebt : amount,
)
simulateRepay(repayCoin, true)
}, [amount, accountDebt, asset, simulateRepay])
useEffect(() => {
if (maxRepayAmount.isEqualTo(max)) return
setMax(maxRepayAmount)
}, [max, maxRepayAmount])
useEffect(() => {
if (amount.isLessThanOrEqualTo(max)) return
handleChange(max)
setAmount(max)
}, [amount, max, handleChange])
if (!modal) return null
return (
<Modal
onClose={close}
header={
<span className='flex items-center gap-4 px-4'>
<AssetImage asset={modal.data.asset} size={24} />
<Text>
{'Repay'} {asset.symbol}
</Text>
</span>
}
headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b'
contentClassName='flex flex-col'
>
<div className='flex gap-3 px-6 py-4 border-b border-white/5 gradient-header'>
<TitleAndSubCell title={formatPercent(apy)} sub={'Borrow Rate APY'} />
<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={accountDebt.toNumber()}
options={{
decimals: asset.decimals,
abbreviated: false,
suffix: ` ${asset.symbol}`,
}}
/>
<DisplayCurrency
className='text-xs'
coin={BNCoin.fromDenomAndBigNumber(asset.denom, accountDebt)}
parentheses
/>
</div>
<Text size='xs' className='text-white/50' tag='span'>
Total 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'>
<FormattedNumber
className='text-xs'
amount={modal.data?.liquidity.toNumber() ?? 0}
options={{ decimals: asset.decimals, abbreviated: true, suffix: ` ${asset.symbol}` }}
animate
/>
<DisplayCurrency
className='text-xs'
coin={BNCoin.fromDenomAndBigNumber(asset.denom, modal.data?.liquidity ?? BN_ZERO)}
parentheses
/>
</div>
<Text size='xs' className='text-white/50' tag='span'>
Liquidity available
</Text>
</div>
</div>
<div className='flex items-start flex-1 gap-6 p-6'>
<Card
className='flex flex-1 p-4 bg-white/5'
contentClassName='gap-6 flex flex-col justify-between h-full'
>
<TokenInputWithSlider
asset={asset}
onChange={handleChange}
onDebounce={onDebounce}
amount={amount}
max={max}
disabled={max.isZero()}
className='w-full'
maxText='Max'
warningMessages={[]}
/>
<Divider />
{maxRepayAmount.isZero() && <RepayNotAvailable asset={asset} />}
<Button
onClick={onConfirmClick}
className='w-full'
disabled={amount.isZero()}
text={`Repay ${asset.symbol}`}
rightIcon={<ArrowRight />}
/>
</Card>
<AccountSummaryInModal account={account} />
</div>
</Modal>
)
}
@@ -0,0 +1,15 @@
import useAccount from 'hooks/accounts/useAccount'
import useStore from 'store'
import Borrow from 'components/Modals/v1/Borrow'
import Repay from 'components/Modals/v1/Repay'
export default function V1BorrowAndRepayModal() {
const address = useStore((s) => s.address)
const { data: account } = useAccount(address)
const modal = useStore<V1BorrowAndRepayModal | null>((s) => s.v1BorrowAndRepayModal)
const isBorrow = modal?.type === 'borrow'
if (!modal || !account) return null
if (isBorrow) return <Borrow account={account} />
return <Repay account={account} />
}
@@ -0,0 +1,15 @@
import useAccount from 'hooks/accounts/useAccount'
import useStore from 'store'
import Deposit from 'components/Modals/v1/Deposit'
import Withdraw from 'components/Modals/v1/Withdraw'
export default function V1DepositAndWithdraw() {
const address = useStore((s) => s.address)
const { data: account } = useAccount(address)
const modal = useStore<V1DepositAndWithdrawModal | null>((s) => s.v1DepositAndWithdrawModal)
const isDeposit = modal?.type === 'deposit'
if (!modal || !account) return null
if (isDeposit) return <Deposit account={account} />
return <Withdraw account={account} />
}
+66
View File
@@ -0,0 +1,66 @@
import { useCallback, useState } from 'react'
import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal'
import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader'
import { BN_ZERO } from 'constants/math'
import useBaseAsset from 'hooks/assets/useBasetAsset'
import useHealthComputer from 'hooks/useHealthComputer'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
interface Props {
account: Account
}
export default function Withdraw(props: Props) {
const { account } = props
const baseAsset = useBaseAsset()
const modal = useStore((s) => s.v1DepositAndWithdrawModal)
const asset = modal?.data.asset ?? baseAsset
const [withdrawAsset, setWithdrawAsset] = useState<BNCoin>(
BNCoin.fromDenomAndBigNumber(modal?.data.asset.denom ?? baseAsset.denom, BN_ZERO),
)
const { computeMaxWithdrawAmount } = useHealthComputer(account)
const maxWithdrawAmount = computeMaxWithdrawAmount(asset.denom)
const { simulateWithdraw } = useUpdatedAccount(account)
const balance = BNCoin.fromDenomAndBigNumber(asset.denom, maxWithdrawAmount)
const v1Action = useStore((s) => s.v1Action)
const close = useCallback(() => {
useStore.setState({ v1DepositAndWithdrawModal: null })
}, [])
const handleClick = useCallback(async () => {
v1Action('withdraw', withdrawAsset)
close()
}, [v1Action, withdrawAsset, close])
const onDebounce = useCallback(() => {
simulateWithdraw(false, withdrawAsset)
}, [withdrawAsset, simulateWithdraw])
const handleAmountChange = useCallback(
(value: BigNumber) => {
setWithdrawAsset(BNCoin.fromDenomAndBigNumber(asset.denom, value))
},
[asset.denom],
)
if (!modal) return
return (
<AssetAmountSelectActionModal
account={account}
asset={asset}
contentHeader={<DetailsHeader data={modal.data} />}
coinBalances={[balance]}
actionButtonText={`Withdraw ${asset.symbol}`}
title={`Withdraw ${asset.symbol} from the Red Bank`}
onClose={close}
onAction={handleClick}
onChange={handleAmountChange}
onDebounce={onDebounce}
/>
)
}
+4 -2
View File
@@ -7,8 +7,8 @@ import Text from 'components/common/Text'
import { TextLink } from 'components/common/TextLink'
import { generateToastContent } from 'components/common/Toaster'
import useTransactions from 'hooks/localStorage/useTransactions'
import useStore from 'store'
import useChainConfig from 'hooks/useChainConfig'
import useStore from 'store'
export default function RecentTransactions() {
const address = useStore((s) => s.address)
@@ -47,7 +47,9 @@ export default function RecentTransactions() {
key={hash}
>
<div className='flex items-start justify-between w-full pb-2'>
<Text className='flex font-bold'>Credit Account {accountId}</Text>
<Text className='flex font-bold'>
{accountId === address ? 'Red Bank' : `Credit Account ${accountId}`}
</Text>
<Text size='sm' className='text-white/70'>
{moment.unix(timestamp).format('lll')}
</Text>
@@ -18,6 +18,7 @@ import chains from 'configs/chains'
import { BN_ZERO } from 'constants/math'
import useBaseAsset from 'hooks/assets/useBasetAsset'
import useMarketEnabledAssets from 'hooks/assets/useMarketEnabledAssets'
import useChainConfig from 'hooks/useChainConfig'
import useCurrentWallet from 'hooks/useCurrentWallet'
import useICNSDomain from 'hooks/useICNSDomain'
import useToggle from 'hooks/useToggle'
@@ -27,7 +28,6 @@ import { NETWORK } from 'types/enums/network'
import { ChainInfoID } from 'types/enums/wallet'
import { truncate } from 'utils/formatters'
import { getPage, getRoute } from 'utils/route'
import useChainConfig from 'hooks/useChainConfig'
export default function WalletConnectedButton() {
// ---------------
@@ -90,7 +90,7 @@ export default function WalletConnectedButton() {
})
}
navigate(getRoute(getPage(pathname), searchParams))
navigate(getRoute(getPage(pathname), new URLSearchParams()))
}
useEffect(() => {
+3 -2
View File
@@ -10,6 +10,7 @@ import useChainConfig from 'hooks/useChainConfig'
import useCurrentWallet from 'hooks/useCurrentWallet'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
import { getUrl } from 'utils/url'
interface Props {
providerId?: string
@@ -53,7 +54,7 @@ export default function WalletConnecting(props: Props) {
setIsConnecting(true)
try {
const response = await connect({ extensionProviderId, chainId: chainConfig.id })
const cosmClient = await CosmWasmClient.connect(chainConfig.endpoints.rpc)
const cosmClient = await CosmWasmClient.connect(getUrl(chainConfig.endpoints.rpc))
const walletClient: WalletClient = {
broadcast,
cosmWasmClient: cosmClient,
@@ -137,7 +138,7 @@ export default function WalletConnecting(props: Props) {
setIsConnecting(true)
try {
await mobileConnect({ mobileProviderId, chainId: chainConfig.id })
const cosmClient = await CosmWasmClient.connect(chainConfig.endpoints.rpc)
const cosmClient = await CosmWasmClient.connect(getUrl(chainConfig.endpoints.rpc))
const walletClient: WalletClient = {
broadcast,
cosmWasmClient: cosmClient,
+1 -1
View File
@@ -13,7 +13,7 @@ export default function Wallet() {
const address = useStore((s) => s.address)
useEffect(() => {
if (address) return
if (address && address === currentWallet?.account.address) return
useStore.setState({
address: undefined,
userDomain: undefined,
@@ -47,7 +47,7 @@ export default function AccountBalancesTable(props: Props) {
const columns = useAccountBalancesColumns(account, showLiquidationPrice)
if (accountBalanceData.length === 0)
if (accountBalanceData.length === 0 && account.id !== address)
return (
<ConditionalWrapper
condition={!hideCard}
@@ -7,7 +7,7 @@ import DisplayCurrency from 'components/common/DisplayCurrency'
import { FormattedNumber } from 'components/common/FormattedNumber'
import { ArrowRight } from 'components/common/Icons'
import Text from 'components/common/Text'
import useLendingMarketAssetsTableData from 'components/earn/lend/Table/useLendingMarketAssetsTableData'
import useLendingMarketAssetsTableData from 'components/lend/Table/useLendingMarketAssetsTableData'
import { BN_ZERO, MAX_AMOUNT_DECIMALS } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle'
import useAllAssets from 'hooks/assets/useAllAssets'
+20 -15
View File
@@ -12,18 +12,19 @@ import DisplayCurrency from 'components/common/DisplayCurrency'
import { FormattedNumber } from 'components/common/FormattedNumber'
import { ThreeDots } from 'components/common/Icons'
import Text from 'components/common/Text'
import useLendingMarketAssetsTableData from 'components/earn/lend/Table/useLendingMarketAssetsTableData'
import useLendingMarketAssetsTableData from 'components/lend/Table/useLendingMarketAssetsTableData'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import { ORACLE_DENOM } from 'constants/oracle'
import useAccount from 'hooks/accounts/useAccount'
import useAccountIds from 'hooks/accounts/useAccountIds'
import useAccounts from 'hooks/accounts/useAccounts'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useAllAssets from 'hooks/assets/useAllAssets'
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useAccountId from 'hooks/useAccountId'
import useHealthComputer from 'hooks/useHealthComputer'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import useHealthComputer from 'hooks/useHealthComputer'
import usePrices from 'hooks/usePrices'
import useVaultAprs from 'hooks/vaults/useVaultAprs'
import useStore from 'store'
@@ -33,33 +34,40 @@ import {
calculateAccountBalanceValue,
calculateAccountLeverage,
} from 'utils/accounts'
import { getPage } from 'utils/route'
export default function AccountDetailsController() {
const address = useStore((s) => s.address)
const isHLS = useStore((s) => s.isHLS)
const isV1 = useStore((s) => s.isV1)
const { data: _, isLoading } = useAccounts('default', address)
const { data: accountIds } = useAccountIds(address, false, true)
const accountId = useAccountId()
const currentAccount = useCurrentAccount()
const { data: v1Account } = useAccount(address)
const { pathname } = useLocation()
const page = getPage(pathname)
const account = useCurrentAccount()
const account = isV1 && page !== 'trade' ? v1Account : currentAccount
const focusComponent = useStore((s) => s.focusComponent)
const isOwnAccount = accountId && accountIds?.includes(accountId)
const isOwnAccount = (accountId && accountIds?.includes(accountId)) || account?.id === address
const hideAccountDetails =
!address || focusComponent || !isOwnAccount || isHLS || page.includes('portfolio')
const isLoadingAccountDetails = (isLoading && accountId && !focusComponent) || !account
if (!address || focusComponent || !isOwnAccount || isHLS) return null
if (hideAccountDetails) return null
if (isLoadingAccountDetails) return <Skeleton />
if ((isLoading && accountId && !focusComponent) || !account) return <Skeleton />
return <AccountDetails account={account} />
return <AccountDetails account={account} page={page} />
}
interface Props {
account: Account
page: Page
}
function AccountDetails(props: Props) {
const { account } = props
const location = useLocation()
const { account, page } = props
const { data: hlsStrategies } = useHLSStakingAssets()
const { data: vaultAprs } = useVaultAprs()
const [reduceMotion] = useLocalStorage<boolean>(
@@ -124,10 +132,7 @@ function AccountDetails(props: Props) {
vaultAprs,
],
)
const isFullWidth =
location.pathname.includes('trade') ||
location.pathname === '/' ||
location.pathname.includes('perps')
const isFullWidth = page === 'trade' || page === 'perps'
return (
<>
@@ -6,7 +6,7 @@ import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMar
import Button from 'components/common/Button'
import { ArrowDownLine, ArrowUpLine, TrashBin } from 'components/common/Icons'
import SwitchAutoLend from 'components/common/Switch/SwitchAutoLend'
import useLendingMarketAssetsTableData from 'components/earn/lend/Table/useLendingMarketAssetsTableData'
import useLendingMarketAssetsTableData from 'components/lend/Table/useLendingMarketAssetsTableData'
import useAccount from 'hooks/accounts/useAccount'
import useAllAssets from 'hooks/assets/useAllAssets'
import useHealthComputer from 'hooks/useHealthComputer'
@@ -42,6 +42,7 @@ export default function AccountSummaryHeader(props: Props) {
updatedHealthFactor,
isAccountDetails,
} = props
const address = useStore((s) => s.address)
const onClose = useCallback(() => useStore.setState({ accountDetailsExpanded: false }), [])
const accountBalance = useMemo(
() => (account ? calculateAccountBalanceValue(account, prices, assets) : BN_ZERO),
@@ -68,10 +69,9 @@ export default function AccountSummaryHeader(props: Props) {
/>
)}
{isAccountDetails && (
<Text
size='sm'
className='w-full pb-1 text-white/50'
>{`Credit Account ${account.id}`}</Text>
<Text size='sm' className='w-full pb-1 text-white/50'>
{account.id === address ? 'Red Bank' : `Credit Account ${account.id}`}
</Text>
)}
<div className='flex items-end w-full gap-2 pb-2 border-b border-white/5'>
<DisplayCurrency
@@ -7,7 +7,7 @@ import AccountStrategiesTable from 'components/account/AccountStrategiesTable'
import AccountSummaryHeader from 'components/account/AccountSummary/AccountSummaryHeader'
import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData'
import Accordion from 'components/common/Accordion'
import useLendingMarketAssetsTableData from 'components/earn/lend/Table/useLendingMarketAssetsTableData'
import useLendingMarketAssetsTableData from 'components/lend/Table/useLendingMarketAssetsTableData'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import { BN_ZERO } from 'constants/math'
+10 -4
View File
@@ -63,16 +63,22 @@ export default function HealthBar({
health={isUpdated ? updatedHealth : health}
healthFactor={isUpdated ? updatedHealthFactor : healthFactor}
>
<>
<div className={classNames('flex w-full', showIcon && 'gap-2')}>
{showIcon && (
<HealthIcon
health={health}
isLoading={healthFactor === 0}
className={classNames('mr-2', iconClassName)}
className={iconClassName}
colorClass='text-white'
/>
)}
<div className={classNames('flex w-full', 'rounded-full overflow-hidden', className)}>
<div
className={classNames(
'flex w-full flex-shrink',
'rounded-full overflow-hidden',
className,
)}
>
<svg
version='1.1'
xmlns='http://www.w3.org/2000/svg'
@@ -153,7 +159,7 @@ export default function HealthBar({
)}
</svg>
</div>
</>
</div>
</HealthTooltip>
)
}
+4 -4
View File
@@ -5,7 +5,7 @@ import { ExclamationMarkCircled, Heart } from 'components/common/Icons'
interface Props {
isLoading: boolean
health: number
className: string
className?: string
colorClass?: string
}
@@ -14,9 +14,9 @@ export default function HealthIcon(props: Props) {
const color = colorClass ?? 'text-white'
return (
<>
<div className='w-5'>
{!isLoading && health === 0 ? (
<ExclamationMarkCircled className={classNames('w-5 text-loss animate-pulse', className)} />
<ExclamationMarkCircled className={classNames(' text-loss animate-pulse', className)} />
) : (
<Heart
className={classNames(
@@ -26,6 +26,6 @@ export default function HealthIcon(props: Props) {
)}
/>
)}
</>
</div>
)
}
@@ -1,44 +0,0 @@
import { useCallback } from 'react'
import Button from 'components/common/Button'
import ActionButton from 'components/common/Button/ActionButton'
import { HandCoins, Plus } from 'components/common/Icons'
import useMarketEnabledAssets from 'hooks/assets/useMarketEnabledAssets'
import useStore from 'store'
interface Props {
data: BorrowMarketTableData
}
export default function BorrowActionButtons(props: Props) {
const { asset, accountDebt } = props.data
const marketAssets = useMarketEnabledAssets()
const currentAsset = marketAssets.find((a) => a.denom === asset.denom)
const borrowHandler = useCallback(() => {
if (!currentAsset) return null
useStore.setState({ borrowModal: { asset: currentAsset, marketData: props.data } })
}, [currentAsset, props.data])
const repayHandler = useCallback(() => {
if (!currentAsset) return null
useStore.setState({
borrowModal: { asset: currentAsset, marketData: props.data, isRepay: true },
})
}, [currentAsset, props.data])
return (
<div className='flex flex-row space-x-2'>
<ActionButton
leftIcon={<Plus className='w-3' />}
onClick={borrowHandler}
color='secondary'
text={accountDebt ? 'Borrow more' : 'Borrow'}
className='text-center min-w-40'
/>
{accountDebt && (
<Button color='tertiary' leftIcon={<HandCoins />} text='Repay' onClick={repayHandler} />
)}
</div>
)
}
+5 -5
View File
@@ -1,19 +1,19 @@
import ActiveBorrowingsTable from 'components/borrow/Table/ActiveBorrowingsTable'
import AvailableBorrowingsTable from 'components/borrow/Table/AvailableBorrowingsTable'
import DepositedBorrowingsTable from 'components/borrow/Table/DepositedBorrowingsTable'
import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData'
import { BN_ZERO } from 'constants/math'
import useBorrowEnabledAssets from 'hooks/assets/useBorrowEnabledAssets'
export default function Borrowings() {
const data = useBorrowMarketAssetsTableData()
const { accountBorrowedAssets, availableAssets, allAssets } = useBorrowMarketAssetsTableData()
if (!data?.allAssets?.length) {
if (!allAssets?.length) {
return <Fallback />
}
return (
<>
<DepositedBorrowingsTable data={data.accountBorrowedAssets} isLoading={false} />
<AvailableBorrowingsTable data={data.availableAssets} isLoading={false} />
<ActiveBorrowingsTable data={accountBorrowedAssets} isLoading={false} />
<AvailableBorrowingsTable data={availableAssets} isLoading={false} />
</>
)
}
@@ -0,0 +1,41 @@
import { Row } from '@tanstack/react-table'
import { useCallback } from 'react'
import { DEBT_VALUE_META } from 'components/borrow/Table/Columns/DebtValue'
import { NAME_META } from 'components/borrow/Table/Columns/Name'
import useBorrowingsColumns from 'components/borrow/Table/Columns/useActiveColumns'
import MarketDetails from 'components/common/MarketDetails'
import Table from 'components/common/Table'
type Props = {
data: BorrowMarketTableData[]
isLoading: boolean
v1?: boolean
}
export default function ActiveBorrowingsTable(props: Props) {
const columns = useBorrowingsColumns({ v1: props.v1 })
const renderExpanded = useCallback((row: Row<BorrowMarketTableData>) => {
return <MarketDetails row={row} type='borrow' />
}, [])
if (!props.data.length) return null
return (
<Table
title={props.v1 ? 'Borrowings' : 'Borrowed Assets'}
columns={columns}
data={props.data}
initialSorting={
props.v1
? [
{ id: DEBT_VALUE_META.id, desc: true },
{ id: NAME_META.id, desc: false },
]
: [{ id: NAME_META.id, desc: false }]
}
renderExpanded={renderExpanded}
/>
)
}
@@ -2,12 +2,10 @@ 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/common/MarketDetails'
import Table from 'components/common/Table'
import ActionButtonRow from 'components/common/Table/ActionButtonRow'
type Props = {
data: BorrowMarketTableData[]
@@ -20,14 +18,7 @@ export default function AvailableBorrowingsTable(props: Props) {
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' />
</>
)
return <MarketDetails row={currentRow} type='borrow' />
},
[],
)
@@ -0,0 +1,61 @@
import { useCallback } from 'react'
import ActionButton from 'components/common/Button/ActionButton'
import { Plus } from 'components/common/Icons'
import Text from 'components/common/Text'
import { Tooltip } from 'components/common/Tooltip'
import ConditionalWrapper from 'hocs/ConditionalWrapper'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useStore from 'store'
export const BORROW_BUTTON_META = {
accessorKey: 'borrow',
enableSorting: false,
header: '',
}
interface Props {
data: LendingMarketTableData
}
export default function BorrowButton(props: Props) {
const account = useCurrentAccount()
const address = useStore((s) => s.address)
const hasNoDeposits = !account?.deposits?.length && !account?.lends?.length && !!address
const borrowHandler = useCallback(() => {
if (!props.data.asset) return null
useStore.setState({ borrowModal: { asset: props.data.asset, marketData: props.data } })
}, [props.data])
return (
<div className='flex justify-end'>
<ConditionalWrapper
condition={hasNoDeposits}
wrapper={(children) => (
<Tooltip
type='warning'
content={
<Text size='sm'>{`You dont have any collateral.
Please first deposit into your Credit Account before borrowing.`}</Text>
}
contentClassName='max-w-[200px]'
className='ml-auto'
>
{children}
</Tooltip>
)}
>
<ActionButton
leftIcon={<Plus />}
disabled={hasNoDeposits}
color='tertiary'
onClick={(e) => {
borrowHandler()
e.stopPropagation()
}}
text='Borrow'
/>
</ConditionalWrapper>
</div>
)
}
@@ -4,7 +4,6 @@ import Loading from 'components/common/Loading'
export const BORROW_RATE_META = {
accessorKey: 'apy.borrow',
header: 'Borrow Rate APY',
meta: { className: 'w-40' },
}
interface Props {
@@ -1,18 +1,18 @@
import { ChevronDown, ChevronUp } from 'components/common/Icons'
export const MANAGE_META = {
accessorKey: 'manage',
export const CHEVRON_META = {
id: 'chevron',
enableSorting: false,
header: 'Manage',
header: '',
meta: {
className: 'w-30',
className: 'w-5',
},
}
interface Props {
isExpanded: boolean
}
export default function Manage(props: Props) {
export default function Chevron(props: Props) {
return (
<div className='flex items-center justify-end'>
<div className='w-4'>{props.isExpanded ? <ChevronUp /> : <ChevronDown />}</div>
@@ -1,36 +0,0 @@
import { Row } from '@tanstack/react-table'
import AmountAndValue from 'components/common/AmountAndValue'
import { BN_ZERO } from 'constants/math'
import useMarketEnabledAssets from 'hooks/assets/useMarketEnabledAssets'
import { byDenom } from 'utils/array'
export const DEBT_META = {
accessorKey: 'debt',
header: 'Debt',
}
export const debtSortingFn = (
a: Row<BorrowMarketTableData>,
b: Row<BorrowMarketTableData>,
): number => {
const assetA = a.original.asset
const assetB = b.original.asset
if (!a.original.accountDebt || !b.original.accountDebt) return 0
const debtA = a.original.accountDebt.shiftedBy(-assetA.decimals)
const debtB = b.original.accountDebt.shiftedBy(-assetB.decimals)
return debtA.minus(debtB).toNumber()
}
interface Props {
data: BorrowMarketTableData
}
export default function Debt(props: Props) {
const marketAssets = useMarketEnabledAssets()
const asset = marketAssets.find(byDenom(props.data.asset.denom))
if (!asset) return null
return <AmountAndValue asset={asset} amount={props.data?.accountDebt ?? BN_ZERO} />
}
@@ -0,0 +1,30 @@
import { Row } from '@tanstack/react-table'
import AmountAndValue from 'components/common/AmountAndValue'
import { BN_ZERO } from 'constants/math'
import { BN } from 'utils/helpers'
export const DEBT_VALUE_META = {
id: 'accountDebtValue',
accessorKey: 'accountDebtValue',
header: 'Debt',
}
export const debtSortingFn = (
a: Row<BorrowMarketTableData>,
b: Row<BorrowMarketTableData>,
): number => {
const debtValueA = BN(a.original?.accountDebtValue ?? 0)
const debtValueB = BN(b.original?.accountDebtValue ?? 0)
return debtValueA.minus(debtValueB).toNumber()
}
interface Props {
asset: Asset
debtAmount?: BigNumber
}
export default function DebtValue(props: Props) {
return (
<AmountAndValue asset={props.asset} amount={props.debtAmount ? props.debtAmount : BN_ZERO} />
)
}
@@ -10,7 +10,6 @@ export const LIQUIDITY_META = {
accessorKey: 'liquidity',
header: 'Liquidity Available',
id: 'liquidity',
meta: { className: 'w-40' },
}
export const liquiditySortingFn = (
+41 -6
View File
@@ -1,20 +1,55 @@
import { ChevronDown, ChevronUp } from 'components/common/Icons'
import { useCallback, useMemo } from 'react'
import DropDownButton from 'components/common/Button/DropDownButton'
import { HandCoins, Plus } from 'components/common/Icons'
import useStore from 'store'
export const MANAGE_META = {
accessorKey: 'manage',
enableSorting: false,
header: 'Manage',
meta: { className: 'w-30' },
header: '',
}
interface Props {
isExpanded: boolean
data: BorrowMarketTableData
}
export default function Manage(props: Props) {
const address = useStore((s) => s.address)
const borrowHandler = useCallback(() => {
if (!props.data.asset) return null
useStore.setState({ borrowModal: { asset: props.data.asset, marketData: props.data } })
}, [props.data])
const repayHandler = useCallback(() => {
if (!props.data.asset) return null
useStore.setState({
borrowModal: { asset: props.data.asset, marketData: props.data, isRepay: true },
})
}, [props.data])
const ITEMS: DropDownItem[] = useMemo(
() => [
{
icon: <Plus />,
text: 'Borrow more',
onClick: borrowHandler,
},
{
icon: <HandCoins />,
text: 'Repay',
onClick: repayHandler,
},
],
[borrowHandler, repayHandler],
)
if (!address) return null
return (
<div className='flex items-center justify-end'>
<div className='w-4'>{props.isExpanded ? <ChevronUp /> : <ChevronDown />}</div>
<div className='z-10 flex justify-end'>
<DropDownButton items={ITEMS} text='Manage' color='tertiary' />
</div>
)
}
@@ -2,15 +2,24 @@ 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 Chevron, { CHEVRON_META } from 'components/borrow/Table/Columns/Chevron'
import DebtValue, {
DEBT_VALUE_META,
debtSortingFn,
} from 'components/borrow/Table/Columns/DebtValue'
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'
import Action from 'components/v1/Table/borrowings/Columns/Action'
export default function useDepositedColumns() {
interface Props {
v1?: boolean
}
export default function useActiveColumns(props: Props) {
return useMemo<ColumnDef<BorrowMarketTableData>[]>(() => {
return [
{
@@ -18,8 +27,10 @@ export default function useDepositedColumns() {
cell: ({ row }) => <Name data={row.original} />,
},
{
...DEBT_META,
cell: ({ row }) => <Debt data={row.original} />,
...DEBT_VALUE_META,
cell: ({ row }) => (
<DebtValue asset={row.original.asset} debtAmount={row.original.accountDebtAmount} />
),
sortingFn: debtSortingFn,
},
{
@@ -33,8 +44,13 @@ export default function useDepositedColumns() {
},
{
...MANAGE_META,
cell: ({ row }) => <Manage isExpanded={row.getIsExpanded()} />,
cell: ({ row }) =>
props.v1 ? <Action data={row.original} /> : <Manage data={row.original} />,
},
{
...CHEVRON_META,
cell: ({ row }) => <Chevron isExpanded={row.getIsExpanded()} />,
},
]
}, [])
}, [props.v1])
}
@@ -1,12 +1,13 @@
import { ColumnDef } from '@tanstack/react-table'
import { useMemo } from 'react'
import BorrowButton, { BORROW_BUTTON_META } from 'components/borrow/Table/Columns/BorrowButton'
import BorrowRate, { BORROW_RATE_META } from 'components/borrow/Table/Columns/BorrowRate'
import Chevron, { CHEVRON_META } from 'components/borrow/Table/Columns/Chevron'
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() {
@@ -26,8 +27,12 @@ export default function useAvailableColumns() {
sortingFn: liquiditySortingFn,
},
{
...MANAGE_META,
cell: ({ row }) => <Manage isExpanded={row.getIsExpanded()} />,
...BORROW_BUTTON_META,
cell: ({ row }) => <BorrowButton data={row.original} />,
},
{
...CHEVRON_META,
cell: ({ row }) => <Chevron isExpanded={row.getIsExpanded()} />,
},
]
}, [])
@@ -1,42 +0,0 @@
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/common/MarketDetails'
import Table from 'components/common/Table'
import ActionButtonRow from 'components/common/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}
/>
)
}
@@ -1,11 +1,14 @@
import { useMemo } from 'react'
import { BN_ZERO } from 'constants/math'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useMarkets from 'hooks/markets/useMarkets'
import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice'
export default function useBorrowMarketAssetsTableData() {
const account = useCurrentAccount()
const markets = useMarkets()
const { convertAmount } = useDisplayCurrencyPrice()
return useMemo((): {
accountBorrowedAssets: BorrowMarketTableData[]
@@ -18,15 +21,21 @@ export default function useBorrowMarketAssetsTableData() {
markets
.filter((market) => market.borrowEnabled)
.forEach((market) => {
const debt = account?.debts?.find((debt) => debt.denom === market.asset.denom)
const amount =
account?.debts?.find((debt) => debt.denom === market.asset.denom)?.amount ?? BN_ZERO
const value = amount ? convertAmount(market.asset, amount) : undefined
const borrowMarketAsset: BorrowMarketTableData = {
...market,
accountDebt: debt?.amount,
accountDebtAmount: amount,
accountDebtValue: value,
}
if (borrowMarketAsset.accountDebtAmount?.isZero()) {
availableAssets.push(borrowMarketAsset)
} else {
accountBorrowedAssets.push(borrowMarketAsset)
}
;(borrowMarketAsset.accountDebt ? accountBorrowedAssets : availableAssets).push(
borrowMarketAsset,
)
})
return {
@@ -34,5 +43,5 @@ export default function useBorrowMarketAssetsTableData() {
availableAssets,
allAssets: [...accountBorrowedAssets, ...availableAssets],
}
}, [account?.debts, markets])
}, [account?.debts, markets, convertAmount])
}
+15 -6
View File
@@ -15,19 +15,28 @@ export default function Background() {
)
const { pathname } = useLocation()
const page = getPage(pathname)
const isHLS = useMemo(() => page.split('-')[0] === 'hls', [page])
const [isHLS] = useMemo(() => [page.split('-')[0] === 'hls'], [page])
useEffect(() => {
useStore.setState({ isHLS })
useStore.setState({ isHLS: isHLS })
}, [isHLS])
const [primaryOrbClassName, secondaryOrbClassName, tertiaryOrbClassName, bodyClassName] =
useMemo(() => {
if (isHLS) {
return ['bg-orb-primary-hls', 'bg-orb-secondary-hls', 'bg-orb-tertiary-hls', 'bg-body-hls']
}
return ['bg-orb-primary', 'bg-orb-secondary', 'bg-orb-tertiary', 'bg-body']
}, [isHLS])
return (
<div
className={classNames(
'fixed inset-0',
'w-full h-full',
'overflow-hidden pointer-events-none background ',
isHLS ? 'bg-body-hls' : 'bg-body',
bodyClassName,
!reduceMotion && 'transition-bg duration-1000 delay-300',
)}
>
@@ -39,7 +48,7 @@ export default function Background() {
'max-h-[500px] max-w-[500px]',
'left-[-10vw] top-[-10vw]',
'blur-orb-primary',
isHLS ? ' bg-orb-primary-hls' : 'bg-orb-primary',
primaryOrbClassName,
'translate-x-0 translate-y-0 rounded-full opacity-20',
!reduceMotion && 'animate-[float_120s_ease-in-out_infinite_2s]',
!reduceMotion && 'transition-bg duration-1000 delay-300',
@@ -53,7 +62,7 @@ export default function Background() {
'max-h-[1000px] max-w-[1000px]',
'bottom-[-20vw] right-[-10vw]',
'blur-orb-secondary',
isHLS ? ' bg-orb-secondary-hls' : 'bg-orb-secondary',
secondaryOrbClassName,
'translate-x-0 translate-y-0 rounded-full opacity-30',
!reduceMotion && 'transition-bg duration-1000 delay-300',
)}
@@ -66,7 +75,7 @@ export default function Background() {
'max-h-[600px] max-w-[600px]',
'right-[-4vw] top-[-10vw]',
'blur-orb-tertiary ',
isHLS ? ' bg-orb-tertiary-hls' : 'bg-orb-tertiary',
tertiaryOrbClassName,
'translate-x-0 translate-y-0 rounded-full opacity-20',
!reduceMotion && 'animate-[float_180s_ease-in_infinite]',
!reduceMotion && 'transition-bg duration-1000 delay-300',
+42 -11
View File
@@ -1,12 +1,16 @@
import classNames from 'classnames'
import Button from 'components/common/Button/index'
import { ChevronDown } from 'components/common/Icons'
import Text from 'components/common/Text'
import { Tooltip } from 'components/common/Tooltip'
import ConditionalWrapper from 'hocs/ConditionalWrapper'
import useToggle from 'hooks/useToggle'
interface Props extends ButtonProps {
items: DropDownItem[]
text: string
showProgressIndicator?: boolean
}
export default function DropDownButton(props: Props) {
@@ -16,16 +20,20 @@ export default function DropDownButton(props: Props) {
content={<DropDown closeMenu={() => toggleIsOpen(false)} {...props} />}
type='info'
placement='bottom'
contentClassName='!bg-white/10 backdrop-blur-xl !p-0'
contentClassName='!bg-white/10 backdrop-blur-xl !p-0 w-full min-w-[140px]'
interactive
hideArrow
visible={isOpen}
onClickOutside={() => toggleIsOpen(false)}
>
<Button
onClick={() => toggleIsOpen()}
onClick={(e) => {
toggleIsOpen()
e.stopPropagation()
}}
rightIcon={<ChevronDown />}
iconClassName='w-3 h-3'
showProgressIndicator={props.showProgressIndicator}
{...props}
/>
</Tooltip>
@@ -39,7 +47,7 @@ interface DropDownProps {
function DropDown(props: DropDownProps) {
return (
<div>
<div className='w-full'>
{props.items.map((item) => (
<DropDownItem key={item.text} item={item} closeMenu={props.closeMenu} />
))}
@@ -54,15 +62,38 @@ interface DropDownItemProps {
function DropDownItem(props: DropDownItemProps) {
return (
<button
onClick={() => {
props.item.onClick()
props.closeMenu()
<ConditionalWrapper
condition={!!props.item.disabled}
wrapper={(children) => {
if (!props.item.disabledTooltip) return children
return (
<Tooltip
type='warning'
content={<Text size='sm'>{props.item.disabledTooltip}</Text>}
contentClassName='max-w-[200px]'
className='ml-auto'
>
{children}
</Tooltip>
)
}}
className=' px-4 py-3 flex gap-2 items-center hover:bg-white/5 w-full [&:not(:last-child)]:border-b border-white/10'
>
<div className='flex justify-center w-5 h-5'>{props.item.icon}</div>
<Text size='sm'>{props.item.text}</Text>
</button>
<button
onClick={(e) => {
e.preventDefault()
props.item.onClick()
props.closeMenu()
e.stopPropagation()
}}
className={classNames(
'z-1 px-4 py-3 flex gap-2 items-center w-full [&:not(:last-child)]:border-b border-white/10',
props.item.disabled ? 'bg-black/20 text-white/40 cursor-events-none' : 'hover:bg-white/5',
)}
disabled={props.item.disabled}
>
<div className='flex justify-center w-4 h-4'>{props.item.icon}</div>
<Text size='sm'>{props.item.text}</Text>
</button>
</ConditionalWrapper>
)
}
+5 -1
View File
@@ -18,7 +18,11 @@ export default function DepositCapMessage(props: Props) {
return (
<div className={classNames('flex items-start', props.className)}>
{props.showIcon && <InfoCircle width={26} className='mr-5' />}
{props.showIcon && (
<div className='w-6 mr-5'>
<InfoCircle />
</div>
)}
<div className='flex flex-col gap-2'>
<Text size='sm'>Deposit Cap Reached!</Text>
<Text size='xs' className='text-white/40'>{`Unfortunately you're not able to ${
+8 -1
View File
@@ -9,6 +9,7 @@ import {
GridPlanet,
GridTire,
GridWeb,
Wallet,
} from 'components/common/Icons'
import Text from 'components/common/Text'
import { Tooltip } from 'components/common/Tooltip'
@@ -17,7 +18,7 @@ import useStore from 'store'
interface Props {
text: string | ReactNode
children?: ReactNode
bg: 'borrow' | 'lend' | 'farm' | 'portfolio' | 'hls-farm' | 'hls-staking'
bg: Page
}
function IntroBackground(props: { bg: Props['bg'] }) {
@@ -52,6 +53,12 @@ function IntroBackground(props: { bg: Props['bg'] }) {
<GridPlanet />
</div>
)
case 'v1':
return (
<div className='absolute top-0 right-0 block w-80 opacity-5'>
<Wallet />
</div>
)
default:
return (
<div className='absolute bottom-0 right-0 block w-3/4 md:w-120 opacity-5'>
+1 -1
View File
@@ -23,7 +23,7 @@ export default function MarketDetails({ row, type }: Props) {
symbol: displayCurrencySymbol,
} = useDisplayCurrencyPrice()
const { asset, ltv, cap, liquidity, deposits, debt } = row.original
const { asset, ltv, deposits, debt } = row.original
const details: Detail[] = useMemo(() => {
const isDollar = displayCurrencySymbol === '$'
-47
View File
@@ -1,47 +0,0 @@
import Card from 'components/common/Card'
import { Cross, ExclamationMarkCircled } from 'components/common/Icons'
import Text from 'components/common/Text'
import { TextLink } from 'components/common/TextLink'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useStore from 'store'
import { DocURL } from 'types/enums/docURL'
export default function MigrationBanner() {
const [_, setMigrationBanner] = useLocalStorage<boolean>(
LocalStorageKeys.MIGRATION_BANNER,
DEFAULT_SETTINGS.migrationBanner,
)
const showMigrationBanner = useStore((s) => s.migrationBanner)
if (!showMigrationBanner) return null
return (
<Card
className='relative w-full bg-info-bg/20 text-info'
contentClassName='flex w-full gap-2 px-4 py-3 items-center'
>
<div className='flex w-6 h-6 p-1 text-white rounded bg-info'>
<ExclamationMarkCircled />
</div>
<Text className='flex flex-grow' size='sm'>
If you have funds on{' '}
<TextLink
href={DocURL.V1_URL}
externalLink
className='mx-1 text-white no-underline hover:underline'
>
Mars v1,
</TextLink>
please withdraw them and deposit them on Mars v2 to migrate.
</Text>
<div
className='absolute right-0 flex items-center h-full px-4 w-11 opacity-80 hover:cursor-pointer hover:opacity-100'
onClick={() => setMigrationBanner(false)}
>
<Cross className='w-3 h-3' />
</div>
</Card>
)
}
@@ -12,11 +12,12 @@ interface Props {
className?: string
tooltip?: string
disabled?: boolean
placement?: 'top' | 'bottom' | 'left' | 'right'
}
export default function SwitchWithLabel(props: Props) {
return (
<div className={classNames('flex w-full', props.className)}>
<div className={classNames('flex w-full items-center ', props.className)}>
<div className='flex flex-1'>
<Text className='mr-2 text-white/70' size='sm'>
{props.label}
@@ -24,8 +25,9 @@ export default function SwitchWithLabel(props: Props) {
{props.tooltip && (
<Tooltip
type='info'
placement={props.placement ?? 'top'}
content={
<Text size='sm' className='px-2 py-3'>
<Text size='sm' className='px-2 py-3 max-w-[320px]'>
{props.tooltip}
</Text>
}
+65
View File
@@ -0,0 +1,65 @@
import classNames from 'classnames'
import { NavLink, useParams, useSearchParams } from 'react-router-dom'
import { useMemo } from 'react'
import SwitchWithLabel from 'components/common/Switch/SwitchWithLabel'
import useAccountId from 'hooks/useAccountId'
import useChainConfig from 'hooks/useChainConfig'
import useStore from 'store'
import { getRoute } from 'utils/route'
const underlineClasses =
'relative before:absolute before:h-[2px] before:-bottom-1 before:left-0 before:right-0 before:gradient-active-tab'
interface Props {
tabs: Tab[]
activeTabIdx: number
showV1Toggle?: boolean
}
export default function Tab(props: Props) {
const accountId = useAccountId()
const { address } = useParams()
const [searchParams] = useSearchParams()
const chainConfig = useChainConfig()
const isV1 = useStore((s) => s.isV1)
const filteredTabs = useMemo(
() =>
props.tabs.map((tab) => (tab.name === 'Farm' && (!chainConfig.farm || isV1) ? null : tab)),
[chainConfig, props.tabs, isV1],
)
return (
<div className='relative flex w-full'>
<div className='flex flex-grow'>
{filteredTabs.map((tab, index) =>
tab ? (
<NavLink
key={tab.page}
to={getRoute(tab.page, searchParams, address, accountId)}
className={classNames(
props.activeTabIdx === index ? underlineClasses : 'text-white/40',
'relative mr-8 text-xl ',
)}
>
{tab.name}
</NavLink>
) : null,
)}
</div>
{props.showV1Toggle && (
<div className='flex items-center w-40 h-full'>
<SwitchWithLabel
name='v1-toggle'
label='Wallet Mode'
value={isV1}
onChange={() => useStore.setState({ isV1: !isV1 })}
tooltip='The Wallet Mode allows you to interact with the Red Bank without the use of a Credit Account. Funds will be deposited and borrowed directly from and to your Wallet. However you will not be able to use the deposited funds for trading or farming.'
placement='bottom'
/>
</div>
)}
</div>
)
}
+7 -2
View File
@@ -10,6 +10,7 @@ interface Props<T> {
className?: string
isSelectable?: boolean
type?: TableType
onClick?: (id: string) => void
}
function getBorderColor(
@@ -36,7 +37,7 @@ export default function Row<T>(props: Props<T>) {
key={`${row.id}-row`}
className={classNames(
'group/row transition-bg',
(renderExpanded || isSelectable) && 'hover:cursor-pointer',
(renderExpanded || isSelectable || props.onClick) && 'hover:cursor-pointer',
canExpand && row.getIsExpanded() ? 'is-expanded bg-black/20' : 'hover:bg-white/5',
)}
onClick={(e) => {
@@ -49,6 +50,10 @@ export default function Row<T>(props: Props<T>) {
table.resetExpanded()
!isExpanded && row.toggleExpanded()
}
if (props.onClick) {
props.onClick((row.original as any).asset.denom)
}
}}
>
{row.getVisibleCells().map((cell) => {
@@ -61,7 +66,7 @@ export default function Row<T>(props: Props<T>) {
spacingClassName ?? 'px-3 py-4',
type && type !== 'strategies' && isSymbolOrName && 'border-l',
type && type !== 'strategies' && getBorderColor(type, cell.row.original as any),
cell.column.columnDef.meta?.className,
cell.column.columnDef.meta?.className ?? 'w-min',
)}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
+3
View File
@@ -31,6 +31,7 @@ interface Props<T> {
hideCard?: boolean
setRowSelection?: OnChangeFn<RowSelectionState>
selectedRows?: RowSelectionState
onClickRow?: (id: string) => void
}
export default function Table<T>(props: Props<T>) {
@@ -75,6 +76,7 @@ export default function Table<T>(props: Props<T>) {
props.spacingClassName ?? 'px-4 py-3',
header.column.getCanSort() && 'hover:cursor-pointer',
header.id === 'symbol' || header.id === 'name' ? 'text-left' : 'text-right',
'w-min',
header.column.columnDef.meta?.className,
)}
>
@@ -122,6 +124,7 @@ export default function Table<T>(props: Props<T>) {
spacingClassName={props.spacingClassName}
isSelectable={!!props.setRowSelection}
type={props.type}
onClick={props.onClickRow}
/>
))}
</tbody>
+9 -2
View File
@@ -11,11 +11,11 @@ import { TextLink } from 'components/common/TextLink'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useChainConfig from 'hooks/useChainConfig'
import useTransactionStore from 'hooks/useTransactionStore'
import useStore from 'store'
import { formatAmountWithSymbol } from 'utils/formatters'
import { BN } from 'utils/helpers'
import useChainConfig from 'hooks/useChainConfig'
const toastBodyClasses = classNames(
'flex flex-wrap w-full group/transaction',
@@ -99,6 +99,13 @@ export default function Toaster() {
if (!isError && toast.accountId) addTransaction(toast)
const generalMessage = isError ? 'Transaction failed!' : 'Transaction completed successfully!'
const showDetailElement = !!(!details && toast.hash)
const address = useStore.getState().address
let target: string
if (!isError) {
target = toast.accountId === address ? 'Red Bank' : `Credit Account ${toast.accountId}`
}
const Msg = () => (
<div className='relative flex flex-wrap w-full m-0 isolate'>
<div className='flex w-full gap-2 mb-2'>
@@ -141,7 +148,7 @@ export default function Toaster() {
)}
>
{!isError && toast.accountId && (
<Text className='mb-1 font-bold text-white'>{`Credit Account ${toast.accountId}`}</Text>
<Text className='mb-1 font-bold text-white'>{target}</Text>
)}
{showDetailElement && toast.message && (
<Text size='sm' className='w-full mb-1 text-white'>
@@ -16,9 +16,9 @@ export default function TooltipContent(props: Props) {
<div>
<div
className={classNames(
'flex max-w-[320px] flex-1 gap-2 rounded-lg p-3 text-sm shadow-tooltip backdrop-blur-[100px]',
'flex max-w-[320px] flex-1 gap-2 rounded-base p-3 text-sm shadow-tooltip backdrop-blur-[100px]',
'relative isolate max-w-full overflow-hidden',
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-lg before:p-[1px] before:border-glas',
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-base before:p-[1px] before:border-glas',
props.type === 'info' && 'bg-white/10',
props.type === 'warning' && 'bg-warning',
props.type === 'error' && 'bg-error',
+3 -3
View File
@@ -38,7 +38,7 @@ export const Tooltip = (props: Props) => {
placement={props.placement ?? 'top'}
render={() => (
<TooltipContent
hideArrow={props.hideArrow}
hideArrow={props.placement === 'bottom' ? true : props.hideArrow}
type={props.type}
content={props.content}
className={props.contentClassName}
@@ -48,7 +48,7 @@ export const Tooltip = (props: Props) => {
visible={props.visible}
>
{props.children ? (
<span
<div
className={classNames(
props.underline &&
'border-b hover:cursor-help border-dashed border-white/20 pb-1 hover:border-transparent',
@@ -57,7 +57,7 @@ export const Tooltip = (props: Props) => {
)}
>
{props.children}
</span>
</div>
) : (
<span
className={classNames(
-36
View File
@@ -1,36 +0,0 @@
import classNames from 'classnames'
import { NavLink, useParams, useSearchParams } from 'react-router-dom'
import useAccountId from 'hooks/useAccountId'
import { getRoute } from 'utils/route'
const underlineClasses =
'relative before:absolute before:h-[2px] before:-bottom-1 before:left-0 before:right-0 before:gradient-active-tab'
interface Props {
tabs: Tab[]
activeTabIdx: number
}
export default function Tab(props: Props) {
const accountId = useAccountId()
const { address } = useParams()
const [searchParams] = useSearchParams()
return (
<div className='relative w-full'>
{props.tabs.map((tab, index) => (
<NavLink
key={tab.page}
to={getRoute(tab.page, searchParams, address, accountId)}
className={classNames(
props.activeTabIdx === index ? underlineClasses : 'text-white/40',
'relative mr-8 text-xl ',
)}
>
{tab.name}
</NavLink>
))}
</div>
)
}
@@ -1,24 +0,0 @@
import classNames from 'classnames'
import React from 'react'
import { ChevronDown } from 'components/common/Icons'
import Loading from 'components/common/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>
)
}
@@ -1,33 +0,0 @@
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/common/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}
/>
)
}
@@ -1,98 +0,0 @@
import { useCallback } from 'react'
import { ACCOUNT_MENU_BUTTON_ID } from 'components/account/AccountMenuContent'
import Button from 'components/common/Button'
import ActionButton from 'components/common/Button/ActionButton'
import { ArrowDownLine, ArrowUpLine, Enter, ExclamationMarkCircled } from 'components/common/Icons'
import Text from 'components/common/Text'
import { Tooltip } from 'components/common/Tooltip'
import ConditionalWrapper from 'hocs/ConditionalWrapper'
import useAccountId from 'hooks/useAccountId'
import useAlertDialog from 'hooks/useAlertDialog'
import useAutoLend from 'hooks/useAutoLend'
import useCurrentAccountDeposits from 'hooks/useCurrentAccountDeposits'
import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal'
import useStore from 'store'
import { byDenom } from 'utils/array'
interface Props {
data: LendingMarketTableData
}
const buttonClassnames = 'm-0 flex w-40'
const iconClassnames = 'ml-0 mr-1 w-4 h-4'
export default function LendingActionButtons(props: Props) {
const { asset, accountLentValue: accountLendValue } = props.data
const accountDeposits = useCurrentAccountDeposits()
const { openLend, openReclaim } = useLendAndReclaimModal()
const { open: showAlertDialog } = useAlertDialog()
const { isAutoLendEnabledForCurrentAccount } = useAutoLend()
const assetDepositAmount = accountDeposits.find(byDenom(asset.denom))?.amount
const address = useStore((s) => s.address)
const accountId = useAccountId()
const hasNoDeposit = !!(!assetDepositAmount && address && accountId)
const handleUnlend = useCallback(() => {
if (isAutoLendEnabledForCurrentAccount) {
showAlertDialog({
icon: <ExclamationMarkCircled width={18} />,
title: 'Disable Automatically Lend Assets',
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',
icon: <Enter />,
},
negativeButton: {
text: 'Cancel',
},
})
return
}
openReclaim(props.data)
}, [isAutoLendEnabledForCurrentAccount, openReclaim, props.data, showAlertDialog])
return (
<div className='flex flex-row space-x-2'>
{accountLendValue && accountLendValue.isGreaterThan(0) && (
<Button
leftIcon={<ArrowDownLine />}
iconClassName={iconClassnames}
color='secondary'
onClick={handleUnlend}
className={buttonClassnames}
>
Unlend
</Button>
)}
<ConditionalWrapper
condition={hasNoDeposit}
wrapper={(children) => (
<Tooltip
type='warning'
content={
<Text size='sm'>{`You dont have any ${asset.symbol}. Please first deposit ${asset.symbol} into your Credit Account before lending.`}</Text>
}
>
{children}
</Tooltip>
)}
>
<ActionButton
leftIcon={<ArrowUpLine />}
iconClassName={iconClassnames}
disabled={hasNoDeposit}
color='secondary'
onClick={() => openLend(props.data)}
className={buttonClassnames}
text='Lend'
/>
</ConditionalWrapper>
</div>
)
}
@@ -1,42 +0,0 @@
import { Row } from '@tanstack/react-table'
import { useCallback } from 'react'
import LendingActionButtons from 'components/earn/lend/LendingActionButtons'
import { NAME_META } from 'components/earn/lend/Table/Columns/Name'
import useDepositedColumns from 'components/earn/lend/Table/Columns/useDepositedColumns'
import MarketDetails from 'components/common/MarketDetails'
import Table from 'components/common/Table'
import ActionButtonRow from 'components/common/Table/ActionButtonRow'
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}
/>
)
}
@@ -1,7 +1,5 @@
import React from 'react'
import useAvailableColumns from 'components/earn/farm/Table/Columns/useAvailableColumns'
import Table from 'components/common/Table'
import useAvailableColumns from 'components/farm/Table/Columns/useAvailableColumns'
type Props = {
data: Vault[]
@@ -1,6 +1,7 @@
import React from 'react'
import ActionButton from 'components/common/Button/ActionButton'
import { Plus } from 'components/common/Icons'
import Loading from 'components/common/Loading'
import useStore from 'store'
@@ -9,6 +10,8 @@ interface Props {
isLoading: boolean
}
export const DEPOSIT_META = { accessorKey: 'deposit', enableSorting: false, header: '' }
export const Deposit = (props: Props) => {
const { vault } = props
@@ -26,7 +29,12 @@ export const Deposit = (props: Props) => {
return (
<div className='flex items-center justify-end'>
<ActionButton onClick={enterVaultHandler} color='tertiary' text='Deposit' />
<ActionButton
onClick={enterVaultHandler}
color='tertiary'
text='Deposit'
leftIcon={<Plus />}
/>
</div>
)
}
@@ -0,0 +1,115 @@
import moment from 'moment/moment'
import { useCallback, useMemo, useState } from 'react'
import DropDownButton from 'components/common/Button/DropDownButton'
import { AccountArrowDown, LockLocked, LockUnlocked, Plus } from 'components/common/Icons'
import Loading from 'components/common/Loading'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useAccountId from 'hooks/useAccountId'
import useStore from 'store'
import { VaultStatus } from 'types/enums/vault'
export const MANAGE_META = { accessorKey: 'details', enableSorting: false, header: '' }
interface Props {
vault: DepositedVault
isLoading: boolean
isExpanded: boolean
}
export default function Manage(props: Props) {
const accountId = useAccountId()
const address = useStore((s) => s.address)
const withdrawFromVaults = useStore((s) => s.withdrawFromVaults)
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
const [isConfirming, setIsConfirming] = useState(false)
const depositMoreHandler = useCallback(() => {
useStore.setState({
vaultModal: {
vault: props.vault,
isDeposited: true,
selectedBorrowDenoms: [props.vault.denoms.secondary],
isCreate: false,
},
})
}, [props.vault])
const unlockHandler = useCallback(
() => useStore.setState({ unlockModal: { vault: props.vault } }),
[props.vault],
)
const withdrawHandler = useCallback(async () => {
if (!accountId) return
setIsConfirming(true)
await withdrawFromVaults({
accountId: accountId,
vaults: [props.vault],
slippage,
})
}, [accountId, props.vault, slippage, withdrawFromVaults])
const ITEMS: DropDownItem[] = useMemo(
() => [
{
icon: <Plus />,
text: 'Deposit more',
onClick: depositMoreHandler,
},
...(props.vault.status === VaultStatus.ACTIVE
? [
{
icon: <LockUnlocked />,
text: 'Unlock to withdraw',
onClick: unlockHandler,
},
]
: []),
...(props.vault.status === VaultStatus.UNLOCKING
? [
{
icon: <LockLocked />,
text: `Withdraw in ${moment(props.vault?.unlocksAt).fromNow(true)}`,
onClick: () => {},
disabled: true,
disabledTooltip: '',
},
]
: []),
...(props.vault.status === VaultStatus.UNLOCKED
? [
{
icon: <AccountArrowDown />,
text: 'Withdraw funds',
onClick: withdrawHandler,
},
]
: []),
],
[
depositMoreHandler,
props.vault.status,
props.vault?.unlocksAt,
unlockHandler,
withdrawHandler,
],
)
if (props.isLoading) return <Loading />
if (!address) return null
return (
<div className='z-10 flex justify-end'>
<DropDownButton
items={ITEMS}
text='Manage'
color='tertiary'
showProgressIndicator={isConfirming}
/>
</div>
)
}
@@ -1,16 +1,15 @@
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 Apy, { APY_META } from 'components/farm/Table/Columns/Apy'
import { Deposit, DEPOSIT_META } from 'components/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 'components/earn/farm/Table/Columns/Details'
} from 'components/farm/Table/Columns/DepositCap'
import MaxLTV, { LTV_MAX_META } from 'components/farm/Table/Columns/MaxLTV'
import Name, { NAME_META } from 'components/farm/Table/Columns/Name'
import TVL, { TVL_META } from 'components/farm/Table/Columns/TVL'
interface Props {
isLoading: boolean
@@ -41,7 +40,7 @@ export default function useAvailableColumns(props: Props) {
cell: ({ row }) => <MaxLTV vault={row.original as Vault} isLoading={props.isLoading} />,
},
{
...DETAILS_META,
...DEPOSIT_META,
cell: ({ row }) => <Deposit vault={row.original as Vault} isLoading={props.isLoading} />,
},
]
@@ -1,18 +1,16 @@
import { ColumnDef, Row } from '@tanstack/react-table'
import { useMemo } from 'react'
import Apy, { APY_META } from 'components/earn/farm/Table/Columns/Apy'
import Apy, { APY_META } from 'components/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'
} from 'components/farm/Table/Columns/DepositCap'
import Manage, { MANAGE_META } from 'components/farm/Table/Columns/Manage'
import MaxLTV, { LTV_MAX_META } from 'components/farm/Table/Columns/MaxLTV'
import Name, { NAME_META } from 'components/farm/Table/Columns/Name'
import PositionValue, { POSITION_VALUE_META } from 'components/farm/Table/Columns/PositionValue'
import TVL, { TVL_META } from 'components/farm/Table/Columns/TVL'
interface Props {
isLoading: boolean
@@ -55,8 +53,14 @@ export default function useDepositedColumns(props: Props) {
),
},
{
...DETAILS_META,
cell: ({ row }) => <Details isLoading={props.isLoading} isExpanded={row.getIsExpanded()} />,
...MANAGE_META,
cell: ({ row }) => (
<Manage
vault={row.original}
isLoading={props.isLoading}
isExpanded={row.getIsExpanded()}
/>
),
},
]
}, [props.isLoading])
@@ -0,0 +1,20 @@
import Table from 'components/common/Table'
import useDepositedColumns from 'components/farm/Table/Columns/useDepositedColumns'
type Props = {
data: DepositedVault[]
isLoading: boolean
}
export default function DepositedVaultsTable(props: Props) {
const columns = useDepositedColumns({ isLoading: props.isLoading })
return (
<Table
title='Deposited Vaults'
columns={columns}
data={props.data}
initialSorting={[{ id: 'name', desc: true }]}
/>
)
}
@@ -1,8 +1,8 @@
import { Suspense, useMemo } from 'react'
import AvailableVaultsTable from 'components/earn/farm/Table/AvailableVaultsTable'
import DepositedVaultsTable from 'components/earn/farm/Table/DepositedVaultsTable'
import VaultUnlockBanner from 'components/earn/farm/VaultUnlockBanner'
import AvailableVaultsTable from 'components/farm/Table/AvailableVaultsTable'
import DepositedVaultsTable from 'components/farm/Table/DepositedVaultsTable'
import VaultUnlockBanner from 'components/farm/VaultUnlockBanner'
import { BN_ZERO } from 'constants/math'
import useAccountId from 'hooks/useAccountId'
import useChainConfig from 'hooks/useChainConfig'
+3 -3
View File
@@ -36,8 +36,7 @@ export const menuTree = (walletId: WalletID, chainConfig: ChainConfig): MenuTree
],
},
...(chainConfig.perps ? [{ pages: ['perps'] as Page[], label: 'Perps' }] : []),
{ pages: chainConfig.farm ? ['lend', 'farm'] : ['lend'], label: 'Earn' },
{ pages: ['borrow'], label: 'Borrow' },
{ pages: chainConfig.farm ? ['lend', 'farm', 'borrow'] : ['lend'], label: 'Lend & Borrow' },
...(chainConfig.hls ? [{ pages: ['hls-staking'] as Page[], label: 'High Leverage' }] : []),
{ pages: ['portfolio'], label: 'Portfolio' },
{ pages: ['governance'], label: 'Governance', externalUrl: getGovernanceUrl(walletId) },
@@ -49,6 +48,7 @@ export default function DesktopHeader() {
const isOracleStale = useStore((s) => s.isOracleStale)
const isHLS = useStore((s) => s.isHLS)
const accountId = useAccountId()
const showAccountMenu = address && !isHLS
function handleCloseFocusMode() {
if (focusComponent && focusComponent.onClose) focusComponent.onClose()
@@ -92,7 +92,7 @@ export default function DesktopHeader() {
<div className='flex gap-4'>
{showStaleOracle && <OracleResyncButton />}
{accountId && <RewardsCenter />}
{address && !isHLS && <AccountMenu />}
{showAccountMenu && <AccountMenu />}
<Wallet />
<ChainSelect />
<Settings />
+4 -2
View File
@@ -13,9 +13,11 @@ import PortfolioAccountPage from 'pages/PortfolioAccountPage'
import PortfolioPage from 'pages/PortfolioPage'
import TradePage from 'pages/TradePage'
import Layout from 'pages/_layout'
import useStore from 'store'
export default function Routes() {
const chainConfig = useChainConfig()
const isV1 = useStore((s) => s.isV1)
return (
<RoutesWrapper>
<Route
@@ -28,7 +30,7 @@ export default function Routes() {
<Route path='/trade' element={<TradePage />} />
<Route path='/trade-advanced' element={<TradePage />} />
{chainConfig.perps && <Route path='/perps' element={<PerpsPage />} />}
{chainConfig.farm && <Route path='/farm' element={<FarmPage />} />}
{chainConfig.farm && !isV1 && <Route path='/farm' element={<FarmPage />} />}
<Route path='/lend' element={<LendPage />} />
<Route path='/borrow' element={<BorrowPage />} />
<Route path='/portfolio' element={<PortfolioPage />} />
@@ -41,7 +43,7 @@ export default function Routes() {
<Route path='trade' element={<TradePage />} />
<Route path='trade-advanced' element={<TradePage />} />
{chainConfig.perps && <Route path='perps' element={<PerpsPage />} />}
{chainConfig.farm && <Route path='farm' element={<FarmPage />} />}
{chainConfig.farm && !isV1 && <Route path='farm' element={<FarmPage />} />}
<Route path='lend' element={<LendPage />} />
<Route path='borrow' element={<BorrowPage />} />
<Route path='portfolio' element={<PortfolioPage />} />
@@ -1,10 +1,10 @@
import { ColumnDef } from '@tanstack/react-table'
import React, { useMemo } from 'react'
import { 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 DepositCap, { DEPOSIT_CAP_META } from 'components/farm/Table/Columns/DepositCap'
import MaxLTV, { LTV_MAX_META } from 'components/farm/Table/Columns/MaxLTV'
import Name, { NAME_META } from 'components/farm/Table/Columns/Name'
import TVL, { TVL_META } from 'components/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'
@@ -1,6 +1,6 @@
import AvailableLendsTable from 'components/earn/lend/Table/AvailableLendsTable'
import DepositedLendsTable from 'components/earn/lend/Table/DepositedLendsTable'
import useLendingMarketAssetsTableData from 'components/earn/lend/Table/useLendingMarketAssetsTableData'
import AvailableLendsTable from 'components/lend/Table/AvailableLendsTable'
import DepositedLendsTable from 'components/lend/Table/DepositedLendsTable'
import useLendingMarketAssetsTableData from 'components/lend/Table/useLendingMarketAssetsTableData'
import { BN_ZERO } from 'constants/math'
import useMarketEnabledAssets from 'hooks/assets/useMarketEnabledAssets'
@@ -1,12 +1,10 @@
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/common/MarketDetails'
import Table from 'components/common/Table'
import ActionButtonRow from 'components/common/Table/ActionButtonRow'
import { NAME_META } from 'components/lend/Table/Columns/Name'
import useAvailableColumns from 'components/lend/Table/Columns/useAvailableColumns'
type Props = {
data: LendingMarketTableData[]
@@ -19,9 +17,6 @@ export default function AvailableLendsTable(props: Props) {
const renderExpanded = useCallback(
(row: Row<LendingMarketTableData>) => (
<>
<ActionButtonRow row={row}>
<LendingActionButtons data={row.original} />
</ActionButtonRow>
<MarketDetails row={row} type='lend' />
</>
),
@@ -0,0 +1,21 @@
import { ChevronDown, ChevronUp } from 'components/common/Icons'
export const CHEVRON_META = {
id: 'chevron',
enableSorting: false,
header: '',
meta: {
className: 'w-5',
},
}
interface Props {
isExpanded: boolean
}
export default function Chevron(props: Props) {
return (
<div className='flex items-center justify-end'>
<div className='w-4'>{props.isExpanded ? <ChevronUp /> : <ChevronDown />}</div>
</div>
)
}
@@ -10,7 +10,6 @@ export const DEPOSIT_CAP_META = {
accessorKey: 'marketDepositCap',
header: 'Deposit Cap',
id: 'marketDepositCap',
meta: { className: 'w-40' },
}
export const marketDepositCapSortingFn = (
@@ -5,6 +5,7 @@ import { BN_ZERO } from 'constants/math'
import { BN } from 'utils/helpers'
export const DEPOSIT_VALUE_META = {
id: 'accountLentValue',
accessorKey: 'accountLentValue',
header: 'Deposited',
}
@@ -0,0 +1,60 @@
import ActionButton from 'components/common/Button/ActionButton'
import { ArrowUpLine } from 'components/common/Icons'
import Text from 'components/common/Text'
import { Tooltip } from 'components/common/Tooltip'
import ConditionalWrapper from 'hocs/ConditionalWrapper'
import useAccountId from 'hooks/useAccountId'
import useCurrentAccountDeposits from 'hooks/useCurrentAccountDeposits'
import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal'
import useStore from 'store'
import { byDenom } from 'utils/array'
export const LEND_BUTTON_META = {
accessorKey: 'lend',
enableSorting: false,
header: '',
}
interface Props {
data: LendingMarketTableData
}
export default function LendButton(props: Props) {
const { openLend } = useLendAndReclaimModal()
const accountDeposits = useCurrentAccountDeposits()
const assetDepositAmount = accountDeposits.find(byDenom(props.data.asset.denom))?.amount
const address = useStore((s) => s.address)
const accountId = useAccountId()
const hasNoDeposit = !!(!assetDepositAmount && address && accountId)
return (
<div className='flex justify-end'>
<ConditionalWrapper
condition={hasNoDeposit}
wrapper={(children) => (
<Tooltip
type='warning'
content={
<Text size='sm'>{`You dont have any ${props.data.asset.symbol}.
Please first deposit ${props.data.asset.symbol} into your Credit Account before lending.`}</Text>
}
contentClassName='max-w-[200px]'
className='ml-auto'
>
{children}
</Tooltip>
)}
>
<ActionButton
leftIcon={<ArrowUpLine />}
disabled={hasNoDeposit}
color='tertiary'
onClick={(e) => {
openLend(props.data)
e.stopPropagation()
}}
text='Lend'
/>
</ConditionalWrapper>
</div>
)
}
@@ -0,0 +1,82 @@
import { useCallback, useMemo } from 'react'
import { ACCOUNT_MENU_BUTTON_ID } from 'components/account/AccountMenuContent'
import DropDownButton from 'components/common/Button/DropDownButton'
import { ArrowDownLine, ArrowUpLine, Enter, ExclamationMarkCircled } from 'components/common/Icons'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useAlertDialog from 'hooks/useAlertDialog'
import useAutoLend from 'hooks/useAutoLend'
import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal'
import useStore from 'store'
export const MANAGE_META = {
accessorKey: 'manage',
enableSorting: false,
header: '',
}
interface Props {
data: LendingMarketTableData
}
export default function Manage(props: Props) {
const { openLend, openReclaim } = useLendAndReclaimModal()
const { isAutoLendEnabledForCurrentAccount } = useAutoLend()
const { open: showAlertDialog } = useAlertDialog()
const address = useStore((s) => s.address)
const account = useCurrentAccount()
const hasAssetInDeposits = useMemo(
() => !!account?.deposits?.find((deposit) => deposit.denom === props.data.asset.denom),
[account?.deposits, props.data.asset.denom],
)
const handleUnlend = useCallback(() => {
if (isAutoLendEnabledForCurrentAccount) {
showAlertDialog({
icon: <ExclamationMarkCircled width={18} />,
title: 'Disable Automatically Lend Assets',
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',
icon: <Enter />,
},
negativeButton: {
text: 'Cancel',
},
})
return
}
openReclaim(props.data)
}, [isAutoLendEnabledForCurrentAccount, openReclaim, props.data, showAlertDialog])
const ITEMS: DropDownItem[] = useMemo(
() => [
{
icon: <ArrowUpLine />,
text: 'Lend more',
onClick: () => openLend(props.data),
disabled: !hasAssetInDeposits,
disabledTooltip: `You dont have any ${props.data.asset.symbol}.
Please first deposit ${props.data.asset.symbol} into your Credit Account before lending.`,
},
{
icon: <ArrowDownLine />,
text: 'Unlend',
onClick: handleUnlend,
},
],
[handleUnlend, hasAssetInDeposits, openLend, props.data],
)
if (!address) return null
return (
<div className='flex justify-end z-10'>
<DropDownButton items={ITEMS} text='Manage' color='tertiary' />
</div>
)
}
@@ -1,13 +1,14 @@
import { ColumnDef } from '@tanstack/react-table'
import { useMemo } from 'react'
import Apy, { APY_META } from 'components/earn/lend/Table/Columns/Apy'
import Apy, { APY_META } from 'components/lend/Table/Columns/Apy'
import Chevron, { CHEVRON_META } from 'components/lend/Table/Columns/Chevron'
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'
} from 'components/lend/Table/Columns/DepositCap'
import LendButton, { LEND_BUTTON_META } from 'components/lend/Table/Columns/LendButton'
import Name, { NAME_META } from 'components/lend/Table/Columns/Name'
interface Props {
isLoading: boolean
@@ -36,8 +37,12 @@ export default function useAvailableColumns(props: Props) {
sortingFn: marketDepositCapSortingFn,
},
{
...MANAGE_META,
cell: ({ row }) => <Manage isExpanded={row.getIsExpanded()} />,
...LEND_BUTTON_META,
cell: ({ row }) => <LendButton data={row.original} />,
},
{
...CHEVRON_META,
cell: ({ row }) => <Chevron isExpanded={row.getIsExpanded()} />,
},
]
}, [props.isLoading])
@@ -1,20 +1,23 @@
import { ColumnDef } from '@tanstack/react-table'
import { useMemo } from 'react'
import Apy, { APY_META } from 'components/earn/lend/Table/Columns/Apy'
import Apy, { APY_META } from 'components/lend/Table/Columns/Apy'
import Chevron, { CHEVRON_META } from 'components/lend/Table/Columns/Chevron'
import DepositCap, {
DEPOSIT_CAP_META,
marketDepositCapSortingFn,
} from 'components/earn/lend/Table/Columns/DepositCap'
} from 'components/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'
} from 'components/lend/Table/Columns/DepositValue'
import Manage, { MANAGE_META } from 'components/lend/Table/Columns/Manage'
import Name, { NAME_META } from 'components/lend/Table/Columns/Name'
import Action from 'components/v1/Table/deposits/Columns/Action'
interface Props {
isLoading: boolean
v1?: boolean
}
export default function useDepositedColumns(props: Props) {
@@ -48,8 +51,13 @@ export default function useDepositedColumns(props: Props) {
},
{
...MANAGE_META,
cell: ({ row }) => <Manage isExpanded={row.getIsExpanded()} />,
cell: ({ row }) =>
props.v1 ? <Action data={row.original} /> : <Manage data={row.original} />,
},
{
...CHEVRON_META,
cell: ({ row }) => <Chevron isExpanded={row.getIsExpanded()} />,
},
]
}, [props.isLoading])
}, [props.isLoading, props.v1])
}
@@ -0,0 +1,42 @@
import { Row } from '@tanstack/react-table'
import { useCallback } from 'react'
import MarketDetails from 'components/common/MarketDetails'
import Table from 'components/common/Table'
import { DEPOSIT_VALUE_META } from 'components/lend/Table/Columns/DepositValue'
import { NAME_META } from 'components/lend/Table/Columns/Name'
import useDepositedColumns from 'components/lend/Table/Columns/useDepositedColumns'
type Props = {
data: LendingMarketTableData[]
isLoading: boolean
v1?: boolean
}
export default function DepositedLendsTable(props: Props) {
const columns = useDepositedColumns({ isLoading: props.isLoading, v1: props.v1 })
const renderExpanded = useCallback(
(row: Row<LendingMarketTableData>) => <MarketDetails row={row} type='lend' />,
[],
)
if (!props.data.length) return null
return (
<Table
title={props.v1 ? 'Deposits' : 'Lent Assets'}
columns={columns}
data={props.data}
initialSorting={
props.v1
? [
{ id: DEPOSIT_VALUE_META.id, desc: true },
{ id: NAME_META.id, desc: false },
]
: [{ id: NAME_META.id, desc: false }]
}
renderExpanded={renderExpanded}
/>
)
}
+27 -2
View File
@@ -1,10 +1,35 @@
import { useCallback } from 'react'
import { useSearchParams } from 'react-router-dom'
import Table from 'components/common/Table'
import usePerpsBalancesColumns from 'components/perps/BalancesTable/Columns/usePerpsBalancesColumns'
import usePerpsBalancesData from 'components/perps/BalancesTable/usePerpsBalancesData'
import Table from 'components/common/Table'
import { SearchParams } from 'types/enums/searchParams'
import { getSearchParamsObject } from 'utils/route'
export default function PerpsBalancesTable() {
const data = usePerpsBalancesData()
const columns = usePerpsBalancesColumns()
const [searchParams, setSearchParams] = useSearchParams()
return <Table title='Perp Positions' columns={columns} data={data} initialSorting={[]} />
const onClickRow = useCallback(
(denom: string) => {
const params = getSearchParamsObject(searchParams)
setSearchParams({
...params,
[SearchParams.PERPS_MARKET]: denom,
})
},
[searchParams, setSearchParams],
)
return (
<Table
title='Perp Positions'
columns={columns}
data={data}
initialSorting={[]}
onClickRow={onClickRow}
/>
)
}
@@ -5,7 +5,7 @@ import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMar
import Card from 'components/common/Card'
import TableSkeleton from 'components/common/Table/TableSkeleton'
import Text from 'components/common/Text'
import useLendingMarketAssetsTableData from 'components/earn/lend/Table/useLendingMarketAssetsTableData'
import useLendingMarketAssetsTableData from 'components/lend/Table/useLendingMarketAssetsTableData'
import useAccount from 'hooks/accounts/useAccount'
interface Props {

Some files were not shown because too many files have changed in this diff Show More