Mp 2546 borrow asset modal (#255)

* Refactor Modal folder and setup basic addassetsmodal

* basic tables

* Update basic logic

* small fixes

* Update closing for modals

* fix slider update bug and set borrowing subtitle

* fix store

* add missing dependency

* fix tests for VaultBorrowings

* Add DisplayCurrency test for VaultBorrowings

* trigger updated

* update borrowModal import path

* update imports for modals

* updating paths again

* update structure of modals directory

* fix all file naming and relative imports

* fix icon spacing button and jest.mocked import

* fix icon classes for button

* change Map to array and add BNCoin

* add AssetImage

* update logic for selecting borrow denoms
This commit is contained in:
Bob van der Helm 2023-06-15 13:00:46 +02:00 committed by GitHub
parent 0f8e656651
commit 7b5d4c3255
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 845 additions and 199 deletions

View File

@ -1,25 +1,36 @@
import { render } from '@testing-library/react'
import VaultBorrowings from 'components/Modals/vault/VaultBorrowings'
import BigNumber from 'bignumber.js'
import { ASSETS } from 'constants/assets'
import { BN } from 'utils/helpers'
import useStore from 'store'
import DisplayCurrency from 'components/DisplayCurrency'
import VaultBorrowings, { VaultBorrowingsProps } from 'components/Modals/Vault/VaultBorrowings'
jest.mock('hooks/usePrices', () =>
jest.fn(() => ({
data: [],
})),
)
jest.mock('hooks/usePrice', () => jest.fn(() => '1'))
jest.mock('hooks/useMarketAssets', () =>
jest.fn(() => ({
data: [],
})),
)
jest.mock('components/DisplayCurrency')
const mockedDisplayCurrency = jest
.mocked(DisplayCurrency)
.mockImplementation(() => <div>Display currency</div>)
describe('<VaultBorrowings />', () => {
const defaultProps: {
account: Account
defaultBorrowDenom: string
onChangeBorrowings: (borrowings: Map<string, BigNumber>) => void
} = {
const defaultProps: VaultBorrowingsProps = {
primaryAsset: ASSETS[0],
secondaryAsset: ASSETS[1],
primaryAmount: BN(0),
secondaryAmount: BN(0),
account: {
id: 'test',
deposits: [],
@ -27,12 +38,32 @@ describe('<VaultBorrowings />', () => {
vaults: [],
lends: [],
},
defaultBorrowDenom: 'test-denom',
borrowings: [],
onChangeBorrowings: jest.fn(),
}
beforeAll(() => {
useStore.setState({
baseCurrency: ASSETS[0],
selectedBorrowDenoms: [ASSETS[1].denom],
})
})
afterAll(() => {
useStore.clearState()
mockedDisplayCurrency.mockClear()
})
it('should render', () => {
const { container } = render(<VaultBorrowings {...defaultProps} />)
expect(container).toBeInTheDocument()
})
it('should render DisplayCurrency correctly', () => {
expect(mockedDisplayCurrency).toHaveBeenCalledTimes(1)
expect(mockedDisplayCurrency).toHaveBeenCalledWith(
{ coin: { denom: 'uosmo', amount: '0' } },
expect.anything(),
)
})
})

View File

@ -2,17 +2,21 @@ import { BN } from 'utils/helpers'
import getPrices from 'api/prices/getPrices'
import getMarkets from 'api/markets/getMarkets'
import getMarketLiquidity from 'api/markets/getMarketLiquidity'
import { getEnabledMarketAssets } from 'utils/assets'
export default async function getMarketBorrowings(): Promise<BorrowAsset[]> {
const liquidity = await getMarketLiquidity()
const enabledAssets = getEnabledMarketAssets()
const borrowEnabledMarkets = (await getMarkets()).filter((market: Market) => market.borrowEnabled)
const prices = await getPrices()
const borrow: BorrowAsset[] = borrowEnabledMarkets.map((market) => {
const price = prices.find((coin) => coin.denom === market.denom)?.amount ?? '1'
const amount = liquidity.find((coin) => coin.denom === market.denom)?.amount ?? '0'
const asset = enabledAssets.find((asset) => asset.denom === market.denom)!
return {
denom: market.denom,
...asset,
borrowRate: market.borrowRate ?? 0,
liquidity: {
amount: amount,

View File

@ -1,10 +1,12 @@
import Card from 'components/Card'
import classNames from 'classnames'
import AccordionContent, { Item } from './AccordionContent'
import Card from 'components/Card'
import AccordionContent, { Item } from 'components/AccordionContent'
interface Props {
items: Item[]
allowMultipleOpen?: boolean
className?: string
}
export default function Accordion(props: Props) {
@ -19,7 +21,7 @@ export default function Accordion(props: Props) {
}
return (
<div className='w-full'>
<div className={classNames('w-full', props.className)}>
{props.items.map((item, index) => (
<Card key={item.title} className='mb-4'>
<AccordionContent item={item} index={index} />

View File

@ -25,8 +25,8 @@ export default function AccountSummary(props: Props) {
if (!props.account) return null
return (
<div className='flex min-w-[345px] basis-[345px] flex-wrap'>
<Card className='mb-4 min-w-fit bg-white/10' contentClassName='flex'>
<div className='h-[546px] min-w-[345px] basis-[345px] overflow-y-scroll scrollbar-hide'>
<Card className='mb-4 h-min min-w-fit bg-white/10' contentClassName='flex'>
<Item>
<DisplayCurrency
coin={{ amount: accountBalance.toString(), denom: baseCurrency.denom }}

View File

@ -0,0 +1,19 @@
import Image from 'next/image'
interface Props {
asset: Asset
size: number
className?: string
}
export default function AssetImage(props: Props) {
return (
<Image
src={props.asset.logo}
alt={`${props.asset.symbol} logo`}
width={props.size}
height={props.size}
className={props.className}
/>
)
}

View File

@ -19,6 +19,7 @@ import Text from 'components/Text'
import TitleAndSubCell from 'components/TitleAndSubCell'
import { getEnabledMarketAssets } from 'utils/assets'
import { formatPercent } from 'utils/formatters'
import AssetImage from 'components/AssetImage'
type Props = {
data: BorrowAsset[] | BorrowAssetActive[]
@ -40,7 +41,7 @@ export const BorrowTable = (props: Props) => {
return (
<div className='flex flex-1 items-center gap-3'>
<Image src={asset.logo} alt='token' width={32} height={32} />
<AssetImage asset={asset} size={32} />
<TitleAndSubCell
title={asset.symbol}
sub={asset.name}

View File

@ -93,10 +93,10 @@ const Button = React.forwardRef(function Button(
const [leftIconClassNames, rightIconClassNames] = useMemo(() => {
const hasContent = !!(text || children)
const iconClasses = ['flex items-center justify-center', iconClassName ?? 'h-4 w-4']
const leftIconClasses = [iconClasses, hasContent && 'mr-2']
const rightIconClasses = [iconClasses, hasContent && 'ml-2']
const leftIconClasses = [...iconClasses, hasContent && 'mr-2']
const rightIconClasses = [...iconClasses, hasContent && 'ml-2']
return [leftIconClasses, rightIconClasses].map(classNames)
return [leftIconClasses, rightIconClasses]
}, [children, iconClassName, text])
return (
@ -111,10 +111,10 @@ const Button = React.forwardRef(function Button(
<CircularProgress size={size === 'small' ? 10 : size === 'medium' ? 12 : 18} />
) : (
<>
{leftIcon && <span className={leftIconClassNames}>{leftIcon}</span>}
{leftIcon && <span className={classNames(leftIconClassNames)}>{leftIcon}</span>}
{shouldShowText && <span>{text}</span>}
{children && children}
{rightIcon && <span className={rightIconClassNames}>{rightIcon}</span>}
{rightIcon && <span className={classNames(rightIconClassNames)}>{rightIcon}</span>}
{hasSubmenu && (
<span data-testid='button-submenu-indicator' className='ml-2 inline-block w-2.5'>
<ChevronDown />

View File

@ -0,0 +1,27 @@
import classNames from 'classnames'
import { Check } from 'components/Icons'
interface Props {
checked: boolean
onChange: (checked: boolean) => void
}
export default function Checkbox(props: Props) {
return (
<button
onClick={() => props.onChange(props.checked)}
role='checkbox'
aria-checked={props.checked}
className={classNames(
'h-5 w-5 rounded-sm p-0.5',
props.checked && 'relative isolate overflow-hidden rounded-sm',
props.checked &&
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-sm before:p-[1px] before:border-glas',
props.checked ? 'bg-white/20' : 'border border-white/60',
)}
>
{props.checked && <Check />}
</button>
)
}

View File

@ -19,7 +19,12 @@ export default function VaultCard(props: Props) {
const currentAccount = useCurrentAccount()
function openVaultModal() {
useStore.setState({ vaultModal: { vault: props.vault } })
useStore.setState({
vaultModal: {
vault: props.vault,
selectedBorrowDenoms: [props.vault.denoms.secondary],
},
})
}
return (

View File

@ -11,7 +11,12 @@ interface Props {
export default function VaultExpanded(props: Props) {
function enterVaultHandler() {
useStore.setState({ vaultModal: { vault: props.row.original } })
useStore.setState({
vaultModal: {
vault: props.row.original,
selectedBorrowDenoms: [props.row.original.denoms.secondary],
},
})
}
return (

View File

@ -1,5 +1,6 @@
import Image from 'next/image'
import AssetImage from 'components/AssetImage'
import { getAssetByDenom } from 'utils/assets'
interface Props {
@ -15,10 +16,10 @@ export default function VaultLogo(props: Props) {
return (
<div className='relative grid w-12 place-items-center'>
<div className='absolute'>
<Image src={primaryAsset.logo} alt={`${primaryAsset.symbol} logo`} width={24} height={24} />
<AssetImage asset={primaryAsset} size={24} />
</div>
<div className='absolute'>
<Image className='ml-5 mt-5' src={secondaryAsset.logo} alt='token' width={16} height={16} />
<AssetImage asset={primaryAsset} size={16} className='ml-5 mt-5' />
</div>
</div>
)

View File

@ -0,0 +1,3 @@
<svg viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 13L10.1 10.1M11.6667 6.33333C11.6667 9.27885 9.27885 11.6667 6.33333 11.6667C3.38781 11.6667 1 9.27885 1 6.33333C1 3.38781 3.38781 1 6.33333 1C9.27885 1 11.6667 3.38781 11.6667 6.33333Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 351 B

View File

@ -26,6 +26,7 @@ export { default as OverlayMark } from 'components/Icons/OverlayMark.svg'
export { default as Plus } from 'components/Icons/Plus.svg'
export { default as PlusCircled } from 'components/Icons/PlusCircled.svg'
export { default as Questionmark } from 'components/Icons/Questionmark.svg'
export { default as Search } from 'components/Icons/Search.svg'
export { default as Shield } from 'components/Icons/Shield.svg'
export { default as SortAsc } from 'components/Icons/SortAsc.svg'
export { default as SortDesc } from 'components/Icons/SortDesc.svg'

View File

@ -4,6 +4,7 @@ import { ReactNode, useEffect, useRef } from 'react'
import Button from 'components/Button'
import Card from 'components/Card'
import { Cross } from 'components/Icons'
import Text from 'components/Text'
interface Props {
header: string | ReactNode
@ -12,6 +13,7 @@ interface Props {
content?: ReactNode | string
className?: string
contentClassName?: string
modalClassName?: string
open: boolean
onClose: () => void
}
@ -47,6 +49,7 @@ export default function Modal(props: Props) {
'w-[895px] border-none bg-transparent text-white',
'focus-visible:outline-none',
'backdrop:bg-black/50 backdrop:backdrop-blur-sm',
props.modalClassName,
)}
>
<Card
@ -57,13 +60,9 @@ export default function Modal(props: Props) {
>
<div className={classNames('flex justify-between', props.headerClassName)}>
{props.header}
<Button
onClick={onClose}
leftIcon={<Cross />}
className='h-8 w-8'
iconClassName='h-2 w-2'
color='tertiary'
/>
<Button onClick={onClose} leftIcon={<Cross />} iconClassName='h-3 w-3' color='tertiary'>
<Text size='sm'>ESC</Text>
</Button>
</div>
<div className={classNames(props.contentClassName, 'flex-grow')}>
{props.children ? props.children : props.content}

View File

@ -0,0 +1,129 @@
import {
flexRender,
getCoreRowModel,
getSortedRowModel,
RowSelectionState,
SortingState,
useReactTable,
} from '@tanstack/react-table'
import { useEffect, useState } from 'react'
import classNames from 'classnames'
import { SortAsc, SortDesc, SortNone } from 'components/Icons'
import Text from 'components/Text'
import useStore from 'store'
import useAddVaultAssetTableColumns from 'components/Modals/AddVaultAssets/useAddVaultAssetTableColumns'
interface Props {
assets: BorrowAsset[]
onChangeSelected: (denoms: string[]) => void
}
export default function AddVaultAssetTable(props: Props) {
const selectedDenoms = useStore((s) => s.addVaultBorrowingsModal?.selectedDenoms) || []
const defaultSelected = props.assets.reduce((acc, asset, index) => {
if (selectedDenoms.includes(asset.denom)) {
acc[index] = true
}
return acc
}, {} as { [key: number]: boolean })
const [sorting, setSorting] = useState<SortingState>([{ id: 'symbol', desc: false }])
const [selected, setSelected] = useState<RowSelectionState>(defaultSelected)
const columns = useAddVaultAssetTableColumns()
const table = useReactTable({
data: props.assets,
columns,
state: {
sorting,
rowSelection: selected,
},
onRowSelectionChange: setSelected,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
})
useEffect(() => {
const selectedDenoms = props.assets
.filter((_, index) => selected[index])
.map((asset) => asset.denom)
props.onChangeSelected(selectedDenoms)
}, [selected, props])
return (
<table className='w-full'>
<thead className='border-b border-b-white/5'>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header, index) => {
return (
<th
key={header.id}
onClick={header.column.getToggleSortingHandler()}
className={classNames(
'p-2',
header.column.getCanSort() && 'cursor-pointer',
header.id === 'symbol' ? 'text-left' : 'text-right',
)}
>
<div
className={classNames(
'flex',
header.id === 'symbol' ? 'justify-start' : 'justify-end',
'align-center',
)}
>
<span className='h-6 w-6 text-white'>
{header.column.getCanSort()
? {
asc: <SortAsc />,
desc: <SortDesc />,
false: <SortNone />,
}[header.column.getIsSorted() as string] ?? null
: null}
</span>
<Text
tag='span'
size='sm'
className='flex items-center font-normal text-white/40'
>
{flexRender(header.column.columnDef.header, header.getContext())}
</Text>
</div>
</th>
)
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => {
return (
<tr
key={row.id}
className='cursor-pointer text-white/60'
onClick={() => row.toggleSelected()}
>
{row.getVisibleCells().map((cell) => {
return (
<td
key={cell.id}
className={classNames(
cell.column.id === 'select' ? `` : 'pl-4 text-right',
'px-4 py-3',
)}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
)
}

View File

@ -0,0 +1,46 @@
import { useCallback, useState } from 'react'
import Modal from 'components/Modal'
import useStore from 'store'
import Text from 'components/Text'
import { CircularProgress } from 'components/CircularProgress'
import AddVaultAssetsModalContent from 'components/Modals/AddVaultAssets/AddVaultBorrowAssetsModalContent'
export default function AddVaultBorrowAssetsModal() {
const modal = useStore((s) => s.addVaultBorrowingsModal)
const vaultModal = useStore((s) => s.vaultModal)
const [selectedDenoms, setSelectedDenoms] = useState<string[]>([])
function onClose() {
if (!vaultModal) return
useStore.setState({
addVaultBorrowingsModal: null,
vaultModal: { ...vaultModal, selectedBorrowDenoms: selectedDenoms },
})
}
const updateSelectedDenoms = useCallback((denoms: string[]) => setSelectedDenoms(denoms), [])
const showContent = modal && vaultModal?.vault
return (
<Modal
open={!!(modal && showContent)}
header={<Text>Add Assets</Text>}
onClose={onClose}
modalClassName='max-w-[478px]'
headerClassName='bg-white/10 border-b-white/5 border-b items-center p-4'
>
{showContent ? (
<AddVaultAssetsModalContent
vault={vaultModal?.vault}
defaultSelectedDenoms={modal.selectedDenoms}
onChangeBorrowDenoms={updateSelectedDenoms}
/>
) : (
<CircularProgress />
)}
</Modal>
)
}

View File

@ -0,0 +1,96 @@
import { useCallback, useMemo, useState } from 'react'
import SearchBar from 'components/SearchBar'
import Text from 'components/Text'
import useMarketBorrowings from 'hooks/useMarketBorrowings'
import AddVaultAssetTable from 'components/Modals/AddVaultAssets/AddVaultAssetTable'
interface Props {
vault: Vault
defaultSelectedDenoms: string[]
onChangeBorrowDenoms: (denoms: string[]) => void
}
export default function AddVaultAssetsModalContent(props: Props) {
const [searchString, setSearchString] = useState<string>('')
const { data: borrowAssets } = useMarketBorrowings()
const [selectedPoolDenoms, setSelectedPoolDenoms] = useState<string[]>([])
const [selectedOtherDenoms, setSelectedOtherDenoms] = useState<string[]>([])
const filteredBorrowAssets: BorrowAsset[] = useMemo(() => {
return borrowAssets.filter(
(asset) =>
asset.name.toLowerCase().includes(searchString.toLowerCase()) ||
asset.denom.toLowerCase().includes(searchString.toLowerCase()) ||
asset.symbol.toLowerCase().includes(searchString.toLowerCase()),
)
}, [borrowAssets, searchString])
function onChangeSearchString(value: string) {
setSearchString(value)
}
const [poolAssets, stableAssets] = useMemo(
() =>
filteredBorrowAssets.reduce(
(acc, asset) => {
if (
asset.denom === props.vault.denoms.primary ||
asset.denom === props.vault.denoms.secondary
) {
acc[0].push(asset)
} else if (asset.isStable) {
acc[1].push(asset)
}
return acc
},
[[], []] as [BorrowAsset[], BorrowAsset[]],
),
[filteredBorrowAssets, props.vault.denoms.primary, props.vault.denoms.secondary],
)
const onChangePoolDenoms = useCallback(
(denoms: string[]) => {
setSelectedPoolDenoms(denoms)
props.onChangeBorrowDenoms([...denoms, ...selectedOtherDenoms])
},
[props, selectedOtherDenoms],
)
const onChangeOtherDenoms = useCallback(
(denoms: string[]) => {
setSelectedOtherDenoms(denoms)
props.onChangeBorrowDenoms([...selectedPoolDenoms, ...denoms])
},
[props, selectedPoolDenoms],
)
return (
<>
<div className='border-b border-b-white/5 bg-white/10 px-4 py-3'>
<SearchBar
value={searchString}
placeholder={`Search for e.g. "ETH" or "Ethereum"`}
onChange={onChangeSearchString}
/>
</div>
<div className='h-[446px] overflow-y-scroll scrollbar-hide'>
<div className='p-4'>
<Text>Available Assets</Text>
<Text size='xs' className='mt-1 text-white/60'>
Leverage will be set at 50% for both assets by default
</Text>
</div>
<AddVaultAssetTable assets={poolAssets} onChangeSelected={onChangePoolDenoms} />
<div className='p-4'>
<Text>Assets not in the liquidity pool</Text>
<Text size='xs' className='mt-1 text-white/60'>
These are swapped for an asset within the pool. Toggle Custom Ratio in order to select
these assets below.
</Text>
</div>
<AddVaultAssetTable assets={stableAssets} onChangeSelected={onChangeOtherDenoms} />
</div>
</>
)
}

View File

@ -0,0 +1,54 @@
import { ColumnDef } from '@tanstack/react-table'
import React from 'react'
import Image from 'next/image'
import Checkbox from 'components/Checkbox'
import Text from 'components/Text'
import { formatPercent } from 'utils/formatters'
import { getAssetByDenom } from 'utils/assets'
import AssetImage from 'components/AssetImage'
export default function useAddVaultAssetTableColumns() {
const columns = React.useMemo<ColumnDef<BorrowAsset>[]>(
() => [
{
header: 'Asset',
accessorKey: 'symbol',
id: 'symbol',
cell: ({ row }) => {
const asset = getAssetByDenom(row.original.denom)
if (!asset) return null
return (
<div className='flex items-center'>
<Checkbox checked={row.getIsSelected()} onChange={row.getToggleSelectedHandler()} />
<AssetImage asset={asset} size={24} className='ml-4' />
<div className='ml-2 text-left'>
<Text size='sm' className='mb-0.5 text-white'>
{asset.symbol}
</Text>
<Text size='xs'>{asset.name}</Text>
</div>
</div>
)
},
},
{
id: 'borrowRate',
accessorKey: 'borrowRate',
header: 'Borrow Rate',
cell: ({ row }) => (
<>
<Text size='sm' className='mb-0.5 text-white'>
{formatPercent(row.original.borrowRate ?? 0)}
</Text>
<Text size='xs'>APY</Text>
</>
),
},
],
[],
)
return columns
}

View File

@ -1,4 +1,3 @@
import Image from 'next/image'
import { useEffect, useState } from 'react'
import AccountSummary from 'components/Account/AccountSummary'
@ -18,6 +17,7 @@ import useStore from 'store'
import { hardcodedFee } from 'utils/contants'
import { formatPercent, formatValue } from 'utils/formatters'
import { BN } from 'utils/helpers'
import AssetImage from 'components/AssetImage'
function getDebtAmount(modal: BorrowModal | null) {
if (!(modal?.marketData as BorrowAssetActive)?.debt) return '0'
@ -26,7 +26,7 @@ function getDebtAmount(modal: BorrowModal | null) {
function getAssetLogo(modal: BorrowModal | null) {
if (!modal?.asset) return null
return <Image src={modal.asset.logo} alt={modal.asset.symbol} width={24} height={24} />
return <AssetImage asset={modal.asset} size={24} />
}
export default function BorrowModal() {

View File

@ -3,8 +3,7 @@ import Text from 'components/Text'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useStore from 'store'
import { CircularProgress } from 'components/CircularProgress'
import FundWithdrawModalContent from './FundWithdrawModalContent'
import FundWithdrawModalContent from 'components/Modals/FundWithdraw/FundAndWithdrawModalContent'
export default function FundAndWithdrawModal() {
const currentAccount = useCurrentAccount()

View File

@ -1,6 +1,7 @@
import BorrowModal from 'components/Modals/BorrowModal'
import FundAndWithdrawModal from 'components/Modals/fundwithdraw/FundAndWithdrawModal'
import VaultModal from 'components/Modals/vault/VaultModal'
import VaultModal from 'components/Modals/Vault/VaultModal'
import BorrowModal from 'components/Modals/Borrow/BorrowModal'
import FundAndWithdrawModal from 'components/Modals/FundWithdraw/FundAndWithdrawModal'
import AddVaultBorrowAssetsModal from 'components/Modals/AddVaultAssets/AddVaultBorrowAssetsModal'
export default function ModalsContainer() {
return (
@ -8,6 +9,7 @@ export default function ModalsContainer() {
<BorrowModal />
<FundAndWithdrawModal />
<VaultModal />
<AddVaultBorrowAssetsModal />
</>
)
}

View File

@ -0,0 +1,197 @@
import { useEffect, useMemo, useState } from 'react'
import BigNumber from 'bignumber.js'
import React from 'react'
import { BN } from 'utils/helpers'
import { findCoinByDenom, getAssetByDenom } from 'utils/assets'
import Button from 'components/Button'
import TokenInput from 'components/TokenInput'
import Divider from 'components/Divider'
import Text from 'components/Text'
import { ArrowRight, ExclamationMarkCircled } from 'components/Icons'
import { formatPercent } from 'utils/formatters'
import Slider from 'components/Slider'
import usePrices from 'hooks/usePrices'
import useMarketAssets from 'hooks/useMarketAssets'
import { calculateMaxBorrowAmounts } from 'utils/vaults'
import useStore from 'store'
import DisplayCurrency from 'components/DisplayCurrency'
import usePrice from 'hooks/usePrice'
import { BNCoin } from 'types/classes/BNCoin'
export interface VaultBorrowingsProps {
account: Account
borrowings: BNCoin[]
primaryAmount: BigNumber
secondaryAmount: BigNumber
primaryAsset: Asset
secondaryAsset: Asset
onChangeBorrowings: (borrowings: BNCoin[]) => void
}
export default function VaultBorrowings(props: VaultBorrowingsProps) {
const { data: marketAssets } = useMarketAssets()
const { data: prices } = usePrices()
const primaryPrice = usePrice(props.primaryAsset.denom)
const secondaryPrice = usePrice(props.secondaryAsset.denom)
const baseCurrency = useStore((s) => s.baseCurrency)
const vaultModal = useStore((s) => s.vaultModal)
const primaryValue = useMemo(
() => props.primaryAmount.times(primaryPrice),
[props.primaryAmount, primaryPrice],
)
const secondaryValue = useMemo(
() => props.secondaryAmount.times(secondaryPrice),
[props.secondaryAmount, secondaryPrice],
)
const borrowingValue = useMemo(() => {
return props.borrowings.reduce((prev, curr) => {
const price = prices.find((price) => price.denom === curr.denom)?.amount
if (!price) return prev
return prev.plus(curr.amount.times(price))
}, BN(0) as BigNumber)
}, [props.borrowings, prices])
const totalValue = useMemo(
() => primaryValue.plus(secondaryValue).plus(borrowingValue),
[primaryValue, secondaryValue, borrowingValue],
)
useEffect(() => {
const selectedBorrowDenoms = vaultModal?.selectedBorrowDenoms || []
if (
props.borrowings.length === selectedBorrowDenoms.length &&
props.borrowings.every((coin) => selectedBorrowDenoms.includes(coin.denom))
) {
return
}
const updatedBorrowings = selectedBorrowDenoms.map((denom) => {
const amount = findCoinByDenom(denom, props.borrowings)?.amount || BN(0)
return new BNCoin({
denom,
amount: amount.toString(),
})
})
props.onChangeBorrowings(updatedBorrowings)
}, [vaultModal, props])
const maxAmounts: BNCoin[] = useMemo(
() =>
calculateMaxBorrowAmounts(
props.account,
marketAssets,
prices,
props.borrowings.map((coin) => coin.denom),
),
[props.borrowings, marketAssets, prices, props.account],
)
const [percentage, setPercentage] = useState<number>(0)
function onChangeSlider(value: number) {
if (props.borrowings.length !== 1) return
const denom = props.borrowings[0].denom
const currentAmount = props.borrowings[0].amount
const maxAmount = maxAmounts.find((coin) => coin.denom === denom)?.amount ?? BN(0)
const newBorrowings: BNCoin[] = [
new BNCoin({
denom,
amount: (
maxAmount.plus(currentAmount).times(value).div(100).decimalPlaces(0) || BN(0)
).toString(),
}),
]
props.onChangeBorrowings(newBorrowings)
setPercentage(value)
}
function updateAssets(denom: string, amount: BigNumber) {
const index = props.borrowings.findIndex((coin) => coin.denom === denom)
props.borrowings[index].amount = amount
props.onChangeBorrowings([...props.borrowings])
}
function onDelete(denom: string) {
const index = props.borrowings.findIndex((coin) => coin.denom === denom)
props.borrowings.splice(index, 1)
props.onChangeBorrowings([...props.borrowings])
if (!vaultModal) return
useStore.setState({
vaultModal: {
...vaultModal,
selectedBorrowDenoms: props.borrowings.map((coin) => coin.denom),
},
})
}
function addAsset() {
useStore.setState({
addVaultBorrowingsModal: {
selectedDenoms: props.borrowings.map((coin) => coin.denom),
},
})
}
return (
<div className='flex flex-grow flex-col gap-4 p-4'>
{props.borrowings.map((coin) => {
const asset = getAssetByDenom(coin.denom)
const maxAmount = maxAmounts.find((maxAmount) => maxAmount.denom === coin.denom)?.amount
if (!asset || !maxAmount)
return <React.Fragment key={`input-${coin.denom}`}></React.Fragment>
return (
<TokenInput
key={`input-${coin.denom}`}
amount={coin.amount}
asset={asset}
max={maxAmount.plus(coin.amount)}
maxText='Max Borrow'
onChange={(amount) => updateAssets(coin.denom, amount)}
onDelete={() => onDelete(coin.denom)}
/>
)
})}
{props.borrowings.length === 1 && <Slider onChange={onChangeSlider} value={percentage} />}
{props.borrowings.length === 0 && (
<div className='flex items-center gap-4 py-2'>
<div className='w-4'>
<ExclamationMarkCircled width={20} height={20} />
</div>
<Text size='xs'>
You have no borrowing assets selected. Click on select borrow assets if you would like
to add assets to borrow.
</Text>
</div>
)}
<Button text='Select borrow assets +' color='tertiary' onClick={addAsset} />
<Divider />
<div className='flex flex-col gap-2'>
<div className='flex justify-between'>
<Text className='text-white/50'>{`${props.primaryAsset.symbol}-${props.secondaryAsset.symbol} Position Value`}</Text>
<DisplayCurrency coin={{ denom: baseCurrency.denom, amount: totalValue.toString() }} />
</div>
{props.borrowings.map((coin) => {
const asset = getAssetByDenom(coin.denom)
const borrowRate = marketAssets?.find((market) => market.denom === coin.denom)?.borrowRate
if (!asset || !borrowRate)
return <React.Fragment key={`borrow-rate-${coin.denom}`}></React.Fragment>
return (
<div key={`borrow-rate-${coin.denom}`} className='flex justify-between'>
<Text className='text-white/50'>Borrow APR {asset.symbol}</Text>
<Text>{formatPercent(borrowRate)}</Text>
</div>
)
})}
</div>
<Button color='primary' text='Deposit' rightIcon={<ArrowRight />} />
</div>
)
}

View File

@ -0,0 +1,49 @@
import BigNumber from 'bignumber.js'
import { useMemo } from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
import { formatAmountWithSymbol } from 'utils/formatters'
import { BN } from 'utils/helpers'
import { BNCoin } from 'types/classes/BNCoin'
interface Props {
borrowings: BNCoin[]
}
export default function VaultDepositSubTitle(props: Props) {
const baseCurrency = useStore((s) => s.baseCurrency)
const { data: prices } = usePrices()
const [borrowingTexts, borrowingValue] = useMemo(() => {
const texts: string[] = []
let borrowingValue = BN(0)
props.borrowings.map((coin) => {
const price = prices.find((p) => p.denom === coin.denom)?.amount
if (!price || coin.amount.isZero()) return
borrowingValue = borrowingValue.plus(coin.amount.times(price))
texts.push(
formatAmountWithSymbol({
denom: coin.denom,
amount: coin.amount.toString(),
}),
)
})
return [texts, borrowingValue]
}, [props.borrowings, prices])
return (
<>
{borrowingTexts.join(' + ')}
{borrowingTexts.length > 0 && (
<>
{` = `}
<DisplayCurrency
coin={{ denom: baseCurrency.denom, amount: borrowingValue.toString() }}
/>
</>
)}
</>
)
}

View File

@ -5,8 +5,7 @@ import { ASSETS } from 'constants/assets'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useStore from 'store'
import { CircularProgress } from 'components/CircularProgress'
import VaultModalContent from './VaultModalContent'
import VaultModalContent from 'components/Modals/Vault/VaultModalContent'
export default function VaultModal() {
const currentAccount = useCurrentAccount()

View File

@ -3,12 +3,13 @@ import { useCallback, useState } from 'react'
import Accordion from 'components/Accordion'
import AccountSummary from 'components/Account/AccountSummary'
import VaultBorrowings from 'components/Modals/vault/VaultBorrowings'
import VaultDeposit from 'components/Modals/vault/VaultDeposit'
import VaultDepositSubTitle from 'components/Modals/vault/VaultDepositSubTitle'
import useIsOpenArray from 'hooks/useIsOpenArray'
import { BN } from 'utils/helpers'
import useUpdateAccount from 'hooks/useUpdateAccount'
import VaultBorrowingsSubTitle from 'components/Modals/Vault/VaultBorrowingsSubTitle'
import VaultDeposit from 'components/Modals/Vault/VaultDeposits'
import VaultBorrowings from 'components/Modals/Vault/VaultBorrowings'
import VaultDepositSubTitle from 'components/Modals/Vault/VaultDepositsSubTitle'
interface Props {
vault: Vault
@ -18,7 +19,10 @@ interface Props {
}
export default function VaultModalContent(props: Props) {
const { updatedAccount, onChangeBorrowings } = useUpdateAccount(props.account)
const { updatedAccount, onChangeBorrowings, borrowings } = useUpdateAccount(
props.account,
props.vault,
)
const [isOpen, toggleOpen] = useIsOpenArray(2, false)
const [primaryAmount, setPrimaryAmount] = useState<BigNumber>(BN(0))
const [secondaryAmount, setSecondaryAmount] = useState<BigNumber>(BN(0))
@ -41,6 +45,7 @@ export default function VaultModalContent(props: Props) {
return (
<div className='flex flex-grow items-start gap-6 p-6'>
<Accordion
className='h-[546px] overflow-y-scroll scrollbar-hide'
items={[
{
renderContent: () => (
@ -73,11 +78,16 @@ export default function VaultModalContent(props: Props) {
renderContent: () => (
<VaultBorrowings
account={updatedAccount}
defaultBorrowDenom={props.secondaryAsset.denom}
borrowings={borrowings}
primaryAmount={primaryAmount}
secondaryAmount={secondaryAmount}
primaryAsset={props.primaryAsset}
secondaryAsset={props.secondaryAsset}
onChangeBorrowings={onChangeBorrowings}
/>
),
title: 'Borrow',
subTitle: <VaultBorrowingsSubTitle borrowings={borrowings} />,
isOpen: isOpen[1],
toggleOpen: (index: number) => toggleOpen(index),
},

View File

@ -1,112 +0,0 @@
import { useMemo, useState } from 'react'
import BigNumber from 'bignumber.js'
import { BN } from 'utils/helpers'
import { getAssetByDenom } from 'utils/assets'
import Button from 'components/Button'
import TokenInput from 'components/TokenInput'
import Divider from 'components/Divider'
import Text from 'components/Text'
import { ArrowRight } from 'components/Icons'
import { formatPercent } from 'utils/formatters'
import Slider from 'components/Slider'
import usePrices from 'hooks/usePrices'
import useMarketAssets from 'hooks/useMarketAssets'
import { calculateMaxBorrowAmounts } from 'utils/vaults'
import React from 'react'
interface Props {
account: Account
defaultBorrowDenom: string
onChangeBorrowings: (borrowings: Map<string, BigNumber>) => void
}
export default function VaultBorrowings(props: Props) {
const { data: prices } = usePrices()
const { data: marketAssets } = useMarketAssets()
const [borrowings, setBorrowings] = useState<Map<string, BigNumber>>(
new Map().set(props.defaultBorrowDenom, BN(0)),
)
const maxAmounts: Map<string, BigNumber> = useMemo(
() =>
calculateMaxBorrowAmounts(props.account, marketAssets, prices, Array.from(borrowings.keys())),
[borrowings, marketAssets, prices, props.account],
)
const [percentage, setPercentage] = useState<number>(0)
function onChangeSlider(value: number) {
if (borrowings.size !== 1) return
const denom = Array.from(borrowings.keys())[0]
const newBorrowings = new Map().set(
denom,
maxAmounts.get(denom)?.times(value).div(100).toPrecision(0) || BN(0),
)
setBorrowings(newBorrowings)
props.onChangeBorrowings(newBorrowings)
setPercentage(value)
}
function updateAssets(denom: string, amount: BigNumber) {
const newborrowings = new Map(borrowings)
newborrowings.set(denom, amount)
setBorrowings(newborrowings)
props.onChangeBorrowings(newborrowings)
}
function onDelete(denom: string) {
const newborrowings = new Map(borrowings)
newborrowings.delete(denom)
setBorrowings(newborrowings)
props.onChangeBorrowings(newborrowings)
}
function addAsset() {
const newborrowings = new Map(borrowings)
// Replace with denom parameter from the modal (MP-2546)
newborrowings.set('', BN(0))
setBorrowings(newborrowings)
props.onChangeBorrowings(newborrowings)
}
return (
<div className='flex flex-grow flex-col gap-4 p-4'>
{Array.from(borrowings.entries()).map(([denom, amount]) => {
const asset = getAssetByDenom(denom)
if (!asset) return <React.Fragment key={`input-${denom}`}></React.Fragment>
return (
<TokenInput
key={`input-${denom}`}
amount={amount}
asset={asset}
max={maxAmounts.get(denom)?.plus(amount) || BN(0)}
maxText='Max Borrow'
onChange={(amount) => updateAssets(denom, amount)}
onDelete={() => onDelete(denom)}
/>
)
})}
{borrowings.size === 1 && <Slider onChange={onChangeSlider} value={percentage} />}
<Button text='Select borrow assets +' color='tertiary' onClick={addAsset} />
<Divider />
{Array.from(borrowings.entries()).map(([denom, amount]) => {
const asset = getAssetByDenom(denom)
const borrowRate = marketAssets?.find((market) => market.denom === denom)?.borrowRate
if (!asset || !borrowRate)
return <React.Fragment key={`borrow-rate-${denom}`}></React.Fragment>
return (
<div key={`borrow-rate-${denom}`} className='flex justify-between'>
<Text className='text-white/50'>Borrow APR {asset.symbol}</Text>
<Text>{formatPercent(borrowRate)}</Text>
</div>
)
})}
<Button color='primary' text='Deposit' rightIcon={<ArrowRight />} />
</div>
)
}

View File

@ -0,0 +1,34 @@
import classNames from 'classnames'
import { ChangeEvent } from 'react'
import { Search } from 'components/Icons'
interface Props {
value: string
placeholder: string
onChange: (value: string) => void
}
export default function SearchBar(props: Props) {
function onChange(event: ChangeEvent<HTMLInputElement>) {
props.onChange(event.target.value)
}
return (
<div
className={classNames(
'flex w-full items-center justify-between rounded-sm bg-white/10 p-2.5',
'relative isolate max-w-full overflow-hidden rounded-base',
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-base before:p-[1px] before:border-glas',
)}
>
<Search width={14} height={14} className='mr-2.5 text-white' />
<input
value={props.value}
className='h-full w-full bg-transparent text-xs placeholder-white/30 outline-none'
placeholder={props.placeholder}
onChange={(event) => onChange(event)}
/>
</div>
)
}

View File

@ -6,6 +6,7 @@ import { ChevronDown } from 'components/Icons'
import Text from 'components/Text'
import { ASSETS } from 'constants/assets'
import { formatValue } from 'utils/formatters'
import AssetImage from 'components/AssetImage'
interface Props extends Option {
isSelected?: boolean
@ -18,11 +19,7 @@ export default function Option(props: Props) {
const isCoin = !!props.denom
if (isCoin) {
const currentAsset = ASSETS.find((asset) => asset.denom === props.denom)
const symbol = currentAsset?.symbol ?? ASSETS[0].symbol
const logo = currentAsset?.logo ?? ASSETS[0].logo
const denom = currentAsset?.denom ?? ASSETS[0].denom
const decimals = currentAsset?.decimals ?? ASSETS[0].decimals
const asset = ASSETS.find((asset) => asset.denom === props.denom) || ASSETS[0]
const balance = props.amount ?? '0'
if (props.isDisplay) {
@ -30,8 +27,8 @@ export default function Option(props: Props) {
<div
className={classNames('flex items-center gap-2 bg-white/10 p-3', 'hover:cursor-pointer')}
>
<Image src={logo} alt={`${symbol} token logo`} width={20} height={20} />
<span>{symbol}</span>
<AssetImage asset={asset} size={20} />
<span>{asset.symbol}</span>
<span
className={classNames(
'inline-block w-2.5 transition-transform',
@ -53,20 +50,25 @@ export default function Option(props: Props) {
'hover:cursor-pointer hover:bg-white/20',
!props.isSelected ? 'bg-white/10' : 'pointer-events-none',
)}
onClick={() => props?.onClick && props.onClick(denom)}
onClick={() => props?.onClick && props.onClick(asset.denom)}
>
<div className='row-span-2 flex h-full items-center justify-center'>
<Image src={logo} alt={`${symbol} token logo`} width={32} height={32} />
<AssetImage asset={asset} size={32} />
</div>
<Text className='col-span-2 pb-1'>{symbol}</Text>
<Text className='col-span-2 pb-1'>{asset.symbol}</Text>
<Text size='sm' className='col-span-2 pb-1 text-right font-bold'>
{formatValue(balance, { decimals, maxDecimals: 4, minDecimals: 0, rounded: true })}
{formatValue(balance, {
decimals: asset.decimals,
maxDecimals: 4,
minDecimals: 0,
rounded: true,
})}
</Text>
<Text size='sm' className='col-span-2 text-white/50'>
{formatValue(5, { maxDecimals: 2, minDecimals: 0, prefix: 'APY ', suffix: '%' })}
</Text>
<Text size='sm' className='col-span-2 text-right text-white/50'>
<DisplayCurrency coin={{ denom, amount: balance }} />
<DisplayCurrency coin={{ denom: asset.denom, amount: balance }} />
</Text>
</div>
)

View File

@ -13,6 +13,7 @@ import { FormattedNumber } from 'components/FormattedNumber'
import Button from 'components/Button'
import { ExclamationMarkTriangle, TrashBin } from 'components/Icons'
import { Tooltip } from 'components/Tooltip'
import AssetImage from 'components/AssetImage'
interface Props {
amount: BigNumber
@ -67,7 +68,7 @@ export default function TokenInput(props: Props) {
/>
) : (
<div className='flex min-w-fit items-center gap-2 border-r border-white/20 bg-white/5 p-3'>
<Image src={props.asset.logo} alt='token' width={20} height={20} />
<AssetImage asset={props.asset} size={20} />
<Text>{props.asset.symbol}</Text>
</div>
)}

View File

@ -4,8 +4,7 @@ import { ReactNode } from 'react'
import { Questionmark } from 'components/Icons'
import useStore from 'store'
import TooltipContent from './TooltipContent'
import TooltipContent from 'components/Tooltip/TooltipContent'
interface Props {
content: ReactNode | string

View File

@ -97,6 +97,7 @@ export const ASSETS: Asset[] = [
isEnabled: true,
isMarket: true,
isDisplayCurrency: true,
isStable: true,
},
{
symbol: 'USDC.n',
@ -112,5 +113,6 @@ export const ASSETS: Asset[] = [
isEnabled: IS_TESTNET,
isMarket: IS_TESTNET,
isDisplayCurrency: IS_TESTNET,
isStable: true,
},
]

View File

@ -4,6 +4,7 @@ import getMarketBorrowings from 'api/markets/getMarketBorrowings'
export default function useMarketBorrowings() {
return useSWR(`marketBorrowings`, getMarketBorrowings, {
suspense: true,
fallbackData: [],
suspense: false,
})
}

View File

@ -1,4 +1,4 @@
import usePrices from './usePrices'
import usePrices from 'hooks/usePrices'
export default function usePrice(denom: string) {
const { data: prices } = usePrices()

View File

@ -1,10 +1,12 @@
import BigNumber from 'bignumber.js'
import { useCallback, useState } from 'react'
import { BNCoin } from 'types/classes/BNCoin'
import { BN } from 'utils/helpers'
export default function useUpdateAccount(account: Account) {
export default function useUpdateAccount(account: Account, vault: Vault) {
const [updatedAccount, setUpdatedAccount] = useState<Account>(account)
const [borrowings, setBorrowings] = useState<BNCoin[]>([])
function getCoin(denom: string, amount: BigNumber): Coin {
return {
@ -14,32 +16,33 @@ export default function useUpdateAccount(account: Account) {
}
const onChangeBorrowings = useCallback(
(borrowings: Map<string, BigNumber>) => {
(borrowings: BNCoin[]) => {
const debts: Coin[] = [...account.debts]
const deposits: Coin[] = [...account.deposits]
const currentDebtDenoms = debts.map((debt) => debt.denom)
const currentDepositDenoms = deposits.map((deposit) => deposit.denom)
borrowings.forEach((amount, denom) => {
if (amount.isZero()) return
borrowings.map((coin) => {
if (coin.amount.isZero()) return
if (currentDebtDenoms.includes(denom)) {
const index = currentDebtDenoms.indexOf(denom)
const newAmount = BN(debts[index].amount).plus(amount)
debts[index] = getCoin(denom, newAmount)
if (currentDebtDenoms.includes(coin.denom)) {
const index = currentDebtDenoms.indexOf(coin.denom)
const newAmount = BN(debts[index].amount).plus(coin.amount)
debts[index] = getCoin(coin.denom, newAmount)
} else {
debts.push(getCoin(denom, amount))
debts.push(coin.toCoin())
}
if (currentDepositDenoms.includes(denom)) {
const index = currentDepositDenoms.indexOf(denom)
const newAmount = BN(deposits[index].amount).plus(amount)
deposits[index] = getCoin(denom, newAmount)
if (currentDepositDenoms.includes(coin.denom)) {
const index = currentDepositDenoms.indexOf(coin.denom)
const newAmount = BN(deposits[index].amount).plus(coin.amount)
deposits[index] = getCoin(coin.denom, newAmount)
} else {
deposits.push(getCoin(denom, amount))
deposits.push(coin.toCoin())
}
})
setBorrowings(borrowings)
setUpdatedAccount({
...account,
debts,
@ -49,5 +52,5 @@ export default function useUpdateAccount(account: Account) {
[account],
)
return { updatedAccount, onChangeBorrowings }
return { borrowings, updatedAccount, onChangeBorrowings }
}

View File

@ -2,6 +2,7 @@ import { GetState, SetState } from 'zustand'
export default function createModalSlice(set: SetState<ModalSlice>, get: GetState<ModalSlice>) {
return {
addVaultBorrowingsModal: null,
borrowModal: null,
createAccountModal: false,
deleteAccountModal: false,

View File

@ -0,0 +1,18 @@
import { BN } from 'utils/helpers'
export class BNCoin {
public denom: string
public amount: BigNumber
constructor(coin: Coin) {
this.denom = coin.denom
this.amount = BN(coin.amount)
}
toCoin(): Coin {
return {
denom: this.denom,
amount: this.amount.toString(),
}
}
}

View File

@ -13,14 +13,14 @@ interface Asset {
isEnabled: boolean
isMarket: boolean
isDisplayCurrency?: boolean
isStable?: boolean
}
interface OtherAsset extends Omit<Asset, 'symbol'> {
symbol: 'MARS'
}
interface BorrowAsset {
denom: string
interface BorrowAsset extends Asset {
borrowRate: number | null
liquidity: {
amount: string
@ -31,3 +31,8 @@ interface BorrowAsset {
interface BorrowAssetActive extends BorrowAsset {
debt: string
}
interface BigNumberCoin {
denom: string
amount: BigNumber
}

View File

@ -1,10 +1,10 @@
interface CommonSlice {
accounts: Account[] | null
address?: string
balances: Coin[]
client?: import('@marsprotocol/wallet-connector').WalletClient
enableAnimations: boolean
isOpen: boolean
balances: Coin[]
selectedAccount: string | null
client?: import('@marsprotocol/wallet-connector').WalletClient
status: import('@marsprotocol/wallet-connector').WalletConnectionStatus
}

View File

@ -1,12 +1,11 @@
interface ModalSlice {
addVaultBorrowingsModal: AddVaultBorrowingsModal | null
borrowModal: BorrowModal | null
createAccountModal: boolean
deleteAccountModal: boolean
fundAccountModal: boolean
fundAndWithdrawModal: 'fund' | 'withdraw' | null
vaultModal: {
vault: Vault
} | null
vaultModal: VaultModal | null
}
interface BorrowModal {
@ -14,3 +13,12 @@ interface BorrowModal {
marketData: BorrowAsset | BorrowAssetActive
isRepay?: boolean
}
interface VaultModal {
vault: Vault
selectedBorrowDenoms: string[]
}
interface AddVaultBorrowingsModal {
selectedDenoms: string[]
}

View File

@ -19,3 +19,7 @@ export function getBaseAsset() {
export function getDisplayCurrencies() {
return ASSETS.filter((asset) => asset.isDisplayCurrency)
}
export function findCoinByDenom(denom: string, coins: BigNumberCoin[]) {
return coins.find((coin) => coin.denom === denom)
}

View File

@ -4,6 +4,7 @@ import { IS_TESTNET } from 'constants/env'
import { TESTNET_VAULTS, VAULTS } from 'constants/vaults'
import { BN } from 'utils/helpers'
import { getNetCollateralValue } from 'utils/accounts'
import { BNCoin } from 'types/classes/BNCoin'
export function getVaultMetaData(address: string) {
const vaults = IS_TESTNET ? TESTNET_VAULTS : VAULTS
@ -16,8 +17,8 @@ export function calculateMaxBorrowAmounts(
marketAssets: Market[],
prices: Coin[],
denoms: string[],
): Map<string, BigNumber> {
const maxAmounts = new Map<string, BigNumber>()
): BNCoin[] {
const maxAmounts: BNCoin[] = []
const collateralValue = getNetCollateralValue(account, marketAssets, prices)
for (const denom of denoms) {
@ -29,7 +30,7 @@ export function calculateMaxBorrowAmounts(
const borrowValue = BN(1).minus(borrowAsset.maxLtv).times(borrowAssetPrice)
const amount = collateralValue.dividedBy(borrowValue).decimalPlaces(0)
maxAmounts.set(denom, amount)
maxAmounts.push(new BNCoin({ denom, amount: amount.toString() }))
}
return maxAmounts