Compare commits

..

23 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
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 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 Link
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
Bob van der Helm
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
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
Linkie Link
3f28ccd09c
feat: handle URLs with or without trailing slash (#803)
* feat: handle URLs with or without trailing slash

* tidy: cleanup slashes
2024-02-14 09:26:10 +01:00
111 changed files with 1609 additions and 380 deletions

View File

@ -10,7 +10,12 @@ import {
TotalDepositResponse, TotalDepositResponse,
VaultConfigBaseForAddr, VaultConfigBaseForAddr,
} from 'types/generated/mars-params/MarsParams.types' } 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 }> {} 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 previewDepositCache: Cache<{ vaultAddress: string; amount: string }> = new Map()
export const stakingAprCache: Cache<StakingApr[]> = new Map() export const stakingAprCache: Cache<StakingApr[]> = new Map()
export const assetParamsCache: Cache<AssetParamsBaseForAddr[]> = new Map() export const assetParamsCache: Cache<AssetParamsBaseForAddr[]> = new Map()
export const userCollateralCache: Cache<ArrayOfUserCollateralResponse> = new Map()
export const userDebtCache: Cache<ArrayOfUserDebtResponse> = new Map()

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 { MarsOracleOsmosisQueryClient } from 'types/generated/mars-oracle-osmosis/MarsOracleOsmosis.client'
import { MarsParamsQueryClient } from 'types/generated/mars-params/MarsParams.client' import { MarsParamsQueryClient } from 'types/generated/mars-params/MarsParams.client'
import { MarsPerpsQueryClient } from 'types/generated/mars-perps/MarsPerps.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 { MarsSwapperOsmosisQueryClient } from 'types/generated/mars-swapper-osmosis/MarsSwapperOsmosis.client'
import { getUrl } from 'utils/url'
let _cosmWasmClient: Map<string, CosmWasmClient> = new Map() let _cosmWasmClient: Map<string, CosmWasmClient> = new Map()
let _creditManagerQueryClient: Map<string, MarsCreditManagerQueryClient> = 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 _incentivesQueryClient: Map<string, MarsIncentivesQueryClient> = new Map()
let _swapperOsmosisClient: Map<string, MarsSwapperOsmosisQueryClient> = new Map() let _swapperOsmosisClient: Map<string, MarsSwapperOsmosisQueryClient> = new Map()
let _perpsClient: Map<string, MarsPerpsQueryClient> = new Map() let _perpsClient: Map<string, MarsPerpsQueryClient> = new Map()
let _redBankQueryClient: Map<string, MarsRedBankQueryClient> = new Map()
const getClient = async (rpc: string) => { const getClient = async (rpc: string) => {
try { try {
@ -32,7 +35,7 @@ const getClient = async (rpc: string) => {
const getCreditManagerQueryClient = async (chainConfig: ChainConfig) => { const getCreditManagerQueryClient = async (chainConfig: ChainConfig) => {
try { try {
const contract = chainConfig.contracts.creditManager const contract = chainConfig.contracts.creditManager
const rpc = chainConfig.endpoints.rpc const rpc = getUrl(chainConfig.endpoints.rpc)
const key = rpc + contract const key = rpc + contract
if (!_creditManagerQueryClient.get(key)) { if (!_creditManagerQueryClient.get(key)) {
@ -49,7 +52,7 @@ const getCreditManagerQueryClient = async (chainConfig: ChainConfig) => {
const getParamsQueryClient = async (chainConfig: ChainConfig) => { const getParamsQueryClient = async (chainConfig: ChainConfig) => {
try { try {
const contract = chainConfig.contracts.params const contract = chainConfig.contracts.params
const rpc = chainConfig.endpoints.rpc const rpc = getUrl(chainConfig.endpoints.rpc)
const key = rpc + contract const key = rpc + contract
if (!_paramsQueryClient.get(key)) { if (!_paramsQueryClient.get(key)) {
@ -66,7 +69,7 @@ const getParamsQueryClient = async (chainConfig: ChainConfig) => {
const getOracleQueryClient = async (chainConfig: ChainConfig) => { const getOracleQueryClient = async (chainConfig: ChainConfig) => {
try { try {
const contract = chainConfig.contracts.oracle const contract = chainConfig.contracts.oracle
const rpc = chainConfig.endpoints.rpc const rpc = getUrl(chainConfig.endpoints.rpc)
const key = rpc + contract const key = rpc + contract
if (!_oracleQueryClient.get(key)) { if (!_oracleQueryClient.get(key)) {
@ -82,7 +85,7 @@ const getOracleQueryClient = async (chainConfig: ChainConfig) => {
const getVaultQueryClient = async (chainConfig: ChainConfig, address: string) => { const getVaultQueryClient = async (chainConfig: ChainConfig, address: string) => {
try { try {
const client = await getClient(chainConfig.endpoints.rpc) const client = await getClient(getUrl(chainConfig.endpoints.rpc))
return new MarsMockVaultQueryClient(client, address) return new MarsMockVaultQueryClient(client, address)
} catch (error) { } catch (error) {
throw error throw error
@ -92,7 +95,7 @@ const getVaultQueryClient = async (chainConfig: ChainConfig, address: string) =>
const getIncentivesQueryClient = async (chainConfig: ChainConfig) => { const getIncentivesQueryClient = async (chainConfig: ChainConfig) => {
try { try {
const contract = chainConfig.contracts.incentives const contract = chainConfig.contracts.incentives
const rpc = chainConfig.endpoints.rpc const rpc = getUrl(chainConfig.endpoints.rpc)
const key = rpc + contract const key = rpc + contract
if (!_incentivesQueryClient.get(key)) { if (!_incentivesQueryClient.get(key)) {
const client = await getClient(rpc) const client = await getClient(rpc)
@ -108,7 +111,7 @@ const getIncentivesQueryClient = async (chainConfig: ChainConfig) => {
const getSwapperQueryClient = async (chainConfig: ChainConfig) => { const getSwapperQueryClient = async (chainConfig: ChainConfig) => {
try { try {
const contract = chainConfig.contracts.swapper const contract = chainConfig.contracts.swapper
const rpc = chainConfig.endpoints.rpc const rpc = getUrl(chainConfig.endpoints.rpc)
const key = rpc + contract const key = rpc + contract
if (!_swapperOsmosisClient.get(key)) { if (!_swapperOsmosisClient.get(key)) {
const client = await getClient(rpc) const client = await getClient(rpc)
@ -124,7 +127,7 @@ const getSwapperQueryClient = async (chainConfig: ChainConfig) => {
const getPerpsQueryClient = async (chainConfig: ChainConfig) => { const getPerpsQueryClient = async (chainConfig: ChainConfig) => {
try { try {
const contract = chainConfig.contracts.perps const contract = chainConfig.contracts.perps
const rpc = chainConfig.endpoints.rpc const rpc = getUrl(chainConfig.endpoints.rpc)
const key = rpc + contract const key = rpc + contract
if (!_perpsClient.get(key)) { if (!_perpsClient.get(key)) {
const client = await getClient(rpc) 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 { export {
getClient, getClient,
getCreditManagerQueryClient, getCreditManagerQueryClient,
getIncentivesQueryClient, getIncentivesQueryClient,
getOracleQueryClient, getOracleQueryClient,
getParamsQueryClient, getParamsQueryClient,
getPerpsQueryClient,
getRedBankQueryClient,
getSwapperQueryClient, getSwapperQueryClient,
getVaultQueryClient, getVaultQueryClient,
getPerpsQueryClient,
} }

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'))
}

View File

@ -10,12 +10,12 @@ import Text from 'components/common/Text'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider' import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import AssetImage from 'components/common/assets/AssetImage' import AssetImage from 'components/common/assets/AssetImage'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
interface Props { interface Props {
account: Account
asset: Asset asset: Asset
title: string title: string
coinBalances: BNCoin[] coinBalances: BNCoin[]
@ -29,6 +29,7 @@ interface Props {
export default function AssetAmountSelectActionModal(props: Props) { export default function AssetAmountSelectActionModal(props: Props) {
const { const {
account,
asset, asset,
title, title,
coinBalances, coinBalances,
@ -41,7 +42,6 @@ export default function AssetAmountSelectActionModal(props: Props) {
} = props } = props
const [amount, setAmount] = useState(BN_ZERO) const [amount, setAmount] = useState(BN_ZERO)
const maxAmount = BN(coinBalances.find(byDenom(asset.denom))?.amount ?? 0) const maxAmount = BN(coinBalances.find(byDenom(asset.denom))?.amount ?? 0)
const account = useCurrentAccount()
const handleAmountChange = useCallback( const handleAmountChange = useCallback(
(value: BigNumber) => { (value: BigNumber) => {
setAmount(value) setAmount(value)
@ -54,7 +54,6 @@ export default function AssetAmountSelectActionModal(props: Props) {
onAction(amount, amount.isEqualTo(maxAmount)) onAction(amount, amount.isEqualTo(maxAmount))
}, [amount, maxAmount, onAction]) }, [amount, maxAmount, onAction])
if (!account) return
return ( return (
<Modal <Modal
onClose={onClose} onClose={onClose}

View File

@ -35,18 +35,9 @@ interface Props {
modal: BorrowModal 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 }) { function RepayNotAvailable(props: { asset: Asset; repayFromWallet: boolean }) {
return ( return (
<Card className='mt-6'> <Card className='w-full'>
<div className='flex items-start p-4'> <div className='flex items-start p-4'>
<InfoCircle className='w-6 mr-2 flex-0' /> <InfoCircle className='w-6 mr-2 flex-0' />
<div className='flex flex-col flex-1 gap-1'> <div className='flex flex-col flex-1 gap-1'>
@ -90,7 +81,6 @@ function BorrowModal(props: Props) {
const apy = modal.marketData.apy.borrow const apy = modal.marketData.apy.borrow
const isAutoLendEnabled = autoLendEnabledAccountIds.includes(account.id) const isAutoLendEnabled = autoLendEnabledAccountIds.includes(account.id)
const { computeMaxBorrowAmount } = useHealthComputer(account) const { computeMaxBorrowAmount } = useHealthComputer(account)
const totalDebt = BN(getDebtAmount(modal))
const accountDebt = account.debts.find(byDenom(asset.denom))?.amount ?? BN_ZERO const accountDebt = account.debts.find(byDenom(asset.denom))?.amount ?? BN_ZERO
const markets = useMarkets() const markets = useMarkets()
@ -237,7 +227,7 @@ function BorrowModal(props: Props) {
onClose={onClose} onClose={onClose}
header={ header={
<span className='flex items-center gap-4 px-4'> <span className='flex items-center gap-4 px-4'>
{getAssetLogo(modal)} <AssetImage asset={asset} size={24} />
<Text> <Text>
{isRepay ? 'Repay' : 'Borrow'} {asset.symbol} {isRepay ? 'Repay' : 'Borrow'} {asset.symbol}
</Text> </Text>
@ -251,14 +241,14 @@ function BorrowModal(props: Props) {
title={formatPercent(modal.marketData.apy.borrow)} title={formatPercent(modal.marketData.apy.borrow)}
sub={'Borrow Rate APY'} sub={'Borrow Rate APY'}
/> />
{totalDebt.isGreaterThan(0) && ( {accountDebt.isGreaterThan(0) && (
<> <>
<div className='h-100 w-[1px] bg-white/10' /> <div className='h-100 w-[1px] bg-white/10' />
<div className='flex flex-col gap-0.5'> <div className='flex flex-col gap-0.5'>
<div className='flex gap-2'> <div className='flex gap-2'>
<FormattedNumber <FormattedNumber
className='text-xs' className='text-xs'
amount={totalDebt.toNumber()} amount={accountDebt.toNumber()}
options={{ options={{
decimals: asset.decimals, decimals: asset.decimals,
abbreviated: false, abbreviated: false,
@ -267,7 +257,7 @@ function BorrowModal(props: Props) {
/> />
<DisplayCurrency <DisplayCurrency
className='text-xs' className='text-xs'
coin={BNCoin.fromDenomAndBigNumber(asset.denom, totalDebt)} coin={BNCoin.fromDenomAndBigNumber(asset.denom, accountDebt)}
parentheses parentheses
/> />
</div> </div>
@ -303,59 +293,57 @@ function BorrowModal(props: Props) {
<div className='flex items-start flex-1 gap-6 p-6'> <div className='flex items-start flex-1 gap-6 p-6'>
<Card <Card
className='flex flex-1 p-4 bg-white/5' 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
<TokenInputWithSlider asset={asset}
asset={asset} onChange={handleChange}
onChange={handleChange} onDebounce={onDebounce}
onDebounce={onDebounce} amount={amount}
amount={amount} max={max}
max={max} disabled={max.isZero()}
disabled={max.isZero()} className='w-full'
className='w-full' maxText='Max'
maxText='Max' warningMessages={[]}
warningMessages={[]} />
/> {isRepay && maxRepayAmount.isZero() && (
{isRepay && maxRepayAmount.isZero() && ( <RepayNotAvailable asset={asset} repayFromWallet={repayFromWallet} />
<RepayNotAvailable asset={asset} repayFromWallet={repayFromWallet} /> )}
)} {isRepay ? (
{isRepay ? ( <>
<> <Divider />
<Divider className='my-6' /> <div className='flex items-center w-full'>
<div className='flex flex-wrap flex-1'> <div className='flex flex-wrap flex-1'>
<Text className='w-full mb-1'>Repay from Wallet</Text> <Text className='w-full mb-1'>Repay from Wallet</Text>
<Text size='xs' className='text-white/50'> <Text size='xs' className='text-white/50'>
Repay your debt directly from your wallet Repay your debt directly from your wallet
</Text> </Text>
</div> </div>
<div className='flex flex-wrap items-center justify-end'> <Switch
<Switch name='borrow-to-wallet'
name='borrow-to-wallet' checked={repayFromWallet}
checked={repayFromWallet} onChange={setRepayFromWallet}
onChange={setRepayFromWallet} />
/> </div>
</div> </>
</> ) : (
) : ( <>
<> <Divider />
<Divider className='my-6' /> <div className='flex items-center w-full'>
<div className='flex flex-wrap flex-1'> <div className='flex flex-wrap flex-1'>
<Text className='w-full mb-1'>Receive funds to Wallet</Text> <Text className='w-full mb-1'>Receive funds to Wallet</Text>
<Text size='xs' className='text-white/50'> <Text size='xs' className='text-white/50'>
Your borrowed funds will directly go to your wallet Your borrowed funds will directly go to your wallet
</Text> </Text>
</div> </div>
<div className='flex flex-wrap items-center justify-end'> <Switch
<Switch name='borrow-to-wallet'
name='borrow-to-wallet' checked={borrowToWallet}
checked={borrowToWallet} onChange={setBorrowToWallet}
onChange={setBorrowToWallet} />
/> </div>
</div> </>
</> )}
)}
</div>
<Button <Button
onClick={onConfirmClick} onClick={onConfirmClick}
className='w-full' className='w-full'

View File

@ -23,6 +23,7 @@ interface Props {
} }
function LendAndReclaimModal({ currentAccount, config }: Props) { function LendAndReclaimModal({ currentAccount, config }: Props) {
const account = useCurrentAccount()
const lend = useStore((s) => s.lend) const lend = useStore((s) => s.lend)
const reclaim = useStore((s) => s.reclaim) const reclaim = useStore((s) => s.reclaim)
const { close } = useLendAndReclaimModal() const { close } = useLendAndReclaimModal()
@ -65,8 +66,11 @@ function LendAndReclaimModal({ currentAccount, config }: Props) {
}, },
[asset.denom, close, currentAccount.id, isLendAction, lend, reclaim], [asset.denom, close, currentAccount.id, isLendAction, lend, reclaim],
) )
if (!account) return null
return ( return (
<AssetAmountSelectActionModal <AssetAmountSelectActionModal
account={account}
asset={asset} asset={asset}
contentHeader={<DetailsHeader data={data} />} contentHeader={<DetailsHeader data={data} />}
coinBalances={coinBalances} coinBalances={coinBalances}

View File

@ -10,6 +10,8 @@ import {
LendAndReclaimModalController, LendAndReclaimModalController,
SettingsModal, SettingsModal,
UnlockModal, UnlockModal,
V1BorrowAndRepay,
V1DepositAndWithdraw,
VaultModal, VaultModal,
WalletAssets, WalletAssets,
WithdrawFromVaultsModal, WithdrawFromVaultsModal,
@ -32,6 +34,8 @@ export default function ModalsContainer() {
<AlertDialogController /> <AlertDialogController />
<HlsModal /> <HlsModal />
<HlsManageModal /> <HlsManageModal />
<V1DepositAndWithdraw />
<V1BorrowAndRepay />
</> </>
) )
} }

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 BorrowModal } from 'components/Modals/BorrowModal'
export { default as FundAndWithdrawModal } from 'components/Modals/FundWithdraw' export { default as FundAndWithdrawModal } from 'components/Modals/FundWithdraw'
export { default as GetStartedModal } from 'components/Modals/GetStartedModal' 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 LendAndReclaimModalController } from 'components/Modals/LendAndReclaim'
export { default as SettingsModal } from 'components/Modals/Settings' export { default as SettingsModal } from 'components/Modals/Settings'
export { default as UnlockModal } from 'components/Modals/Unlock' export { default as UnlockModal } from 'components/Modals/Unlock'
export { default as VaultModal } from 'components/Modals/Vault' export { default as VaultModal } from 'components/Modals/Vault'
export { default as WalletAssets } from 'components/Modals/WalletAssets' export { default as WalletAssets } from 'components/Modals/WalletAssets'
export { default as WithdrawFromVaultsModal } from 'components/Modals/WithdrawFromVaultsModal' export { default as WithdrawFromVaultsModal } from 'components/Modals/WithdrawFromVaultsModal'
export { default as HlsModal } from 'components/Modals/HLS' export { default as V1BorrowAndRepay } from 'components/Modals/v1/V1BorrowAndRepay'
export { default as HlsManageModal } from 'components/Modals/HLS/Manage' export { default as V1DepositAndWithdraw } from 'components/Modals/v1/V1DepositAndWithdraw'

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>
)
}

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}
/>
)
}

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>
)
}

View File

@ -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} />
}

View File

@ -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} />
}

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}
/>
)
}

View File

@ -7,8 +7,8 @@ import Text from 'components/common/Text'
import { TextLink } from 'components/common/TextLink' import { TextLink } from 'components/common/TextLink'
import { generateToastContent } from 'components/common/Toaster' import { generateToastContent } from 'components/common/Toaster'
import useTransactions from 'hooks/localStorage/useTransactions' import useTransactions from 'hooks/localStorage/useTransactions'
import useStore from 'store'
import useChainConfig from 'hooks/useChainConfig' import useChainConfig from 'hooks/useChainConfig'
import useStore from 'store'
export default function RecentTransactions() { export default function RecentTransactions() {
const address = useStore((s) => s.address) const address = useStore((s) => s.address)
@ -47,7 +47,9 @@ export default function RecentTransactions() {
key={hash} key={hash}
> >
<div className='flex items-start justify-between w-full pb-2'> <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'> <Text size='sm' className='text-white/70'>
{moment.unix(timestamp).format('lll')} {moment.unix(timestamp).format('lll')}
</Text> </Text>

View File

@ -10,6 +10,7 @@ import useChainConfig from 'hooks/useChainConfig'
import useCurrentWallet from 'hooks/useCurrentWallet' import useCurrentWallet from 'hooks/useCurrentWallet'
import useToggle from 'hooks/useToggle' import useToggle from 'hooks/useToggle'
import useStore from 'store' import useStore from 'store'
import { getUrl } from 'utils/url'
interface Props { interface Props {
providerId?: string providerId?: string
@ -53,7 +54,7 @@ export default function WalletConnecting(props: Props) {
setIsConnecting(true) setIsConnecting(true)
try { try {
const response = await connect({ extensionProviderId, chainId: chainConfig.id }) 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 = { const walletClient: WalletClient = {
broadcast, broadcast,
cosmWasmClient: cosmClient, cosmWasmClient: cosmClient,
@ -137,7 +138,7 @@ export default function WalletConnecting(props: Props) {
setIsConnecting(true) setIsConnecting(true)
try { try {
await mobileConnect({ mobileProviderId, chainId: chainConfig.id }) 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 = { const walletClient: WalletClient = {
broadcast, broadcast,
cosmWasmClient: cosmClient, cosmWasmClient: cosmClient,

View File

@ -13,7 +13,7 @@ export default function Wallet() {
const address = useStore((s) => s.address) const address = useStore((s) => s.address)
useEffect(() => { useEffect(() => {
if (address) return if (address && address === currentWallet?.account.address) return
useStore.setState({ useStore.setState({
address: undefined, address: undefined,
userDomain: undefined, userDomain: undefined,

View File

@ -47,7 +47,7 @@ export default function AccountBalancesTable(props: Props) {
const columns = useAccountBalancesColumns(account, showLiquidationPrice) const columns = useAccountBalancesColumns(account, showLiquidationPrice)
if (accountBalanceData.length === 0) if (accountBalanceData.length === 0 && account.id !== address)
return ( return (
<ConditionalWrapper <ConditionalWrapper
condition={!hideCard} condition={!hideCard}

View File

@ -7,7 +7,7 @@ import DisplayCurrency from 'components/common/DisplayCurrency'
import { FormattedNumber } from 'components/common/FormattedNumber' import { FormattedNumber } from 'components/common/FormattedNumber'
import { ArrowRight } from 'components/common/Icons' import { ArrowRight } from 'components/common/Icons'
import Text from 'components/common/Text' 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 { BN_ZERO, MAX_AMOUNT_DECIMALS } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle' import { ORACLE_DENOM } from 'constants/oracle'
import useAllAssets from 'hooks/assets/useAllAssets' import useAllAssets from 'hooks/assets/useAllAssets'

View File

@ -12,18 +12,19 @@ import DisplayCurrency from 'components/common/DisplayCurrency'
import { FormattedNumber } from 'components/common/FormattedNumber' import { FormattedNumber } from 'components/common/FormattedNumber'
import { ThreeDots } from 'components/common/Icons' import { ThreeDots } from 'components/common/Icons'
import Text from 'components/common/Text' 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 { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys' import { LocalStorageKeys } from 'constants/localStorageKeys'
import { ORACLE_DENOM } from 'constants/oracle' import { ORACLE_DENOM } from 'constants/oracle'
import useAccount from 'hooks/accounts/useAccount'
import useAccountIds from 'hooks/accounts/useAccountIds' import useAccountIds from 'hooks/accounts/useAccountIds'
import useAccounts from 'hooks/accounts/useAccounts' import useAccounts from 'hooks/accounts/useAccounts'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount' import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useAllAssets from 'hooks/assets/useAllAssets' import useAllAssets from 'hooks/assets/useAllAssets'
import useLocalStorage from 'hooks/localStorage/useLocalStorage' import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useAccountId from 'hooks/useAccountId' import useAccountId from 'hooks/useAccountId'
import useHealthComputer from 'hooks/useHealthComputer'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets' import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import useHealthComputer from 'hooks/useHealthComputer'
import usePrices from 'hooks/usePrices' import usePrices from 'hooks/usePrices'
import useVaultAprs from 'hooks/vaults/useVaultAprs' import useVaultAprs from 'hooks/vaults/useVaultAprs'
import useStore from 'store' import useStore from 'store'
@ -33,33 +34,40 @@ import {
calculateAccountBalanceValue, calculateAccountBalanceValue,
calculateAccountLeverage, calculateAccountLeverage,
} from 'utils/accounts' } from 'utils/accounts'
import { getPage } from 'utils/route'
export default function AccountDetailsController() { export default function AccountDetailsController() {
const address = useStore((s) => s.address) const address = useStore((s) => s.address)
const isHLS = useStore((s) => s.isHLS) const isHLS = useStore((s) => s.isHLS)
const isV1 = useStore((s) => s.isV1)
const { data: _, isLoading } = useAccounts('default', address) const { data: _, isLoading } = useAccounts('default', address)
const { data: accountIds } = useAccountIds(address, false, true) const { data: accountIds } = useAccountIds(address, false, true)
const accountId = useAccountId() 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 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} page={page} />
return <AccountDetails account={account} />
} }
interface Props { interface Props {
account: Account account: Account
page: Page
} }
function AccountDetails(props: Props) { function AccountDetails(props: Props) {
const { account } = props const { account, page } = props
const location = useLocation()
const { data: hlsStrategies } = useHLSStakingAssets() const { data: hlsStrategies } = useHLSStakingAssets()
const { data: vaultAprs } = useVaultAprs() const { data: vaultAprs } = useVaultAprs()
const [reduceMotion] = useLocalStorage<boolean>( const [reduceMotion] = useLocalStorage<boolean>(
@ -124,10 +132,7 @@ function AccountDetails(props: Props) {
vaultAprs, vaultAprs,
], ],
) )
const isFullWidth = const isFullWidth = page === 'trade' || page === 'perps'
location.pathname.includes('trade') ||
location.pathname === '/' ||
location.pathname.includes('perps')
return ( return (
<> <>

View File

@ -6,7 +6,7 @@ import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMar
import Button from 'components/common/Button' import Button from 'components/common/Button'
import { ArrowDownLine, ArrowUpLine, TrashBin } from 'components/common/Icons' import { ArrowDownLine, ArrowUpLine, TrashBin } from 'components/common/Icons'
import SwitchAutoLend from 'components/common/Switch/SwitchAutoLend' 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 useAccount from 'hooks/accounts/useAccount'
import useAllAssets from 'hooks/assets/useAllAssets' import useAllAssets from 'hooks/assets/useAllAssets'
import useHealthComputer from 'hooks/useHealthComputer' import useHealthComputer from 'hooks/useHealthComputer'

View File

@ -42,6 +42,7 @@ export default function AccountSummaryHeader(props: Props) {
updatedHealthFactor, updatedHealthFactor,
isAccountDetails, isAccountDetails,
} = props } = props
const address = useStore((s) => s.address)
const onClose = useCallback(() => useStore.setState({ accountDetailsExpanded: false }), []) const onClose = useCallback(() => useStore.setState({ accountDetailsExpanded: false }), [])
const accountBalance = useMemo( const accountBalance = useMemo(
() => (account ? calculateAccountBalanceValue(account, prices, assets) : BN_ZERO), () => (account ? calculateAccountBalanceValue(account, prices, assets) : BN_ZERO),
@ -68,10 +69,9 @@ export default function AccountSummaryHeader(props: Props) {
/> />
)} )}
{isAccountDetails && ( {isAccountDetails && (
<Text <Text size='sm' className='w-full pb-1 text-white/50'>
size='sm' {account.id === address ? 'Red Bank' : `Credit Account ${account.id}`}
className='w-full pb-1 text-white/50' </Text>
>{`Credit Account ${account.id}`}</Text>
)} )}
<div className='flex items-end w-full gap-2 pb-2 border-b border-white/5'> <div className='flex items-end w-full gap-2 pb-2 border-b border-white/5'>
<DisplayCurrency <DisplayCurrency

View File

@ -7,7 +7,7 @@ import AccountStrategiesTable from 'components/account/AccountStrategiesTable'
import AccountSummaryHeader from 'components/account/AccountSummary/AccountSummaryHeader' import AccountSummaryHeader from 'components/account/AccountSummary/AccountSummaryHeader'
import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData' import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData'
import Accordion from 'components/common/Accordion' 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 { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys' import { LocalStorageKeys } from 'constants/localStorageKeys'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'

View File

@ -1,19 +1,19 @@
import ActiveBorrowingsTable from 'components/borrow/Table/ActiveBorrowingsTable'
import AvailableBorrowingsTable from 'components/borrow/Table/AvailableBorrowingsTable' import AvailableBorrowingsTable from 'components/borrow/Table/AvailableBorrowingsTable'
import DepositedBorrowingsTable from 'components/borrow/Table/DepositedBorrowingsTable'
import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData' import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import useBorrowEnabledAssets from 'hooks/assets/useBorrowEnabledAssets' import useBorrowEnabledAssets from 'hooks/assets/useBorrowEnabledAssets'
export default function Borrowings() { export default function Borrowings() {
const data = useBorrowMarketAssetsTableData() const { accountBorrowedAssets, availableAssets, allAssets } = useBorrowMarketAssetsTableData()
if (!data?.allAssets?.length) { if (!allAssets?.length) {
return <Fallback /> return <Fallback />
} }
return ( return (
<> <>
<DepositedBorrowingsTable data={data.accountBorrowedAssets} isLoading={false} /> <ActiveBorrowingsTable data={accountBorrowedAssets} isLoading={false} />
<AvailableBorrowingsTable data={data.availableAssets} isLoading={false} /> <AvailableBorrowingsTable data={availableAssets} isLoading={false} />
</> </>
) )
} }

View File

@ -1,18 +1,20 @@
import { Row } from '@tanstack/react-table' import { Row } from '@tanstack/react-table'
import { useCallback } from 'react' import { useCallback } from 'react'
import { DEBT_VALUE_META } from 'components/borrow/Table/Columns/DebtValue'
import { NAME_META } from 'components/borrow/Table/Columns/Name' import { NAME_META } from 'components/borrow/Table/Columns/Name'
import useDepositedColumns from 'components/borrow/Table/Columns/useDepositedColumns' import useBorrowingsColumns from 'components/borrow/Table/Columns/useActiveColumns'
import MarketDetails from 'components/common/MarketDetails' import MarketDetails from 'components/common/MarketDetails'
import Table from 'components/common/Table' import Table from 'components/common/Table'
type Props = { type Props = {
data: BorrowMarketTableData[] data: BorrowMarketTableData[]
isLoading: boolean isLoading: boolean
v1?: boolean
} }
export default function DepositedBorrowingsTable(props: Props) { export default function ActiveBorrowingsTable(props: Props) {
const columns = useDepositedColumns() const columns = useBorrowingsColumns({ v1: props.v1 })
const renderExpanded = useCallback((row: Row<BorrowMarketTableData>) => { const renderExpanded = useCallback((row: Row<BorrowMarketTableData>) => {
return <MarketDetails row={row} type='borrow' /> return <MarketDetails row={row} type='borrow' />
@ -22,10 +24,17 @@ export default function DepositedBorrowingsTable(props: Props) {
return ( return (
<Table <Table
title='Borrowed Assets' title={props.v1 ? 'Borrowings' : 'Borrowed Assets'}
columns={columns} columns={columns}
data={props.data} data={props.data}
initialSorting={[{ id: NAME_META.id, desc: false }]} initialSorting={
props.v1
? [
{ id: DEBT_VALUE_META.id, desc: true },
{ id: NAME_META.id, desc: false },
]
: [{ id: NAME_META.id, desc: false }]
}
renderExpanded={renderExpanded} renderExpanded={renderExpanded}
/> />
) )

View File

@ -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} />
}

View File

@ -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} />
)
}

View File

@ -48,7 +48,7 @@ export default function Manage(props: Props) {
if (!address) return null if (!address) return null
return ( return (
<div className='flex justify-end z-10'> <div className='z-10 flex justify-end'>
<DropDownButton items={ITEMS} text='Manage' color='tertiary' /> <DropDownButton items={ITEMS} text='Manage' color='tertiary' />
</div> </div>
) )

View File

@ -3,15 +3,23 @@ import { useMemo } from 'react'
import BorrowRate, { BORROW_RATE_META } from 'components/borrow/Table/Columns/BorrowRate' import BorrowRate, { BORROW_RATE_META } from 'components/borrow/Table/Columns/BorrowRate'
import Chevron, { CHEVRON_META } from 'components/borrow/Table/Columns/Chevron' import Chevron, { CHEVRON_META } from 'components/borrow/Table/Columns/Chevron'
import Debt, { DEBT_META, debtSortingFn } from 'components/borrow/Table/Columns/Debt' import DebtValue, {
DEBT_VALUE_META,
debtSortingFn,
} from 'components/borrow/Table/Columns/DebtValue'
import Liquidity, { import Liquidity, {
LIQUIDITY_META, LIQUIDITY_META,
liquiditySortingFn, liquiditySortingFn,
} from 'components/borrow/Table/Columns/Liquidity' } from 'components/borrow/Table/Columns/Liquidity'
import Manage, { MANAGE_META } from 'components/borrow/Table/Columns/Manage' import Manage, { MANAGE_META } from 'components/borrow/Table/Columns/Manage'
import Name, { NAME_META } from 'components/borrow/Table/Columns/Name' 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 useMemo<ColumnDef<BorrowMarketTableData>[]>(() => {
return [ return [
{ {
@ -19,8 +27,10 @@ export default function useDepositedColumns() {
cell: ({ row }) => <Name data={row.original} />, cell: ({ row }) => <Name data={row.original} />,
}, },
{ {
...DEBT_META, ...DEBT_VALUE_META,
cell: ({ row }) => <Debt data={row.original} />, cell: ({ row }) => (
<DebtValue asset={row.original.asset} debtAmount={row.original.accountDebtAmount} />
),
sortingFn: debtSortingFn, sortingFn: debtSortingFn,
}, },
{ {
@ -34,12 +44,13 @@ export default function useDepositedColumns() {
}, },
{ {
...MANAGE_META, ...MANAGE_META,
cell: ({ row }) => <Manage data={row.original} />, cell: ({ row }) =>
props.v1 ? <Action data={row.original} /> : <Manage data={row.original} />,
}, },
{ {
...CHEVRON_META, ...CHEVRON_META,
cell: ({ row }) => <Chevron isExpanded={row.getIsExpanded()} />, cell: ({ row }) => <Chevron isExpanded={row.getIsExpanded()} />,
}, },
] ]
}, []) }, [props.v1])
} }

View File

@ -1,11 +1,14 @@
import { useMemo } from 'react' import { useMemo } from 'react'
import { BN_ZERO } from 'constants/math'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount' import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useMarkets from 'hooks/markets/useMarkets' import useMarkets from 'hooks/markets/useMarkets'
import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice'
export default function useBorrowMarketAssetsTableData() { export default function useBorrowMarketAssetsTableData() {
const account = useCurrentAccount() const account = useCurrentAccount()
const markets = useMarkets() const markets = useMarkets()
const { convertAmount } = useDisplayCurrencyPrice()
return useMemo((): { return useMemo((): {
accountBorrowedAssets: BorrowMarketTableData[] accountBorrowedAssets: BorrowMarketTableData[]
@ -18,15 +21,21 @@ export default function useBorrowMarketAssetsTableData() {
markets markets
.filter((market) => market.borrowEnabled) .filter((market) => market.borrowEnabled)
.forEach((market) => { .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 = { const borrowMarketAsset: BorrowMarketTableData = {
...market, ...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 { return {
@ -34,5 +43,5 @@ export default function useBorrowMarketAssetsTableData() {
availableAssets, availableAssets,
allAssets: [...accountBorrowedAssets, ...availableAssets], allAssets: [...accountBorrowedAssets, ...availableAssets],
} }
}, [account?.debts, markets]) }, [account?.debts, markets, convertAmount])
} }

View File

@ -15,19 +15,28 @@ export default function Background() {
) )
const { pathname } = useLocation() const { pathname } = useLocation()
const page = getPage(pathname) const page = getPage(pathname)
const isHLS = useMemo(() => page.split('-')[0] === 'hls', [page]) const [isHLS] = useMemo(() => [page.split('-')[0] === 'hls'], [page])
useEffect(() => { useEffect(() => {
useStore.setState({ isHLS }) useStore.setState({ isHLS: 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 ( return (
<div <div
className={classNames( className={classNames(
'fixed inset-0', 'fixed inset-0',
'w-full h-full', 'w-full h-full',
'overflow-hidden pointer-events-none background ', 'overflow-hidden pointer-events-none background ',
isHLS ? 'bg-body-hls' : 'bg-body', bodyClassName,
!reduceMotion && 'transition-bg duration-1000 delay-300', !reduceMotion && 'transition-bg duration-1000 delay-300',
)} )}
> >
@ -39,7 +48,7 @@ export default function Background() {
'max-h-[500px] max-w-[500px]', 'max-h-[500px] max-w-[500px]',
'left-[-10vw] top-[-10vw]', 'left-[-10vw] top-[-10vw]',
'blur-orb-primary', 'blur-orb-primary',
isHLS ? ' bg-orb-primary-hls' : 'bg-orb-primary', primaryOrbClassName,
'translate-x-0 translate-y-0 rounded-full opacity-20', 'translate-x-0 translate-y-0 rounded-full opacity-20',
!reduceMotion && 'animate-[float_120s_ease-in-out_infinite_2s]', !reduceMotion && 'animate-[float_120s_ease-in-out_infinite_2s]',
!reduceMotion && 'transition-bg duration-1000 delay-300', !reduceMotion && 'transition-bg duration-1000 delay-300',
@ -53,7 +62,7 @@ export default function Background() {
'max-h-[1000px] max-w-[1000px]', 'max-h-[1000px] max-w-[1000px]',
'bottom-[-20vw] right-[-10vw]', 'bottom-[-20vw] right-[-10vw]',
'blur-orb-secondary', 'blur-orb-secondary',
isHLS ? ' bg-orb-secondary-hls' : 'bg-orb-secondary', secondaryOrbClassName,
'translate-x-0 translate-y-0 rounded-full opacity-30', 'translate-x-0 translate-y-0 rounded-full opacity-30',
!reduceMotion && 'transition-bg duration-1000 delay-300', !reduceMotion && 'transition-bg duration-1000 delay-300',
)} )}
@ -66,7 +75,7 @@ export default function Background() {
'max-h-[600px] max-w-[600px]', 'max-h-[600px] max-w-[600px]',
'right-[-4vw] top-[-10vw]', 'right-[-4vw] top-[-10vw]',
'blur-orb-tertiary ', 'blur-orb-tertiary ',
isHLS ? ' bg-orb-tertiary-hls' : 'bg-orb-tertiary', tertiaryOrbClassName,
'translate-x-0 translate-y-0 rounded-full opacity-20', 'translate-x-0 translate-y-0 rounded-full opacity-20',
!reduceMotion && 'animate-[float_180s_ease-in_infinite]', !reduceMotion && 'animate-[float_180s_ease-in_infinite]',
!reduceMotion && 'transition-bg duration-1000 delay-300', !reduceMotion && 'transition-bg duration-1000 delay-300',

View File

@ -9,6 +9,7 @@ import {
GridPlanet, GridPlanet,
GridTire, GridTire,
GridWeb, GridWeb,
Wallet,
} from 'components/common/Icons' } from 'components/common/Icons'
import Text from 'components/common/Text' import Text from 'components/common/Text'
import { Tooltip } from 'components/common/Tooltip' import { Tooltip } from 'components/common/Tooltip'
@ -17,7 +18,7 @@ import useStore from 'store'
interface Props { interface Props {
text: string | ReactNode text: string | ReactNode
children?: ReactNode children?: ReactNode
bg: 'borrow' | 'lend' | 'farm' | 'portfolio' | 'hls-farm' | 'hls-staking' bg: Page
} }
function IntroBackground(props: { bg: Props['bg'] }) { function IntroBackground(props: { bg: Props['bg'] }) {
@ -52,6 +53,12 @@ function IntroBackground(props: { bg: Props['bg'] }) {
<GridPlanet /> <GridPlanet />
</div> </div>
) )
case 'v1':
return (
<div className='absolute top-0 right-0 block w-80 opacity-5'>
<Wallet />
</div>
)
default: default:
return ( return (
<div className='absolute bottom-0 right-0 block w-3/4 md:w-120 opacity-5'> <div className='absolute bottom-0 right-0 block w-3/4 md:w-120 opacity-5'>

View File

@ -23,7 +23,7 @@ export default function MarketDetails({ row, type }: Props) {
symbol: displayCurrencySymbol, symbol: displayCurrencySymbol,
} = useDisplayCurrencyPrice() } = useDisplayCurrencyPrice()
const { asset, ltv, cap, liquidity, deposits, debt } = row.original const { asset, ltv, deposits, debt } = row.original
const details: Detail[] = useMemo(() => { const details: Detail[] = useMemo(() => {
const isDollar = displayCurrencySymbol === '$' const isDollar = displayCurrencySymbol === '$'

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>
)
}

View File

@ -12,11 +12,12 @@ interface Props {
className?: string className?: string
tooltip?: string tooltip?: string
disabled?: boolean disabled?: boolean
placement?: 'top' | 'bottom' | 'left' | 'right'
} }
export default function SwitchWithLabel(props: Props) { export default function SwitchWithLabel(props: Props) {
return ( return (
<div className={classNames('flex w-full', props.className)}> <div className={classNames('flex w-full items-center ', props.className)}>
<div className='flex flex-1'> <div className='flex flex-1'>
<Text className='mr-2 text-white/70' size='sm'> <Text className='mr-2 text-white/70' size='sm'>
{props.label} {props.label}
@ -24,8 +25,9 @@ export default function SwitchWithLabel(props: Props) {
{props.tooltip && ( {props.tooltip && (
<Tooltip <Tooltip
type='info' type='info'
placement={props.placement ?? 'top'}
content={ content={
<Text size='sm' className='px-2 py-3'> <Text size='sm' className='px-2 py-3 max-w-[320px]'>
{props.tooltip} {props.tooltip}
</Text> </Text>
} }

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>
)
}

View File

@ -11,11 +11,11 @@ import { TextLink } from 'components/common/TextLink'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings' import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys' import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLocalStorage from 'hooks/localStorage/useLocalStorage' import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useChainConfig from 'hooks/useChainConfig'
import useTransactionStore from 'hooks/useTransactionStore' import useTransactionStore from 'hooks/useTransactionStore'
import useStore from 'store' import useStore from 'store'
import { formatAmountWithSymbol } from 'utils/formatters' import { formatAmountWithSymbol } from 'utils/formatters'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
import useChainConfig from 'hooks/useChainConfig'
const toastBodyClasses = classNames( const toastBodyClasses = classNames(
'flex flex-wrap w-full group/transaction', 'flex flex-wrap w-full group/transaction',
@ -99,6 +99,13 @@ export default function Toaster() {
if (!isError && toast.accountId) addTransaction(toast) if (!isError && toast.accountId) addTransaction(toast)
const generalMessage = isError ? 'Transaction failed!' : 'Transaction completed successfully!' const generalMessage = isError ? 'Transaction failed!' : 'Transaction completed successfully!'
const showDetailElement = !!(!details && toast.hash) 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 = () => ( const Msg = () => (
<div className='relative flex flex-wrap w-full m-0 isolate'> <div className='relative flex flex-wrap w-full m-0 isolate'>
<div className='flex w-full gap-2 mb-2'> <div className='flex w-full gap-2 mb-2'>
@ -141,7 +148,7 @@ export default function Toaster() {
)} )}
> >
{!isError && toast.accountId && ( {!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 && ( {showDetailElement && toast.message && (
<Text size='sm' className='w-full mb-1 text-white'> <Text size='sm' className='w-full mb-1 text-white'>

View File

@ -38,7 +38,7 @@ export const Tooltip = (props: Props) => {
placement={props.placement ?? 'top'} placement={props.placement ?? 'top'}
render={() => ( render={() => (
<TooltipContent <TooltipContent
hideArrow={props.hideArrow} hideArrow={props.placement === 'bottom' ? true : props.hideArrow}
type={props.type} type={props.type}
content={props.content} content={props.content}
className={props.contentClassName} className={props.contentClassName}

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>
)
}

View File

@ -1,7 +1,5 @@
import React from 'react'
import useAvailableColumns from 'components/earn/farm/Table/Columns/useAvailableColumns'
import Table from 'components/common/Table' import Table from 'components/common/Table'
import useAvailableColumns from 'components/farm/Table/Columns/useAvailableColumns'
type Props = { type Props = {
data: Vault[] data: Vault[]

View File

@ -1,17 +1,16 @@
import moment from 'moment/moment' import moment from 'moment/moment'
import React, { useCallback, useMemo, useState } from 'react' import { useCallback, useMemo, useState } from 'react'
import DropDownButton from 'components/common/Button/DropDownButton'
import { AccountArrowDown, LockLocked, LockUnlocked, Plus } from 'components/common/Icons' import { AccountArrowDown, LockLocked, LockUnlocked, Plus } from 'components/common/Icons'
import Loading from 'components/common/Loading' import Loading from 'components/common/Loading'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useAccountId from 'hooks/useAccountId'
import useStore from 'store'
import { VaultStatus } from 'types/enums/vault' import { VaultStatus } from 'types/enums/vault'
import { DEFAULT_SETTINGS } from '../../../../../constants/defaultSettings'
import { LocalStorageKeys } from '../../../../../constants/localStorageKeys'
import useLocalStorage from '../../../../../hooks/localStorage/useLocalStorage'
import useAccountId from '../../../../../hooks/useAccountId'
import useStore from '../../../../../store'
import DropDownButton from '../../../../common/Button/DropDownButton'
export const MANAGE_META = { accessorKey: 'details', enableSorting: false, header: '' } export const MANAGE_META = { accessorKey: 'details', enableSorting: false, header: '' }
interface Props { interface Props {
@ -104,7 +103,7 @@ export default function Manage(props: Props) {
if (!address) return null if (!address) return null
return ( return (
<div className='flex justify-end z-10'> <div className='z-10 flex justify-end'>
<DropDownButton <DropDownButton
items={ITEMS} items={ITEMS}
text='Manage' text='Manage'

View File

@ -1,16 +1,15 @@
import { ColumnDef } from '@tanstack/react-table' import { ColumnDef } from '@tanstack/react-table'
import { useMemo } from 'react' 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 { Deposit, DEPOSIT_META } from 'components/farm/Table/Columns/Deposit'
import DepositCap, { import DepositCap, {
DEPOSIT_CAP_META, DEPOSIT_CAP_META,
depositCapSortingFn, depositCapSortingFn,
} from 'components/earn/farm/Table/Columns/DepositCap' } from 'components/farm/Table/Columns/DepositCap'
import MaxLTV, { LTV_MAX_META } from 'components/earn/farm/Table/Columns/MaxLTV' import MaxLTV, { LTV_MAX_META } from 'components/farm/Table/Columns/MaxLTV'
import Name, { NAME_META } from 'components/earn/farm/Table/Columns/Name' import Name, { NAME_META } from 'components/farm/Table/Columns/Name'
import TVL, { TVL_META } from 'components/earn/farm/Table/Columns/TVL' import TVL, { TVL_META } from 'components/farm/Table/Columns/TVL'
import { Deposit, DEPOSIT_META } from './Deposit'
interface Props { interface Props {
isLoading: boolean isLoading: boolean

View File

@ -1,18 +1,16 @@
import { ColumnDef, Row } from '@tanstack/react-table' import { ColumnDef, Row } from '@tanstack/react-table'
import { useMemo } from 'react' 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, { import DepositCap, {
DEPOSIT_CAP_META, DEPOSIT_CAP_META,
depositCapSortingFn, depositCapSortingFn,
} from 'components/earn/farm/Table/Columns/DepositCap' } from 'components/farm/Table/Columns/DepositCap'
import Manage, { MANAGE_META } from 'components/earn/farm/Table/Columns/Manage' import Manage, { MANAGE_META } from 'components/farm/Table/Columns/Manage'
import MaxLTV, { LTV_MAX_META } from 'components/earn/farm/Table/Columns/MaxLTV' import MaxLTV, { LTV_MAX_META } from 'components/farm/Table/Columns/MaxLTV'
import Name, { NAME_META } from 'components/earn/farm/Table/Columns/Name' import Name, { NAME_META } from 'components/farm/Table/Columns/Name'
import PositionValue, { import PositionValue, { POSITION_VALUE_META } from 'components/farm/Table/Columns/PositionValue'
POSITION_VALUE_META, import TVL, { TVL_META } from 'components/farm/Table/Columns/TVL'
} from 'components/earn/farm/Table/Columns/PositionValue'
import TVL, { TVL_META } from 'components/earn/farm/Table/Columns/TVL'
interface Props { interface Props {
isLoading: boolean isLoading: boolean

View File

@ -1,7 +1,5 @@
import React from 'react'
import Table from 'components/common/Table' import Table from 'components/common/Table'
import useDepositedColumns from 'components/earn/farm/Table/Columns/useDepositedColumns' import useDepositedColumns from 'components/farm/Table/Columns/useDepositedColumns'
type Props = { type Props = {
data: DepositedVault[] data: DepositedVault[]

View File

@ -1,8 +1,8 @@
import { Suspense, useMemo } from 'react' import { Suspense, useMemo } from 'react'
import AvailableVaultsTable from 'components/earn/farm/Table/AvailableVaultsTable' import AvailableVaultsTable from 'components/farm/Table/AvailableVaultsTable'
import DepositedVaultsTable from 'components/earn/farm/Table/DepositedVaultsTable' import DepositedVaultsTable from 'components/farm/Table/DepositedVaultsTable'
import VaultUnlockBanner from 'components/earn/farm/VaultUnlockBanner' import VaultUnlockBanner from 'components/farm/VaultUnlockBanner'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import useAccountId from 'hooks/useAccountId' import useAccountId from 'hooks/useAccountId'
import useChainConfig from 'hooks/useChainConfig' import useChainConfig from 'hooks/useChainConfig'

View File

@ -36,8 +36,7 @@ export const menuTree = (walletId: WalletID, chainConfig: ChainConfig): MenuTree
], ],
}, },
...(chainConfig.perps ? [{ pages: ['perps'] as Page[], label: 'Perps' }] : []), ...(chainConfig.perps ? [{ pages: ['perps'] as Page[], label: 'Perps' }] : []),
{ pages: chainConfig.farm ? ['lend', 'farm'] : ['lend'], label: 'Earn' }, { pages: chainConfig.farm ? ['lend', 'farm', 'borrow'] : ['lend'], label: 'Lend & Borrow' },
{ pages: ['borrow'], label: 'Borrow' },
...(chainConfig.hls ? [{ pages: ['hls-staking'] as Page[], label: 'High Leverage' }] : []), ...(chainConfig.hls ? [{ pages: ['hls-staking'] as Page[], label: 'High Leverage' }] : []),
{ pages: ['portfolio'], label: 'Portfolio' }, { pages: ['portfolio'], label: 'Portfolio' },
{ pages: ['governance'], label: 'Governance', externalUrl: getGovernanceUrl(walletId) }, { pages: ['governance'], label: 'Governance', externalUrl: getGovernanceUrl(walletId) },
@ -49,6 +48,7 @@ export default function DesktopHeader() {
const isOracleStale = useStore((s) => s.isOracleStale) const isOracleStale = useStore((s) => s.isOracleStale)
const isHLS = useStore((s) => s.isHLS) const isHLS = useStore((s) => s.isHLS)
const accountId = useAccountId() const accountId = useAccountId()
const showAccountMenu = address && !isHLS
function handleCloseFocusMode() { function handleCloseFocusMode() {
if (focusComponent && focusComponent.onClose) focusComponent.onClose() if (focusComponent && focusComponent.onClose) focusComponent.onClose()
@ -92,7 +92,7 @@ export default function DesktopHeader() {
<div className='flex gap-4'> <div className='flex gap-4'>
{showStaleOracle && <OracleResyncButton />} {showStaleOracle && <OracleResyncButton />}
{accountId && <RewardsCenter />} {accountId && <RewardsCenter />}
{address && !isHLS && <AccountMenu />} {showAccountMenu && <AccountMenu />}
<Wallet /> <Wallet />
<ChainSelect /> <ChainSelect />
<Settings /> <Settings />

View File

@ -13,9 +13,11 @@ import PortfolioAccountPage from 'pages/PortfolioAccountPage'
import PortfolioPage from 'pages/PortfolioPage' import PortfolioPage from 'pages/PortfolioPage'
import TradePage from 'pages/TradePage' import TradePage from 'pages/TradePage'
import Layout from 'pages/_layout' import Layout from 'pages/_layout'
import useStore from 'store'
export default function Routes() { export default function Routes() {
const chainConfig = useChainConfig() const chainConfig = useChainConfig()
const isV1 = useStore((s) => s.isV1)
return ( return (
<RoutesWrapper> <RoutesWrapper>
<Route <Route
@ -28,7 +30,7 @@ export default function Routes() {
<Route path='/trade' element={<TradePage />} /> <Route path='/trade' element={<TradePage />} />
<Route path='/trade-advanced' element={<TradePage />} /> <Route path='/trade-advanced' element={<TradePage />} />
{chainConfig.perps && <Route path='/perps' element={<PerpsPage />} />} {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='/lend' element={<LendPage />} />
<Route path='/borrow' element={<BorrowPage />} /> <Route path='/borrow' element={<BorrowPage />} />
<Route path='/portfolio' element={<PortfolioPage />} /> <Route path='/portfolio' element={<PortfolioPage />} />
@ -41,7 +43,7 @@ export default function Routes() {
<Route path='trade' element={<TradePage />} /> <Route path='trade' element={<TradePage />} />
<Route path='trade-advanced' element={<TradePage />} /> <Route path='trade-advanced' element={<TradePage />} />
{chainConfig.perps && <Route path='perps' element={<PerpsPage />} />} {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='lend' element={<LendPage />} />
<Route path='borrow' element={<BorrowPage />} /> <Route path='borrow' element={<BorrowPage />} />
<Route path='portfolio' element={<PortfolioPage />} /> <Route path='portfolio' element={<PortfolioPage />} />

View File

@ -1,10 +1,10 @@
import { ColumnDef } from '@tanstack/react-table' 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 DepositCap, { DEPOSIT_CAP_META } from 'components/farm/Table/Columns/DepositCap'
import MaxLTV, { LTV_MAX_META } from 'components/earn/farm/Table/Columns/MaxLTV' import MaxLTV, { LTV_MAX_META } from 'components/farm/Table/Columns/MaxLTV'
import Name, { NAME_META } from 'components/earn/farm/Table/Columns/Name' import Name, { NAME_META } from 'components/farm/Table/Columns/Name'
import TVL, { TVL_META } from 'components/earn/farm/Table/Columns/TVL' import TVL, { TVL_META } from 'components/farm/Table/Columns/TVL'
import Apy, { APY_META } from 'components/hls/Farm/Table/Columns/APY' import Apy, { APY_META } from 'components/hls/Farm/Table/Columns/APY'
import Deposit, { DEPOSIT_META } from 'components/hls/Farm/Table/Columns/Deposit' import Deposit, { DEPOSIT_META } from 'components/hls/Farm/Table/Columns/Deposit'
import MaxLeverage, { MAX_LEV_META } from 'components/hls/Farm/Table/Columns/MaxLeverage' import MaxLeverage, { MAX_LEV_META } from 'components/hls/Farm/Table/Columns/MaxLeverage'

View File

@ -1,6 +1,6 @@
import AvailableLendsTable from 'components/earn/lend/Table/AvailableLendsTable' import AvailableLendsTable from 'components/lend/Table/AvailableLendsTable'
import DepositedLendsTable from 'components/earn/lend/Table/DepositedLendsTable' import DepositedLendsTable from 'components/lend/Table/DepositedLendsTable'
import useLendingMarketAssetsTableData from 'components/earn/lend/Table/useLendingMarketAssetsTableData' import useLendingMarketAssetsTableData from 'components/lend/Table/useLendingMarketAssetsTableData'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import useMarketEnabledAssets from 'hooks/assets/useMarketEnabledAssets' import useMarketEnabledAssets from 'hooks/assets/useMarketEnabledAssets'

View File

@ -3,8 +3,8 @@ import { useCallback } from 'react'
import MarketDetails from 'components/common/MarketDetails' import MarketDetails from 'components/common/MarketDetails'
import Table from 'components/common/Table' import Table from 'components/common/Table'
import { NAME_META } from 'components/earn/lend/Table/Columns/Name' import { NAME_META } from 'components/lend/Table/Columns/Name'
import useAvailableColumns from 'components/earn/lend/Table/Columns/useAvailableColumns' import useAvailableColumns from 'components/lend/Table/Columns/useAvailableColumns'
type Props = { type Props = {
data: LendingMarketTableData[] data: LendingMarketTableData[]

View File

@ -5,6 +5,7 @@ import { BN_ZERO } from 'constants/math'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
export const DEPOSIT_VALUE_META = { export const DEPOSIT_VALUE_META = {
id: 'accountLentValue',
accessorKey: 'accountLentValue', accessorKey: 'accountLentValue',
header: 'Deposited', header: 'Deposited',
} }

View File

@ -1,14 +1,14 @@
import { ColumnDef } from '@tanstack/react-table' import { ColumnDef } from '@tanstack/react-table'
import { useMemo } from 'react' 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/earn/lend/Table/Columns/Chevron' import Chevron, { CHEVRON_META } from 'components/lend/Table/Columns/Chevron'
import DepositCap, { import DepositCap, {
DEPOSIT_CAP_META, DEPOSIT_CAP_META,
marketDepositCapSortingFn, marketDepositCapSortingFn,
} from 'components/earn/lend/Table/Columns/DepositCap' } from 'components/lend/Table/Columns/DepositCap'
import LendButton, { LEND_BUTTON_META } from 'components/earn/lend/Table/Columns/LendButton' import LendButton, { LEND_BUTTON_META } from 'components/lend/Table/Columns/LendButton'
import Name, { NAME_META } from 'components/earn/lend/Table/Columns/Name' import Name, { NAME_META } from 'components/lend/Table/Columns/Name'
interface Props { interface Props {
isLoading: boolean isLoading: boolean

View File

@ -1,21 +1,23 @@
import { ColumnDef } from '@tanstack/react-table' import { ColumnDef } from '@tanstack/react-table'
import { useMemo } from 'react' 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/earn/lend/Table/Columns/Chevron' import Chevron, { CHEVRON_META } from 'components/lend/Table/Columns/Chevron'
import DepositCap, { import DepositCap, {
DEPOSIT_CAP_META, DEPOSIT_CAP_META,
marketDepositCapSortingFn, marketDepositCapSortingFn,
} from 'components/earn/lend/Table/Columns/DepositCap' } from 'components/lend/Table/Columns/DepositCap'
import DepositValue, { import DepositValue, {
DEPOSIT_VALUE_META, DEPOSIT_VALUE_META,
depositedSortingFn, depositedSortingFn,
} from 'components/earn/lend/Table/Columns/DepositValue' } from 'components/lend/Table/Columns/DepositValue'
import Manage, { MANAGE_META } from 'components/earn/lend/Table/Columns/Manage' import Manage, { MANAGE_META } from 'components/lend/Table/Columns/Manage'
import Name, { NAME_META } from 'components/earn/lend/Table/Columns/Name' import Name, { NAME_META } from 'components/lend/Table/Columns/Name'
import Action from 'components/v1/Table/deposits/Columns/Action'
interface Props { interface Props {
isLoading: boolean isLoading: boolean
v1?: boolean
} }
export default function useDepositedColumns(props: Props) { export default function useDepositedColumns(props: Props) {
@ -49,12 +51,13 @@ export default function useDepositedColumns(props: Props) {
}, },
{ {
...MANAGE_META, ...MANAGE_META,
cell: ({ row }) => <Manage data={row.original} />, cell: ({ row }) =>
props.v1 ? <Action data={row.original} /> : <Manage data={row.original} />,
}, },
{ {
...CHEVRON_META, ...CHEVRON_META,
cell: ({ row }) => <Chevron isExpanded={row.getIsExpanded()} />, cell: ({ row }) => <Chevron isExpanded={row.getIsExpanded()} />,
}, },
] ]
}, [props.isLoading]) }, [props.isLoading, props.v1])
} }

View File

@ -3,16 +3,18 @@ import { useCallback } from 'react'
import MarketDetails from 'components/common/MarketDetails' import MarketDetails from 'components/common/MarketDetails'
import Table from 'components/common/Table' import Table from 'components/common/Table'
import { NAME_META } from 'components/earn/lend/Table/Columns/Name' import { DEPOSIT_VALUE_META } from 'components/lend/Table/Columns/DepositValue'
import useDepositedColumns from 'components/earn/lend/Table/Columns/useDepositedColumns' import { NAME_META } from 'components/lend/Table/Columns/Name'
import useDepositedColumns from 'components/lend/Table/Columns/useDepositedColumns'
type Props = { type Props = {
data: LendingMarketTableData[] data: LendingMarketTableData[]
isLoading: boolean isLoading: boolean
v1?: boolean
} }
export default function DepositedLendsTable(props: Props) { export default function DepositedLendsTable(props: Props) {
const columns = useDepositedColumns({ isLoading: props.isLoading }) const columns = useDepositedColumns({ isLoading: props.isLoading, v1: props.v1 })
const renderExpanded = useCallback( const renderExpanded = useCallback(
(row: Row<LendingMarketTableData>) => <MarketDetails row={row} type='lend' />, (row: Row<LendingMarketTableData>) => <MarketDetails row={row} type='lend' />,
@ -23,10 +25,17 @@ export default function DepositedLendsTable(props: Props) {
return ( return (
<Table <Table
title='Lent Assets' title={props.v1 ? 'Deposits' : 'Lent Assets'}
columns={columns} columns={columns}
data={props.data} data={props.data}
initialSorting={[{ id: NAME_META.id, desc: false }]} initialSorting={
props.v1
? [
{ id: DEPOSIT_VALUE_META.id, desc: true },
{ id: NAME_META.id, desc: false },
]
: [{ id: NAME_META.id, desc: false }]
}
renderExpanded={renderExpanded} renderExpanded={renderExpanded}
/> />
) )

View File

@ -5,7 +5,7 @@ import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMar
import Card from 'components/common/Card' import Card from 'components/common/Card'
import TableSkeleton from 'components/common/Table/TableSkeleton' import TableSkeleton from 'components/common/Table/TableSkeleton'
import Text from 'components/common/Text' 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' import useAccount from 'hooks/accounts/useAccount'
interface Props { interface Props {

View File

@ -3,7 +3,7 @@ import { Suspense, useMemo } from 'react'
import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData' import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData'
import DisplayCurrency from 'components/common/DisplayCurrency' import DisplayCurrency from 'components/common/DisplayCurrency'
import { FormattedNumber } from 'components/common/FormattedNumber' import { FormattedNumber } from 'components/common/FormattedNumber'
import useLendingMarketAssetsTableData from 'components/earn/lend/Table/useLendingMarketAssetsTableData' import useLendingMarketAssetsTableData from 'components/lend/Table/useLendingMarketAssetsTableData'
import Skeleton from 'components/portfolio/SummarySkeleton' import Skeleton from 'components/portfolio/SummarySkeleton'
import { MAX_AMOUNT_DECIMALS } from 'constants/math' import { MAX_AMOUNT_DECIMALS } from 'constants/math'
import useAccount from 'hooks/accounts/useAccount' import useAccount from 'hooks/accounts/useAccount'
@ -17,6 +17,7 @@ import { DEFAULT_PORTFOLIO_STATS } from 'utils/constants'
interface Props { interface Props {
accountId: string accountId: string
v1?: boolean
} }
function Content(props: Props) { function Content(props: Props) {
@ -45,15 +46,25 @@ function Content(props: Props) {
return [ return [
{ {
title: <DisplayCurrency className='text-xl' coin={positionValue} />, title: (
<DisplayCurrency
className='text-xl'
coin={positionValue}
options={{ abbreviated: false }}
/>
),
sub: DEFAULT_PORTFOLIO_STATS[0].sub, sub: DEFAULT_PORTFOLIO_STATS[0].sub,
}, },
{ {
title: <DisplayCurrency className='text-xl' coin={debts} />, title: (
<DisplayCurrency className='text-xl' coin={debts} options={{ abbreviated: false }} />
),
sub: DEFAULT_PORTFOLIO_STATS[1].sub, sub: DEFAULT_PORTFOLIO_STATS[1].sub,
}, },
{ {
title: <DisplayCurrency className='text-xl' coin={netWorth} />, title: (
<DisplayCurrency className='text-xl' coin={netWorth} options={{ abbreviated: false }} />
),
sub: DEFAULT_PORTFOLIO_STATS[2].sub, sub: DEFAULT_PORTFOLIO_STATS[2].sub,
}, },
{ {
@ -74,21 +85,23 @@ function Content(props: Props) {
title: ( title: (
<FormattedNumber <FormattedNumber
className='text-xl' className='text-xl'
amount={leverage.toNumber()} amount={isNaN(leverage.toNumber()) ? 1 : leverage.toNumber()}
options={{ suffix: 'x' }} options={{ suffix: 'x' }}
/> />
), ),
sub: DEFAULT_PORTFOLIO_STATS[4].sub, sub: props.v1 ? 'Total Leverage' : DEFAULT_PORTFOLIO_STATS[4].sub,
}, },
] ]
}, [account, assets, borrowAssets, hlsStrategies, lendingAssets, prices, vaultAprs]) }, [account, assets, borrowAssets, hlsStrategies, lendingAssets, prices, vaultAprs, props.v1])
if (props.v1 && account?.lends.length == 0) return null
return ( return (
<Skeleton <Skeleton
stats={stats} stats={stats}
health={health} health={health}
healthFactor={healthFactor} healthFactor={healthFactor}
title={`Credit Account ${props.accountId}`} title={props.v1 ? 'Wallet Mode Portfolio' : `Credit Account ${props.accountId}`}
accountId={props.accountId} accountId={props.accountId}
/> />
) )

View File

@ -5,7 +5,7 @@ import { NavLink, useParams, useSearchParams } from 'react-router-dom'
import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData' import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData'
import { FormattedNumber } from 'components/common/FormattedNumber' import { FormattedNumber } from 'components/common/FormattedNumber'
import Loading from 'components/common/Loading' import Loading from 'components/common/Loading'
import useLendingMarketAssetsTableData from 'components/earn/lend/Table/useLendingMarketAssetsTableData' import useLendingMarketAssetsTableData from 'components/lend/Table/useLendingMarketAssetsTableData'
import Skeleton from 'components/portfolio/Card/Skeleton' import Skeleton from 'components/portfolio/Card/Skeleton'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings' import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys' import { LocalStorageKeys } from 'constants/localStorageKeys'

View File

@ -4,9 +4,10 @@ import { useParams } from 'react-router-dom'
import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData' import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData'
import DisplayCurrency from 'components/common/DisplayCurrency' import DisplayCurrency from 'components/common/DisplayCurrency'
import { FormattedNumber } from 'components/common/FormattedNumber' import { FormattedNumber } from 'components/common/FormattedNumber'
import useLendingMarketAssetsTableData from 'components/earn/lend/Table/useLendingMarketAssetsTableData' import useLendingMarketAssetsTableData from 'components/lend/Table/useLendingMarketAssetsTableData'
import SummarySkeleton from 'components/portfolio/SummarySkeleton' import SummarySkeleton from 'components/portfolio/SummarySkeleton'
import { MAX_AMOUNT_DECIMALS } from 'constants/math' import { MAX_AMOUNT_DECIMALS } from 'constants/math'
import useAccount from 'hooks/accounts/useAccount'
import useAccounts from 'hooks/accounts/useAccounts' import useAccounts from 'hooks/accounts/useAccounts'
import useAllAssets from 'hooks/assets/useAllAssets' import useAllAssets from 'hooks/assets/useAllAssets'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets' import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
@ -27,8 +28,11 @@ export default function PortfolioSummary() {
const { data: hlsStrategies } = useHLSStakingAssets() const { data: hlsStrategies } = useHLSStakingAssets()
const { data: vaultAprs } = useVaultAprs() const { data: vaultAprs } = useVaultAprs()
const assets = useAllAssets() const assets = useAllAssets()
const { data: v1Account } = useAccount(urlAddress)
const stats = useMemo(() => { const stats = useMemo(() => {
if (!accounts?.length) return if (!accounts?.length) return
if (v1Account && !accounts.find((account) => account.id === urlAddress))
accounts.push(v1Account)
const combinedAccount = accounts.reduce( const combinedAccount = accounts.reduce(
(combinedAccount, account) => { (combinedAccount, account) => {
combinedAccount.debts = combinedAccount.debts.concat(account.debts) combinedAccount.debts = combinedAccount.debts.concat(account.debts)
@ -60,15 +64,25 @@ export default function PortfolioSummary() {
return [ return [
{ {
title: <DisplayCurrency className='text-xl' coin={positionValue} />, title: (
<DisplayCurrency
className='text-xl'
coin={positionValue}
options={{ abbreviated: false }}
/>
),
sub: DEFAULT_PORTFOLIO_STATS[0].sub, sub: DEFAULT_PORTFOLIO_STATS[0].sub,
}, },
{ {
title: <DisplayCurrency className='text-xl' coin={debts} />, title: (
<DisplayCurrency className='text-xl' coin={debts} options={{ abbreviated: false }} />
),
sub: DEFAULT_PORTFOLIO_STATS[1].sub, sub: DEFAULT_PORTFOLIO_STATS[1].sub,
}, },
{ {
title: <DisplayCurrency className='text-xl' coin={netWorth} />, title: (
<DisplayCurrency className='text-xl' coin={netWorth} options={{ abbreviated: false }} />
),
sub: DEFAULT_PORTFOLIO_STATS[2].sub, sub: DEFAULT_PORTFOLIO_STATS[2].sub,
}, },
{ {
@ -89,14 +103,14 @@ export default function PortfolioSummary() {
title: ( title: (
<FormattedNumber <FormattedNumber
className='text-xl' className='text-xl'
amount={leverage.toNumber()} amount={isNaN(leverage.toNumber()) ? 1 : leverage.toNumber()}
options={{ suffix: 'x' }} options={{ suffix: 'x' }}
/> />
), ),
sub: 'Combined leverage', sub: 'Combined leverage',
}, },
] ]
}, [accounts, assets, borrowAssets, hlsStrategies, lendingAssets, prices, vaultAprs]) }, [accounts, assets, borrowAssets, hlsStrategies, lendingAssets, prices, vaultAprs, v1Account])
if (!walletAddress && !urlAddress) return null if (!walletAddress && !urlAddress) return null

View File

@ -2,7 +2,7 @@ import { useMemo } from 'react'
import AccountBalancesTable from 'components/account/AccountBalancesTable' import AccountBalancesTable from 'components/account/AccountBalancesTable'
import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData' import useBorrowMarketAssetsTableData from 'components/borrow/Table/useBorrowMarketAssetsTableData'
import useLendingMarketAssetsTableData from 'components/earn/lend/Table/useLendingMarketAssetsTableData' import useLendingMarketAssetsTableData from 'components/lend/Table/useLendingMarketAssetsTableData'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount' import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
export default function AccountDetailsCard() { export default function AccountDetailsCard() {

View File

@ -0,0 +1,47 @@
import BorrowingsTable from 'components/borrow/Table/ActiveBorrowingsTable'
import useV1BorrowingsTableData from 'components/v1/Table/useV1BorrowingsTableData'
import { BN_ZERO } from 'constants/math'
import useBorrowEnabledAssets from 'hooks/assets/useBorrowEnabledAssets'
export default function Borrowings() {
const { debtAssets } = useV1BorrowingsTableData()
if (!debtAssets?.length) {
return <Fallback />
}
return (
<>
<BorrowingsTable data={debtAssets} isLoading={false} v1 />
</>
)
}
function Fallback() {
const assets = useBorrowEnabledAssets()
const data: BorrowMarketTableData[] = assets.map((asset) => ({
asset,
apy: {
borrow: 0,
deposit: 0,
},
ltv: {
max: 0,
liq: 0,
},
liquidity: BN_ZERO,
marketLiquidityRate: 0,
cap: {
denom: asset.denom,
max: BN_ZERO,
used: BN_ZERO,
},
debt: BN_ZERO,
borrowEnabled: true,
depositEnabled: true,
deposits: BN_ZERO,
accountDebt: BN_ZERO,
}))
return <BorrowingsTable data={data} isLoading v1 />
}

View File

@ -0,0 +1,46 @@
import DepositsTable from 'components/lend/Table/DepositedLendsTable'
import useV1DepositsTableData from 'components/v1/Table/useV1DepositsTableData'
import { BN_ZERO } from 'constants/math'
import useMarketEnabledAssets from 'hooks/assets/useMarketEnabledAssets'
export default function Deposits() {
const { depositAssets } = useV1DepositsTableData()
if (!depositAssets?.length) {
return <Fallback />
}
return (
<>
<DepositsTable data={depositAssets} isLoading={false} v1 />
</>
)
}
function Fallback() {
const assets = useMarketEnabledAssets()
const data: LendingMarketTableData[] = assets.map((asset) => ({
asset,
borrowEnabled: true,
depositEnabled: true,
debt: BN_ZERO,
deposits: BN_ZERO,
liquidity: BN_ZERO,
cap: {
max: BN_ZERO,
used: BN_ZERO,
denom: asset.denom,
},
apy: {
borrow: 0,
deposit: 0,
},
ltv: {
max: 0,
liq: 0,
},
}))
return <DepositsTable data={data} isLoading v1 />
}

View File

@ -0,0 +1,20 @@
import BorrowButton from 'components/v1/Table/borrowings/Columns/BorrowButton'
import Manage from 'components/v1/Table/borrowings/Columns/Manage'
export const MANAGE_META = {
accessorKey: 'manage',
enableSorting: false,
header: '',
}
interface Props {
data: BorrowMarketTableData
}
export default function Action(props: Props) {
const hasDebt = !props.data.accountDebtAmount?.isZero() ?? false
if (hasDebt) return <Manage data={props.data} />
return <BorrowButton data={props.data} />
}

View File

@ -0,0 +1,50 @@
import ActionButton from 'components/common/Button/ActionButton'
import { Plus } from 'components/common/Icons'
import Text from 'components/common/Text'
import { Tooltip } from 'components/common/Tooltip'
import ConditionalWrapper from 'hocs/ConditionalWrapper'
import useAccount from 'hooks/accounts/useAccount'
import useStore from 'store'
interface Props {
data: BorrowMarketTableData
}
export default function BorrowButton(props: Props) {
const address = useStore((s) => s.address)
const { data: account } = useAccount(address)
const hasCollateral = account?.lends?.length ?? 0 > 0
return (
<div className='flex justify-end'>
<ConditionalWrapper
condition={!hasCollateral}
wrapper={(children) => (
<Tooltip
type='warning'
content={
<Text size='sm'>{`You dont have assets deposited in the Red Bank. Please deposit assets before you borrow.`}</Text>
}
contentClassName='max-w-[200px]'
className='ml-auto'
>
{children}
</Tooltip>
)}
>
<ActionButton
leftIcon={<Plus />}
disabled={!hasCollateral}
color='tertiary'
onClick={(e) => {
useStore.setState({
v1BorrowAndRepayModal: { type: 'borrow', data: props.data },
})
e.stopPropagation()
}}
text='Borrow'
/>
</ConditionalWrapper>
</div>
)
}

View File

@ -0,0 +1,47 @@
import { useMemo } from 'react'
import DropDownButton from 'components/common/Button/DropDownButton'
import { HandCoins, Plus } from 'components/common/Icons'
import useWalletBalances from 'hooks/useWalletBalances'
import useStore from 'store'
import { byDenom } from 'utils/array'
interface Props {
data: BorrowMarketTableData
}
export default function Manage(props: Props) {
const address = useStore((s) => s.address)
const { data: balances } = useWalletBalances(address)
const hasBalance = !!balances.find(byDenom(props.data.asset.denom))
const ITEMS: DropDownItem[] = useMemo(
() => [
{
icon: <Plus />,
text: 'Borrow more',
onClick: () =>
useStore.setState({
v1BorrowAndRepayModal: { type: 'borrow', data: props.data },
}),
},
{
icon: <HandCoins />,
text: 'Repay',
onClick: () =>
useStore.setState({
v1BorrowAndRepayModal: { type: 'repay', data: props.data },
}),
disabled: !hasBalance,
disabledTooltip: `You dont have any ${props.data.asset.symbol} in your Wallet.`,
},
],
[hasBalance, props.data],
)
return (
<div className='z-10 flex justify-end'>
<DropDownButton items={ITEMS} text='Manage' color='tertiary' />
</div>
)
}

View File

@ -0,0 +1,19 @@
import DepositButton from 'components/v1/Table/deposits/Columns/DepositButton'
import Manage from 'components/v1/Table/deposits/Columns/Manage'
export const MANAGE_META = {
accessorKey: 'manage',
enableSorting: false,
header: '',
}
interface Props {
data: LendingMarketTableData
}
export default function Action(props: Props) {
const hasDeposits = !props.data.accountLentAmount?.isZero() ?? false
if (hasDeposits) return <Manage data={props.data} />
return <DepositButton data={props.data} />
}

View File

@ -0,0 +1,50 @@
import ActionButton from 'components/common/Button/ActionButton'
import { ArrowUpLine } from 'components/common/Icons'
import Text from 'components/common/Text'
import { Tooltip } from 'components/common/Tooltip'
import ConditionalWrapper from 'hocs/ConditionalWrapper'
import useWalletBalances from 'hooks/useWalletBalances'
import useStore from 'store'
import { byDenom } from 'utils/array'
interface Props {
data: LendingMarketTableData
}
export default function DepositButton(props: Props) {
const address = useStore((s) => s.address)
const { data: balances } = useWalletBalances(address)
const hasBalance = !!balances.find(byDenom(props.data.asset.denom))
return (
<div className='flex justify-end'>
<ConditionalWrapper
condition={!hasBalance}
wrapper={(children) => (
<Tooltip
type='warning'
content={
<Text size='sm'>{`You dont have any ${props.data.asset.symbol} in your Wallet.`}</Text>
}
contentClassName='max-w-[200px]'
className='ml-auto'
>
{children}
</Tooltip>
)}
>
<ActionButton
leftIcon={<ArrowUpLine />}
disabled={!hasBalance}
color='tertiary'
onClick={(e) => {
useStore.setState({
v1DepositAndWithdrawModal: { type: 'deposit', data: props.data },
})
e.stopPropagation()
}}
text='Deposit'
/>
</ConditionalWrapper>
</div>
)
}

View File

@ -0,0 +1,48 @@
import { useMemo } from 'react'
import DropDownButton from 'components/common/Button/DropDownButton'
import { ArrowDownLine, ArrowUpLine } from 'components/common/Icons'
import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal'
import useWalletBalances from 'hooks/useWalletBalances'
import useStore from 'store'
import { byDenom } from 'utils/array'
interface Props {
data: LendingMarketTableData
}
export default function Manage(props: Props) {
const address = useStore((s) => s.address)
const { data: balances } = useWalletBalances(address)
const hasBalance = !!balances.find(byDenom(props.data.asset.denom))
const ITEMS: DropDownItem[] = useMemo(
() => [
{
icon: <ArrowUpLine />,
text: 'Deposit more',
onClick: () =>
useStore.setState({
v1DepositAndWithdrawModal: { type: 'deposit', data: props.data },
}),
disabled: !hasBalance,
disabledTooltip: `You dont have any ${props.data.asset.symbol} in your Wallet.`,
},
{
icon: <ArrowDownLine />,
text: 'Withdraw',
onClick: () =>
useStore.setState({
v1DepositAndWithdrawModal: { type: 'withdraw', data: props.data },
}),
},
],
[hasBalance, props.data],
)
return (
<div className='z-10 flex justify-end'>
<DropDownButton items={ITEMS} text='Manage' color='tertiary' />
</div>
)
}

View File

@ -0,0 +1,38 @@
import { useMemo } from 'react'
import { BN_ZERO } from 'constants/math'
import useAccount from 'hooks/accounts/useAccount'
import useMarkets from 'hooks/markets/useMarkets'
import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice'
import useStore from 'store'
export default function useV1BorrowingsTableData() {
const address = useStore((s) => s.address)
const markets = useMarkets()
const { data: v1Positions } = useAccount(address)
const { convertAmount } = useDisplayCurrencyPrice()
return useMemo((): {
debtAssets: BorrowMarketTableData[]
} => {
const userDebts = v1Positions?.debts ?? []
const debtAssets: BorrowMarketTableData[] = []
markets
.filter((market) => market.borrowEnabled)
.forEach((market) => {
const amount =
userDebts.find((debt) => debt.denom === market.asset.denom)?.amount ?? BN_ZERO
const value = amount ? convertAmount(market.asset, amount) : undefined
const borrowMarketAsset: BorrowMarketTableData = {
...market,
accountDebtAmount: amount,
accountDebtValue: value,
}
debtAssets.push(borrowMarketAsset)
})
return { debtAssets }
}, [v1Positions, markets, convertAmount])
}

View File

@ -0,0 +1,39 @@
import { useMemo } from 'react'
import { BN_ZERO } from 'constants/math'
import useAccount from 'hooks/accounts/useAccount'
import useMarkets from 'hooks/markets/useMarkets'
import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice'
import useStore from 'store'
import { byDenom } from 'utils/array'
export default function useV1DepositsTableData(): {
depositAssets: LendingMarketTableData[]
} {
const address = useStore((s) => s.address)
const markets = useMarkets()
const { data: v1Positions } = useAccount(address)
const { convertAmount } = useDisplayCurrencyPrice()
return useMemo(() => {
const depositAssets: LendingMarketTableData[] = []
const userCollateral = v1Positions?.lends ?? []
markets.forEach((market) => {
const amount = userCollateral.find(byDenom(market.asset.denom))?.amount ?? BN_ZERO
const value = amount ? convertAmount(market.asset, amount) : undefined
const lendingMarketAsset: LendingMarketTableData = {
...market,
accountLentValue: value,
accountLentAmount: amount,
}
depositAssets.push(lendingMarketAsset)
})
return {
depositAssets,
}
}, [markets, v1Positions, convertAmount])
}

View File

@ -0,0 +1,21 @@
import WalletConnectButton from 'components/Wallet/WalletConnectButton'
import Intro from 'components/common/Intro'
import useStore from 'store'
export default function V1Intro() {
const address = useStore((state) => state.address)
return (
<Intro
text={
<>
The <span className='text-white'>Wallet Mode</span> provides simple lending and borrowing
without the use of Credit Accounts. Deposits{' '}
<span className='text-white'>can&lsquo;t be used</span> in the Trade or Farm interface.
</>
}
bg='v1'
>
{!address && <WalletConnectButton className='mt-4' />}
</Intro>
)
}

View File

@ -1,5 +1,6 @@
export const EARN_TABS: Tab[] = [ export const LEND_AND_BORROW_TABS: Tab[] = [
{ page: 'lend', name: 'Lend' }, { page: 'lend', name: 'Lend' },
{ page: 'borrow', name: 'Borrow' },
{ page: 'farm', name: 'Farm' }, { page: 'farm', name: 'Farm' },
] ]

View File

@ -1,14 +1,22 @@
import useSWR from 'swr' import useSWR from 'swr'
import getAccount from 'api/accounts/getAccount' import getAccount from 'api/accounts/getAccount'
import getV1Positions from 'api/v1/getV1Positions'
import useChainConfig from 'hooks/useChainConfig' import useChainConfig from 'hooks/useChainConfig'
import useStore from 'store'
export default function useAccount(accountId?: string, suspense?: boolean) { export default function useAccount(accountId?: string, suspense?: boolean) {
const chainConfig = useChainConfig() const chainConfig = useChainConfig()
const address = useStore((s) => s.address)
const isV1 = accountId === address
const cacheKey = isV1
? `chains/${chainConfig.id}/v1/user/${accountId}`
: `chains/${chainConfig.id}/accounts/${accountId}`
return useSWR( return useSWR(
accountId && `chains/${chainConfig.id}/accounts/${accountId}`, accountId && cacheKey,
() => getAccount(chainConfig, accountId), () => (isV1 ? getV1Positions(chainConfig, accountId) : getAccount(chainConfig, accountId)),
{ {
suspense: suspense, suspense: suspense,
revalidateOnFocus: false, revalidateOnFocus: false,

View File

@ -11,6 +11,7 @@ import { MarsParamsQueryClient } from 'types/generated/mars-params/MarsParams.cl
import { MarsPerpsQueryClient } from 'types/generated/mars-perps/MarsPerps.client' import { MarsPerpsQueryClient } from 'types/generated/mars-perps/MarsPerps.client'
import { MarsRedBankQueryClient } from 'types/generated/mars-red-bank/MarsRedBank.client' import { MarsRedBankQueryClient } from 'types/generated/mars-red-bank/MarsRedBank.client'
import { MarsSwapperOsmosisQueryClient } from 'types/generated/mars-swapper-osmosis/MarsSwapperOsmosis.client' import { MarsSwapperOsmosisQueryClient } from 'types/generated/mars-swapper-osmosis/MarsSwapperOsmosis.client'
import { getUrl } from 'utils/url'
export default function useClients() { export default function useClients() {
const chainConfig = useChainConfig() const chainConfig = useChainConfig()
@ -18,7 +19,7 @@ export default function useClients() {
const swr = useSWR( const swr = useSWR(
`chains/${chainConfig.id}/clients`, `chains/${chainConfig.id}/clients`,
async () => { async () => {
const client = await CosmWasmClient.connect(chainConfig.endpoints.rpc) const client = await CosmWasmClient.connect(getUrl(chainConfig.endpoints.rpc))
return { return {
creditManager: new MarsCreditManagerQueryClient( creditManager: new MarsCreditManagerQueryClient(

View File

@ -1,13 +1,19 @@
import Borrowings from 'components/borrow/Borrowings'
import BorrowIntro from 'components/borrow/BorrowIntro' import BorrowIntro from 'components/borrow/BorrowIntro'
import MigrationBanner from 'components/common/MigrationBanner' import Borrowings from 'components/borrow/Borrowings'
import Tab from 'components/common/Tab'
import V1Borrowings from 'components/v1/Borrowings'
import V1Intro from 'components/v1/V1Intro'
import { LEND_AND_BORROW_TABS } from 'constants/pages'
import useStore from 'store'
export default function BorrowPage() { export default function BorrowPage() {
const isV1 = useStore((s) => s.isV1)
return ( return (
<div className='flex flex-wrap w-full gap-6'> <div className='flex flex-wrap w-full gap-6'>
<MigrationBanner /> <Tab tabs={LEND_AND_BORROW_TABS} activeTabIdx={1} showV1Toggle />
<BorrowIntro /> {isV1 ? <V1Intro /> : <BorrowIntro />}
<Borrowings /> {isV1 ? <V1Borrowings /> : <Borrowings />}
</div> </div>
) )
} }

View File

@ -1,14 +1,12 @@
import FarmIntro from 'components/earn/farm/FarmIntro' import Tab from 'components/common/Tab'
import Vaults from 'components/earn/farm/Vaults' import FarmIntro from 'components/farm/FarmIntro'
import Tab from 'components/earn/Tab' import Vaults from 'components/farm/Vaults'
import MigrationBanner from 'components/common/MigrationBanner' import { LEND_AND_BORROW_TABS } from 'constants/pages'
import { EARN_TABS } from 'constants/pages'
export default function FarmPage() { export default function FarmPage() {
return ( return (
<div className='flex flex-wrap w-full gap-6'> <div className='flex flex-wrap w-full gap-6'>
<MigrationBanner /> <Tab tabs={LEND_AND_BORROW_TABS} activeTabIdx={2} showV1Toggle />
<Tab tabs={EARN_TABS} activeTabIdx={1} />
<FarmIntro /> <FarmIntro />
<Vaults /> <Vaults />
</div> </div>

View File

@ -1,13 +1,11 @@
import Tab from 'components/earn/Tab' import Tab from 'components/common/Tab'
import AvailableHLSVaults from 'components/hls/Farm/AvailableHLSVaults' import AvailableHLSVaults from 'components/hls/Farm/AvailableHLSVaults'
import HlsFarmIntro from 'components/hls/Farm/HLSFarmIntro' import HlsFarmIntro from 'components/hls/Farm/HLSFarmIntro'
import MigrationBanner from 'components/common/MigrationBanner'
import { HLS_TABS } from 'constants/pages' import { HLS_TABS } from 'constants/pages'
export default function HLSFarmPage() { export default function HLSFarmPage() {
return ( return (
<div className='flex flex-wrap w-full gap-6'> <div className='flex flex-wrap w-full gap-6'>
<MigrationBanner />
<Tab tabs={HLS_TABS} activeTabIdx={1} /> <Tab tabs={HLS_TABS} activeTabIdx={1} />
<HlsFarmIntro /> <HlsFarmIntro />
<AvailableHLSVaults /> <AvailableHLSVaults />

View File

@ -1,14 +1,12 @@
import Tab from 'components/earn/Tab' import Tab from 'components/common/Tab'
import ActiveStakingAccounts from 'components/hls/Staking/ActiveStakingAccounts' import ActiveStakingAccounts from 'components/hls/Staking/ActiveStakingAccounts'
import AvailableHlsStakingAssets from 'components/hls/Staking/AvailableHLSStakingAssets' import AvailableHlsStakingAssets from 'components/hls/Staking/AvailableHLSStakingAssets'
import HLSStakingIntro from 'components/hls/Staking/HLSStakingIntro' import HLSStakingIntro from 'components/hls/Staking/HLSStakingIntro'
import MigrationBanner from 'components/common/MigrationBanner'
import { HLS_TABS } from 'constants/pages' import { HLS_TABS } from 'constants/pages'
export default function HLSStakingPage() { export default function HLSStakingPage() {
return ( return (
<div className='flex flex-wrap w-full gap-6'> <div className='flex flex-wrap w-full gap-6'>
<MigrationBanner />
<Tab tabs={HLS_TABS} activeTabIdx={0} /> <Tab tabs={HLS_TABS} activeTabIdx={0} />
<HLSStakingIntro /> <HLSStakingIntro />
<AvailableHlsStakingAssets /> <AvailableHlsStakingAssets />

View File

@ -1,19 +1,19 @@
import MigrationBanner from 'components/common/MigrationBanner' import Tab from 'components/common/Tab'
import Tab from 'components/earn/Tab' import LendIntro from 'components/lend/LendIntro'
import LendIntro from 'components/earn/lend/LendIntro' import Lends from 'components/lend/Lends'
import Lends from 'components/earn/lend/Lends' import Deposits from 'components/v1/Deposits'
import { EARN_TABS } from 'constants/pages' import V1Intro from 'components/v1/V1Intro'
import useChainConfig from 'hooks/useChainConfig' import { LEND_AND_BORROW_TABS } from 'constants/pages'
import useStore from 'store'
export default function LendPage() { export default function LendPage() {
const chainConfig = useChainConfig() const isV1 = useStore((s) => s.isV1)
return ( return (
<div className='flex flex-wrap w-full gap-6'> <div className='flex flex-wrap w-full gap-6'>
<MigrationBanner /> <Tab tabs={LEND_AND_BORROW_TABS} activeTabIdx={0} showV1Toggle />
{chainConfig.farm && <Tab tabs={EARN_TABS} activeTabIdx={0} />} {isV1 ? <V1Intro /> : <LendIntro />}
<LendIntro /> {isV1 ? <Deposits /> : <Lends />}
<Lends />
</div> </div>
) )
} }

View File

@ -1,6 +1,5 @@
import { useNavigate, useParams, useSearchParams } from 'react-router-dom' import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
import MigrationBanner from 'components/common/MigrationBanner'
import ShareBar from 'components/common/ShareBar' import ShareBar from 'components/common/ShareBar'
import Balances from 'components/portfolio/Account/Balances' import Balances from 'components/portfolio/Account/Balances'
import BreadCrumbs from 'components/portfolio/Account/BreadCrumbs' import BreadCrumbs from 'components/portfolio/Account/BreadCrumbs'
@ -25,7 +24,6 @@ export default function PortfolioAccountPage() {
return ( return (
<div className='flex flex-wrap w-full gap-6'> <div className='flex flex-wrap w-full gap-6'>
<MigrationBanner />
<BreadCrumbs accountId={accountId} /> <BreadCrumbs accountId={accountId} />
<Summary accountId={accountId} /> <Summary accountId={accountId} />
<Balances accountId={accountId} /> <Balances accountId={accountId} />

View File

@ -1,15 +1,18 @@
import MigrationBanner from 'components/common/MigrationBanner' import { useParams } from 'react-router-dom'
import ShareBar from 'components/common/ShareBar'
import Summary from 'components/portfolio/Account/Summary'
import AccountOverview from 'components/portfolio/Overview' import AccountOverview from 'components/portfolio/Overview'
import PortfolioSummary from 'components/portfolio/Overview/Summary' import PortfolioSummary from 'components/portfolio/Overview/Summary'
import PortfolioIntro from 'components/portfolio/PortfolioIntro' import PortfolioIntro from 'components/portfolio/PortfolioIntro'
import ShareBar from 'components/common/ShareBar'
export default function PortfolioPage() { export default function PortfolioPage() {
const { address } = useParams()
return ( return (
<div className='flex flex-wrap w-full gap-6'> <div className='flex flex-wrap w-full gap-6'>
<MigrationBanner />
<PortfolioIntro /> <PortfolioIntro />
<PortfolioSummary /> <PortfolioSummary />
{address && <Summary accountId={address} v1 />}
<AccountOverview /> <AccountOverview />
<ShareBar text='Have a look at this @mars_protocol portfolio!' /> <ShareBar text='Have a look at this @mars_protocol portfolio!' />
</div> </div>

View File

@ -1,7 +1,6 @@
import { useMemo } from 'react' import { useMemo } from 'react'
import { useLocation } from 'react-router-dom' import { useLocation } from 'react-router-dom'
import MigrationBanner from 'components/common/MigrationBanner'
import AccountDetailsCard from 'components/trade/AccountDetailsCard' import AccountDetailsCard from 'components/trade/AccountDetailsCard'
import TradeChart from 'components/trade/TradeChart' import TradeChart from 'components/trade/TradeChart'
import TradeModule from 'components/trade/TradeModule' import TradeModule from 'components/trade/TradeModule'
@ -47,7 +46,6 @@ export default function TradePage() {
) )
return ( return (
<div className='flex flex-col w-full h-full gap-4'> <div className='flex flex-col w-full h-full gap-4'>
<MigrationBanner />
<div className='grid w-full grid-cols-[auto_346px] gap-4'> <div className='grid w-full grid-cols-[auto_346px] gap-4'>
<TradeChart buyAsset={buyAsset} sellAsset={sellAsset} /> <TradeChart buyAsset={buyAsset} sellAsset={sellAsset} />
<TradeModule buyAsset={buyAsset} sellAsset={sellAsset} isAdvanced={isAdvanced} /> <TradeModule buyAsset={buyAsset} sellAsset={sellAsset} isAdvanced={isAdvanced} />

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