Mp 2681 interact with credit account modal (#189)

* MP-2681: fixed and simplified fund and withdraw
;.
:

* MP-2352: created the CreditAccountComposition

* fix: adjusted according to feedback

* fix: fixed funding account max

* fix: fix portfolio
This commit is contained in:
Linkie Link 2023-05-09 17:22:11 +02:00 committed by GitHub
parent 78aa6b3089
commit 5c01ec6872
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 392 additions and 151 deletions

View File

@ -11,13 +11,18 @@ interface Props {
interface Item { interface Item {
title: string title: string
content: React.ReactNode content: React.ReactNode
open?: boolean
} }
export default function Accordion(props: Props) { export default function Accordion(props: Props) {
return ( return (
<Card> <Card className='w-full'>
{props.items.map((item) => ( {props.items.map((item) => (
<details key={item.title} className='group border-b-white/10 [&:not(:last-child)]:border-b'> <details
key={item.title}
open={item.open}
className='group border-b-white/10 [&:not(:last-child)]:border-b'
>
<summary <summary
className={classNames( className={classNames(
'mb-0 flex cursor-pointer items-center justify-between border-t border-white/10 bg-white/10 p-4 text-white', 'mb-0 flex cursor-pointer items-center justify-between border-t border-white/10 bg-white/10 p-4 text-white',

View File

@ -0,0 +1,98 @@
'use client'
import BigNumber from 'bignumber.js'
import classNames from 'classnames'
import DisplayCurrency from 'components/DisplayCurrency'
import { ArrowRight } from 'components/Icons'
import Text from 'components/Text'
import useStore from 'store'
import {
calculateAccountApr,
calculateAccountBalance,
calculateAccountBorrowRate,
calculateAccountDebt,
calculateAccountPnL,
} from 'utils/accounts'
import { BN } from 'utils/helpers'
interface Props {
account: Account
change?: AccountChange
}
interface ItemProps {
title: string
current: BigNumber
change: BigNumber
className?: string
}
export default function AccountComposition(props: Props) {
const prices = useStore((s) => s.prices)
const balance = calculateAccountBalance(props.account, prices)
const balanceChange = props.change ? calculateAccountBalance(props.change, prices) : BN(0)
const debtBalance = calculateAccountDebt(props.account, prices)
const debtBalanceChange = props.change ? calculateAccountDebt(props.change, prices) : BN(0)
const pnL = calculateAccountPnL(props.account, prices)
const pnLChange = props.change ? calculateAccountPnL(props.change, prices) : BN(0)
const apr = calculateAccountApr(props.account, prices)
const aprChange = props.change ? calculateAccountPnL(props.change, prices) : BN(0)
const borrowRate = calculateAccountBorrowRate(props.account, prices)
const borrowRateChange = props.change ? calculateAccountPnL(props.change, prices) : BN(0)
return (
<div className='w-full flex-wrap'>
<Item
title='Total Position Value'
current={balance}
change={balance.plus(balanceChange)}
className='pb-3'
/>
<Item
title='Total Liabilities'
current={debtBalance}
change={debtBalance.plus(debtBalanceChange)}
className='pb-3'
/>
<Item
title='Unrealized PnL'
current={pnL}
change={pnL.plus(pnLChange)}
className='border border-transparent border-y-white/20 py-3'
/>
<Item title='APR' current={apr} change={apr.plus(aprChange)} className='py-3' />
<Item title='Borrow Rate' current={borrowRate} change={borrowRate.plus(borrowRateChange)} />
</div>
)
}
function Item(props: ItemProps) {
const baseCurrency = useStore((s) => s.baseCurrency)
const increase = props.current.isLessThan(props.change)
return (
<div className={classNames('flex w-full flex-nowrap', props.className)}>
<div className='flex flex-shrink items-center'>
<Text size='sm' className='text-white/60'>
{props.title}
</Text>
</div>
<div className='flex flex-grow items-center justify-end gap-2'>
<DisplayCurrency
coin={{ amount: props.current.toString(), denom: baseCurrency.denom }}
className='text-sm'
/>
{!props.current.isEqualTo(props.change) && (
<>
<span className={classNames('w-3', increase ? 'text-profit' : 'text-loss')}>
<ArrowRight />
</span>
<DisplayCurrency
coin={{ amount: props.change.toString(), denom: baseCurrency.denom }}
className={classNames('text-sm', increase ? 'text-profit' : 'text-loss')}
/>
</>
)}
</div>
</div>
)
}

View File

@ -15,6 +15,7 @@ import useToggle from 'hooks/useToggle'
import useStore from 'store' import useStore from 'store'
import { calculateAccountBalance } from 'utils/accounts' import { calculateAccountBalance } from 'utils/accounts'
import { hardcodedFee } from 'utils/contants' import { hardcodedFee } from 'utils/contants'
import { BN } from 'utils/helpers'
import useParams, { getRoute } from 'utils/route' import useParams, { getRoute } from 'utils/route'
interface Props { interface Props {
@ -41,7 +42,7 @@ export default function AccountList(props: Props) {
const selectedAccountDetails = props.accounts.find((account) => account.id === selectedAccount) const selectedAccountDetails = props.accounts.find((account) => account.id === selectedAccount)
const selectedAccountBalance = selectedAccountDetails const selectedAccountBalance = selectedAccountDetails
? calculateAccountBalance(selectedAccountDetails, prices) ? calculateAccountBalance(selectedAccountDetails, prices)
: 0 : BN(0)
async function deleteAccountHandler() { async function deleteAccountHandler() {
if (!accountSelected) return if (!accountSelected) return
@ -107,7 +108,9 @@ export default function AccountList(props: Props) {
text='Fund' text='Fund'
color='tertiary' color='tertiary'
leftIcon={<ArrowUpLine />} leftIcon={<ArrowUpLine />}
onClick={() => props.setShowFundAccount(true)} onClick={() => {
useStore.setState({ fundAndWithdrawModal: 'fund' })
}}
/> />
<Button <Button
className='w-full' className='w-full'
@ -115,9 +118,9 @@ export default function AccountList(props: Props) {
leftIcon={<ArrowDownLine />} leftIcon={<ArrowDownLine />}
text='Withdraw' text='Withdraw'
onClick={() => { onClick={() => {
useStore.setState({ withdrawModal: true }) useStore.setState({ fundAndWithdrawModal: 'withdraw' })
}} }}
disabled={positionBalance <= 0} disabled={positionBalance.isLessThanOrEqualTo(0)}
/> />
<Button <Button
className='w-full' className='w-full'

View File

@ -2,7 +2,7 @@
import classNames from 'classnames' import classNames from 'classnames'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { useState } from 'react' import { useEffect, useState } from 'react'
import AccountList from 'components/Account/AccountList' import AccountList from 'components/Account/AccountList'
import CreateAccount from 'components/Account/CreateAccount' import CreateAccount from 'components/Account/CreateAccount'
@ -52,6 +52,10 @@ export default function AccountMenuContent(props: Props) {
router.push(`/wallets/${params.address}/accounts/${accountId}`) router.push(`/wallets/${params.address}/accounts/${accountId}`)
} }
useEffect(() => {
useStore.setState({ accounts: props.accounts })
}, [props.accounts])
if (!params.address) return null if (!params.address) return null
return ( return (

View File

@ -1,11 +1,12 @@
'use client' 'use client'
import BigNumber from 'bignumber.js'
import AccountHealth from 'components/Account/AccountHealth'
import DisplayCurrency from 'components/DisplayCurrency' import DisplayCurrency from 'components/DisplayCurrency'
import useStore from 'store' import useStore from 'store'
import AccountHealth from './AccountHealth'
interface Props { interface Props {
balance: number balance: BigNumber
risk: number risk: number
health: number health: number
} }

View File

@ -1,18 +1,35 @@
'use client'
import Accordion from 'components/Accordion' import Accordion from 'components/Accordion'
import AccountComposition from 'components/Account/AccountComposition'
import AccountHealth from 'components/Account/AccountHealth' import AccountHealth from 'components/Account/AccountHealth'
import Card from 'components/Card' import Card from 'components/Card'
import DisplayCurrency from 'components/DisplayCurrency'
import { ArrowChartLineUp } from 'components/Icons' import { ArrowChartLineUp } from 'components/Icons'
import Text from 'components/Text' import Text from 'components/Text'
import useParams from 'utils/route' import useStore from 'store'
import { calculateAccountBalance } from 'utils/accounts'
import { BN } from 'utils/helpers'
export default function AccountSummary() { interface Props {
const params = useParams() account?: Account
change?: AccountChange
}
export default function AccountSummary(props: Props) {
const prices = useStore((s) => s.prices)
const baseCurrency = useStore((s) => s.baseCurrency)
const accountBalance = props.account ? calculateAccountBalance(props.account, prices) : BN(0)
if (!props.account) return null
return ( return (
<div className='flex min-w-[320px] flex-col'> <div className='flex basis-[345px] flex-wrap'>
<Card className='mb-4 min-w-fit bg-white/10' contentClassName='flex'> <Card className='mb-4 min-w-fit bg-white/10' contentClassName='flex'>
<Item> <Item>
<Text size='sm'>$90,000</Text> <DisplayCurrency
coin={{ amount: accountBalance.toString(), denom: baseCurrency.denom }}
className='text-sm'
/>
</Item> </Item>
<Item> <Item>
<span className='flex h-4 w-4 items-center'> <span className='flex h-4 w-4 items-center'>
@ -26,8 +43,11 @@ export default function AccountSummary() {
</Card> </Card>
<Accordion <Accordion
items={[ items={[
{ title: `Subaccount ${params.accountId} Composition`, content: <p>My content</p> }, {
{ title: 'Risk Score: 60/100', content: <p>My content</p> }, title: `Subaccount ${props.account.id} Composition`,
content: <AccountComposition account={props.account} change={props.change} />,
open: true,
},
{ title: 'Balances', content: <p>My content</p> }, { title: 'Balances', content: <p>My content</p> },
]} ]}
/> />

View File

@ -1,7 +1,7 @@
'use client' 'use client'
import { useCallback, useState } from 'react'
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import { useCallback, useState } from 'react'
import { Button } from 'components/Button' import { Button } from 'components/Button'
import { ArrowRight, Cross } from 'components/Icons' import { ArrowRight, Cross } from 'components/Icons'
@ -11,6 +11,7 @@ import TokenInputWithSlider from 'components/TokenInputWithSlider'
import { ASSETS } from 'constants/assets' import { ASSETS } from 'constants/assets'
import useToggle from 'hooks/useToggle' import useToggle from 'hooks/useToggle'
import useStore from 'store' import useStore from 'store'
import { getAmount } from 'utils/accounts'
import { hardcodedFee } from 'utils/contants' import { hardcodedFee } from 'utils/contants'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
import useParams from 'utils/route' import useParams from 'utils/route'
@ -23,12 +24,15 @@ interface Props {
export default function FundAccount(props: Props) { export default function FundAccount(props: Props) {
const params = useParams() const params = useParams()
const deposit = useStore((s) => s.deposit) const deposit = useStore((s) => s.deposit)
const balances = useStore((s) => s.balances)
const [amount, setAmount] = useState(BN(0)) const [amount, setAmount] = useState(BN(0))
const [asset, setAsset] = useState<Asset>(ASSETS[0]) const [asset, setAsset] = useState<Asset>(ASSETS[0])
const [isLending, setIsLending] = useToggle() const [isLending, setIsLending] = useToggle()
const [isFunding, setIsFunding] = useToggle() const [isFunding, setIsFunding] = useToggle()
const max = getAmount(asset.denom, balances ?? [])
const onChangeAmount = useCallback((amount: BigNumber) => { const onChangeAmount = useCallback((amount: BigNumber) => {
setAmount(amount) setAmount(amount)
}, []) }, [])
@ -85,10 +89,11 @@ export default function FundAccount(props: Props) {
onChange={onChangeAmount} onChange={onChangeAmount}
onChangeAsset={onChangeAsset} onChangeAsset={onChangeAsset}
amount={amount} amount={amount}
max={BN(1).shiftedBy(ASSETS[0].decimals)} max={max}
className='mb-4' className='mb-4'
disabled={isFunding} disabled={isFunding}
hasSelect hasSelect
balances={balances}
/> />
<div className='mb-4 w-full border-b border-white/10' /> <div className='mb-4 w-full border-b border-white/10' />
<SwitchWithLabel <SwitchWithLabel

View File

@ -53,7 +53,7 @@ export default function Modal(props: Props) {
> >
<Card <Card
className={classNames( className={classNames(
'relative w-full max-w-full bg-white/5 backdrop-blur-3xl', 'relative flex max-w-full flex-grow bg-white/5 backdrop-blur-3xl',
props.className, props.className,
)} )}
> >

View File

@ -11,6 +11,7 @@ import Text from 'components/Text'
import TitleAndSubCell from 'components/TitleAndSubCell' import TitleAndSubCell from 'components/TitleAndSubCell'
import TokenInputWithSlider from 'components/TokenInputWithSlider' import TokenInputWithSlider from 'components/TokenInputWithSlider'
import { ASSETS } from 'constants/assets' import { ASSETS } from 'constants/assets'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useStore from 'store' import useStore from 'store'
import { hardcodedFee } from 'utils/contants' import { hardcodedFee } from 'utils/contants'
import { formatPercent, formatValue } from 'utils/formatters' import { formatPercent, formatValue } from 'utils/formatters'
@ -29,6 +30,7 @@ function getAssetLogo(modal: BorrowModal | null) {
export default function BorrowModal() { export default function BorrowModal() {
const params = useParams() const params = useParams()
const currentAccount = useCurrentAccount()
const [percentage, setPercentage] = useState(0) const [percentage, setPercentage] = useState(0)
const [amount, setAmount] = useState(BN(0)) const [amount, setAmount] = useState(BN(0))
const [selectedAccount, setSelectedAccount] = useState(params.accountId) const [selectedAccount, setSelectedAccount] = useState(params.accountId)
@ -149,7 +151,7 @@ export default function BorrowModal() {
rightIcon={<ArrowRight />} rightIcon={<ArrowRight />}
/> />
</Card> </Card>
<AccountSummary /> <AccountSummary account={currentAccount} />
</div> </div>
</Modal> </Modal>
) )

View File

@ -0,0 +1,140 @@
'use client'
import { useEffect, useState } from 'react'
import AccountSummary from 'components/Account/AccountSummary'
import { Button } from 'components/Button'
import Card from 'components/Card'
import Divider from 'components/Divider'
import { ArrowRight } from 'components/Icons'
import Modal from 'components/Modal'
import Text from 'components/Text'
import TokenInputWithSlider from 'components/TokenInputWithSlider'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
import { getAmount } from 'utils/accounts'
import { hardcodedFee } from 'utils/contants'
import { BN } from 'utils/helpers'
export default function FundAndWithdrawModal() {
const currentAccount = useCurrentAccount()
const modal = useStore((s) => s.fundAndWithdrawModal)
const baseCurrency = useStore((s) => s.baseCurrency)
const withdraw = useStore((s) => s.withdraw)
const deposit = useStore((s) => s.deposit)
const [amount, setAmount] = useState(BN(0))
const [currentAsset, setCurrentAsset] = useState(baseCurrency)
const [change, setChange] = useState<AccountChange | undefined>()
const [isConfirming, setIsConfirming] = useToggle()
const balances = useStore((s) => s.balances)
const isFunding = modal === 'fund'
function resetState() {
setCurrentAsset(baseCurrency)
setAmount(BN(0))
setChange(undefined)
}
function onClose() {
resetState()
useStore.setState({ fundAndWithdrawModal: null })
}
async function onConfirm() {
if (!currentAccount) return
setIsConfirming(true)
let result
if (isFunding) {
result = await deposit({
fee: hardcodedFee,
accountId: currentAccount.id,
coin: {
denom: currentAsset.denom,
amount: amount.toString(),
},
})
} else {
result = await withdraw({
fee: hardcodedFee,
accountId: currentAccount.id,
coin: {
denom: currentAsset.denom,
amount: amount.toString(),
},
})
}
setIsConfirming(false)
if (result) {
resetState()
useStore.setState({ fundAndWithdrawModal: null })
}
}
const max = isFunding
? getAmount(currentAsset.denom, balances ?? [])
: currentAccount
? getAmount(currentAsset.denom, currentAccount.deposits)
: BN(0)
useEffect(() => {
setChange({
deposits: [
{
amount: isFunding ? BN(0).plus(amount).toString() : BN(0).minus(amount).toString(),
denom: currentAsset.denom,
},
],
})
}, [amount, currentAsset, currentAccount, isFunding])
return (
<Modal
open={!!modal}
onClose={onClose}
header={
<span className='flex items-center gap-4 px-4'>
<Text>
{isFunding
? `Fund Account ${currentAccount?.id ?? '0'}`
: `Withdraw from Account ${currentAccount?.id ?? '0'}`}
</Text>
</span>
}
headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b'
contentClassName='flex flex-col min-h-[400px]'
>
<div className='flex flex-grow items-start gap-6 p-6'>
<Card
className='flex flex-grow bg-white/5 p-4'
contentClassName='gap-6 flex flex-col justify-between h-full'
>
<TokenInputWithSlider
asset={currentAsset}
onChange={(val) => {
setAmount(val)
}}
onChangeAsset={(asset) => {
setCurrentAsset(asset)
}}
amount={amount}
max={max}
hasSelect
balances={isFunding ? balances : currentAccount?.deposits ?? []}
accountId={!isFunding ? currentAccount?.id ?? '0' : undefined}
/>
<Divider />
<Button
onClick={onConfirm}
showProgressIndicator={isConfirming}
className='w-full'
text={isFunding ? 'Fund' : 'Withdraw'}
rightIcon={<ArrowRight />}
/>
</Card>
<AccountSummary account={currentAccount} change={change} />
</div>
</Modal>
)
}

View File

@ -1,14 +1,14 @@
'use client' 'use client'
import WithdrawModal from 'components/Modals//WithdrawModal'
import BorrowModal from 'components/Modals/BorrowModal' import BorrowModal from 'components/Modals/BorrowModal'
import FundAndWithdrawModal from 'components/Modals/FundAndWithdrawModal'
import VaultModal from 'components/Modals/VaultModal' import VaultModal from 'components/Modals/VaultModal'
export default function ModalsContainer() { export default function ModalsContainer() {
return ( return (
<> <>
<BorrowModal /> <BorrowModal />
<WithdrawModal /> <FundAndWithdrawModal />
<VaultModal /> <VaultModal />
</> </>
) )

View File

@ -22,11 +22,11 @@ import { formatValue } from 'utils/formatters'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
export default function VaultModal() { export default function VaultModal() {
const currentAccount = useCurrentAccount()
const modal = useStore((s) => s.vaultModal) const modal = useStore((s) => s.vaultModal)
const [amount, setAmount] = useState(BN(0)) const [amount, setAmount] = useState(BN(0))
const [percentage, setPercentage] = useState(0) const [percentage, setPercentage] = useState(0)
const [isCustomAmount, setIsCustomAmount] = useState(false) const [isCustomAmount, setIsCustomAmount] = useState(false)
const currentAccount = useCurrentAccount()
function handleSwitch() { function handleSwitch() {
setIsCustomAmount(() => !isCustomAmount) setIsCustomAmount(() => !isCustomAmount)
@ -80,7 +80,7 @@ export default function VaultModal() {
</div> </div>
<div className='flex flex-grow items-start gap-6 p-6'> <div className='flex flex-grow items-start gap-6 p-6'>
<Card <Card
className='w-full bg-white/5 p-4' className='flex flex-grow bg-white/5 p-4'
contentClassName='gap-6 flex flex-col justify-between h-full' contentClassName='gap-6 flex flex-col justify-between h-full'
> >
<TokenInput <TokenInput
@ -112,7 +112,7 @@ export default function VaultModal() {
rightIcon={<ArrowRight />} rightIcon={<ArrowRight />}
/> />
</Card> </Card>
<AccountSummary /> <AccountSummary account={currentAccount} />
</div> </div>
</Modal> </Modal>
) )

View File

@ -1,96 +0,0 @@
import { useState } from 'react'
import AccountSummary from 'components/Account/AccountSummary'
import { Button } from 'components/Button'
import Card from 'components/Card'
import Divider from 'components/Divider'
import { ArrowRight } from 'components/Icons'
import Modal from 'components/Modal'
import Text from 'components/Text'
import TokenInputWithSlider from 'components/TokenInputWithSlider'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
import { getAmount } from 'utils/accounts'
import { hardcodedFee } from 'utils/contants'
import { BN } from 'utils/helpers'
export default function WithdrawModal() {
const currentAccount = useCurrentAccount()
const modal = useStore((s) => s.withdrawModal)
const baseCurrency = useStore((s) => s.baseCurrency)
const withdraw = useStore((s) => s.withdraw)
const [amount, setAmount] = useState(BN(0))
const [currentAsset, setCurrentAsset] = useState(baseCurrency)
const [isWithdrawing, setIsWithdrawing] = useToggle()
function onClose() {
useStore.setState({ withdrawModal: false })
setAmount(BN(0))
}
async function onWithdraw() {
if (!currentAccount) return
setIsWithdrawing(true)
const result = await withdraw({
fee: hardcodedFee,
accountId: currentAccount.id,
coin: {
denom: currentAsset.denom,
amount: amount.toString(),
},
})
setIsWithdrawing(false)
if (result) {
useStore.setState({ withdrawModal: false })
}
}
const maxWithdraw = currentAccount
? getAmount(currentAsset.denom, currentAccount.deposits)
: BN(0)
return (
<Modal
open={modal}
onClose={onClose}
header={
<span className='flex items-center gap-4 px-4'>
<Text>{`Withdraw from Account ${currentAccount?.id ?? '0'}`}</Text>
</span>
}
headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b'
contentClassName='flex flex-col min-h-[400px]'
>
<div className='flex flex-grow items-start gap-6 p-6'>
<Card
className='w-full bg-white/5 p-4'
contentClassName='gap-6 flex flex-col justify-between h-full'
>
<TokenInputWithSlider
asset={currentAsset}
onChange={(val) => {
setAmount(val)
}}
onChangeAsset={(asset) => {
setCurrentAsset(asset)
}}
amount={amount}
max={maxWithdraw}
hasSelect
currentAccount={currentAccount}
/>
<Divider />
<Button
onClick={onWithdraw}
showProgressIndicator={isWithdrawing}
className='w-full'
text='Withdraw'
rightIcon={<ArrowRight />}
/>
</Card>
<AccountSummary />
</div>
</Modal>
)
}

View File

@ -31,7 +31,7 @@ async function Content(props: PageProps) {
{account.map((account: Account, index: number) => ( {account.map((account: Account, index: number) => (
<Card <Card
className='h-fit w-full bg-white/5' className='h-fit w-full bg-white/5'
title={`Account ${account}`} title={`Account ${account.id}`}
key={index} key={index}
contentClassName='px-4 py-6' contentClassName='px-4 py-6'
> >

View File

@ -1,3 +1,5 @@
'use client'
import classNames from 'classnames' import classNames from 'classnames'
import { ChangeEvent, useRef, useState } from 'react' import { ChangeEvent, useRef, useState } from 'react'
import Draggable from 'react-draggable' import Draggable from 'react-draggable'

View File

@ -19,7 +19,8 @@ interface Props {
onChange: (amount: BigNumber) => void onChange: (amount: BigNumber) => void
className?: string className?: string
disabled?: boolean disabled?: boolean
currentAccount?: Account balances?: Coin[] | null
accountId?: string
} }
interface SingleProps extends Props { interface SingleProps extends Props {
@ -37,7 +38,6 @@ interface SelectProps extends Props {
} }
export default function TokenInput(props: SingleProps | SelectProps) { export default function TokenInput(props: SingleProps | SelectProps) {
const balances = useStore((s) => s.balances)
const baseCurrency = useStore((s) => s.baseCurrency) const baseCurrency = useStore((s) => s.baseCurrency)
const [asset, setAsset] = useState<Asset>(props.asset ? props.asset : baseCurrency) const [asset, setAsset] = useState<Asset>(props.asset ? props.asset : baseCurrency)
const [coin, setCoin] = useState<Coin>({ const [coin, setCoin] = useState<Coin>({
@ -45,26 +45,24 @@ export default function TokenInput(props: SingleProps | SelectProps) {
amount: '0', amount: '0',
}) })
const selectableBalances = props.currentAccount?.deposits ?? balances
const selectedAssetDenom = props.asset ? props.asset.denom : baseCurrency.denom const selectedAssetDenom = props.asset ? props.asset.denom : baseCurrency.denom
const updateAsset = useCallback( const updateAsset = useCallback(
(coinDenom: string) => { (coinDenom: string) => {
const newAsset = ASSETS.find((asset) => asset.denom === coinDenom) ?? baseCurrency const newAsset = ASSETS.find((asset) => asset.denom === coinDenom) ?? baseCurrency
const newCoin = selectableBalances?.find((coin) => coin.denom === coinDenom) const newCoin = props.balances?.find((coin) => coin.denom === coinDenom)
setAsset(newAsset) setAsset(newAsset)
setCoin(newCoin ?? { denom: coinDenom, amount: '0' }) setCoin(newCoin ?? { denom: coinDenom, amount: '0' })
}, },
[selectableBalances, baseCurrency], [props.balances, baseCurrency],
) )
function setDefaultAsset() { function setDefaultAsset() {
if (!selectableBalances || selectableBalances?.length === 0) return setAsset(baseCurrency) if (!props.balances || props.balances?.length === 0) return setAsset(baseCurrency)
if (selectableBalances.length === 1) if (props.balances.length === 1) {
return setAsset( const balances = props.balances ?? []
ASSETS.find((asset) => asset.denom === selectableBalances[0].denom) ?? baseCurrency, return setAsset(ASSETS.find((asset) => asset.denom === balances[0].denom) ?? baseCurrency)
) }
return setAsset(ASSETS.find((asset) => asset.denom === selectedAssetDenom) ?? baseCurrency) return setAsset(ASSETS.find((asset) => asset.denom === selectedAssetDenom) ?? baseCurrency)
} }
@ -91,12 +89,12 @@ export default function TokenInput(props: SingleProps | SelectProps) {
)} )}
> >
<div className='relative isolate z-40 box-content flex h-11 w-full rounded-sm border border-white/20 bg-white/5'> <div className='relative isolate z-40 box-content flex h-11 w-full rounded-sm border border-white/20 bg-white/5'>
{props.hasSelect && selectableBalances ? ( {props.hasSelect && props.balances ? (
<Select <Select
options={selectableBalances} options={props.balances}
defaultValue={coin.denom} defaultValue={coin.denom}
onChange={(value) => updateAsset(value)} onChange={(value) => updateAsset(value)}
title={props.currentAccount ? `Account ${props.currentAccount.id}` : 'Your Wallet'} title={props.accountId ? `Account ${props.accountId}` : 'Your Wallet'}
className='border-r border-white/20 bg-white/5' className='border-r border-white/20 bg-white/5'
/> />
) : ( ) : (

View File

@ -1,5 +1,7 @@
'use client'
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import { useCallback, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import Slider from 'components/Slider' import Slider from 'components/Slider'
import TokenInput from 'components/TokenInput' import TokenInput from 'components/TokenInput'
@ -11,7 +13,8 @@ interface Props {
onChange: (amount: BigNumber) => void onChange: (amount: BigNumber) => void
className?: string className?: string
disabled?: boolean disabled?: boolean
currentAccount?: Account balances?: Coin[] | null
accountId?: string
} }
interface SingleProps extends Props { interface SingleProps extends Props {
@ -64,6 +67,15 @@ export default function TokenInputWithSlider(props: SingleProps | SelectProps) {
[props], [props],
) )
useEffect(() => {
if (props.max?.isEqualTo(max)) return
setMax(props.max ?? BN(0))
setPercentage(0)
setAmount(BN(0))
setAsset(props.asset ?? ASSETS[0])
}, [props.max, props.asset, max])
return ( return (
<div className={props.className}> <div className={props.className}>
<TokenInput <TokenInput
@ -75,7 +87,8 @@ export default function TokenInputWithSlider(props: SingleProps | SelectProps) {
className='mb-4' className='mb-4'
disabled={props.disabled} disabled={props.disabled}
hasSelect={props.hasSelect} hasSelect={props.hasSelect}
currentAccount={props.currentAccount} balances={props.balances}
accountId={props.accountId}
/> />
<Slider <Slider
value={percentage} value={percentage}

View File

@ -6,7 +6,7 @@ export default function createModalSlice(set: SetState<ModalSlice>, get: GetStat
createAccountModal: false, createAccountModal: false,
deleteAccountModal: false, deleteAccountModal: false,
fundAccountModal: false, fundAccountModal: false,
withdrawModal: false, fundAndWithdrawModal: null,
vaultModal: null, vaultModal: null,
} }
} }

View File

@ -5,3 +5,10 @@ interface Account {
lends: Coin[] lends: Coin[]
vaults: import('types/generated/mars-mock-credit-manager/MarsMockCreditManager.types').ArrayOfVaultInfoResponse vaults: import('types/generated/mars-mock-credit-manager/MarsMockCreditManager.types').ArrayOfVaultInfoResponse
} }
interface AccountChange {
deposits?: Coin[]
debts?: Coin[]
lends?: Coin[]
vaults?: import('types/generated/mars-mock-credit-manager/MarsMockCreditManager.types').ArrayOfVaultInfoResponse
}

View File

@ -3,7 +3,7 @@ interface ModalSlice {
createAccountModal: boolean createAccountModal: boolean
deleteAccountModal: boolean deleteAccountModal: boolean
fundAccountModal: boolean fundAccountModal: boolean
withdrawModal: boolean fundAndWithdrawModal: 'fund' | 'withdraw' | null
vaultModal: { vaultModal: {
vault: Vault vault: Vault
} | null } | null

View File

@ -1,21 +1,61 @@
import BigNumber from 'bignumber.js'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
export const calculateAccountBalance = (account: Account, prices: Coin[]) => { export const calculateAccountBalance = (
const totalDepositValue = account.deposits.reduce((acc, deposit) => { account: Account | AccountChange,
prices: Coin[],
): BigNumber => {
const totalDepositValue = calculateAccountDeposits(account, prices)
const totalDebtValue = calculateAccountDebt(account, prices)
return totalDepositValue.minus(totalDebtValue)
}
export const calculateAccountDeposits = (
account: Account | AccountChange,
prices: Coin[],
): BigNumber => {
if (!account.deposits) return BN(0)
return account.deposits.reduce((acc, deposit) => {
const price = prices.find((price) => price.denom === deposit.denom)?.amount ?? 0 const price = prices.find((price) => price.denom === deposit.denom)?.amount ?? 0
const depositValue = BN(deposit.amount).multipliedBy(price) const depositValue = BN(deposit.amount).multipliedBy(price)
return acc.plus(depositValue) return acc.plus(depositValue)
}, BN(0)) }, BN(0))
}
const totalDebtValue = account.debts.reduce((acc, debt) => { export const calculateAccountDebt = (
account: Account | AccountChange,
prices: Coin[],
): BigNumber => {
if (!account.debts) return BN(0)
return account.debts.reduce((acc, debt) => {
const price = prices.find((price) => price.denom === debt.denom)?.amount ?? 0 const price = prices.find((price) => price.denom === debt.denom)?.amount ?? 0
const debtValue = BN(debt.amount).multipliedBy(price) const debtValue = BN(debt.amount).multipliedBy(price)
return acc.plus(debtValue) return acc.plus(debtValue)
}, BN(0)) }, BN(0))
return totalDepositValue.minus(totalDebtValue).toNumber()
} }
export function getAmount(denom: string, coins: Coin[]) { export const calculateAccountPnL = (
account: Account | AccountChange,
prices: Coin[],
): BigNumber => {
return BN(0)
}
export const calculateAccountApr = (
account: Account | AccountChange,
prices: Coin[],
): BigNumber => {
return BN(0)
}
export const calculateAccountBorrowRate = (
account: Account | AccountChange,
prices: Coin[],
): BigNumber => {
return BN(0)
}
export function getAmount(denom: string, coins: Coin[]): BigNumber {
return BN(coins.find((asset) => asset.denom === denom)?.amount ?? 0) return BN(coins.find((asset) => asset.denom === denom)?.amount ?? 0)
} }

View File

@ -2,8 +2,7 @@ import { usePathname } from 'next/navigation'
export default function useParams() { export default function useParams() {
const pathname = usePathname() const pathname = usePathname()
return getParamsFromUrl(pathname ?? '')
return getParamsFromUrl(pathname || '')
} }
export function getRouteParams(url: string | null): PageParams { export function getRouteParams(url: string | null): PageParams {