feat: finished deposit and withdraw

This commit is contained in:
Linkie Link 2024-02-15 20:40:22 +01:00
parent f609540809
commit 37be3240eb
No known key found for this signature in database
GPG Key ID: 5318B0F2564D38EA
16 changed files with 281 additions and 15 deletions

View File

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

View File

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

View File

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

View File

@ -0,0 +1,83 @@
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 '../AssetAmountSelectActionModal'
import DetailsHeader from '../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,67 @@
import { useCallback, useState } from 'react'
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 AssetAmountSelectActionModal from '../AssetAmountSelectActionModal'
import DetailsHeader from '../LendAndReclaim/DetailsHeader'
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

@ -0,0 +1,16 @@
import useAccount from 'hooks/accounts/useAccount'
import useStore from 'store'
import Deposit from './Deposit'
import Withdraw from './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

@ -4,11 +4,12 @@ export { default as AlertDialogController } from 'components/Modals/AlertDialog'
export { default as BorrowModal } from 'components/Modals/BorrowModal'
export { default as FundAndWithdrawModal } from 'components/Modals/FundWithdraw'
export { default as GetStartedModal } from 'components/Modals/GetStartedModal'
export { default as HlsModal } from 'components/Modals/HLS'
export { default as HlsManageModal } from 'components/Modals/HLS/Manage'
export { default as LendAndReclaimModalController } from 'components/Modals/LendAndReclaim'
export { default as SettingsModal } from 'components/Modals/Settings'
export { default as UnlockModal } from 'components/Modals/Unlock'
export { default as V1DepositAndWithdraw } from 'components/Modals/V1DepositAndWithdraw'
export { default as VaultModal } from 'components/Modals/Vault'
export { default as WalletAssets } from 'components/Modals/WalletAssets'
export { default as WithdrawFromVaultsModal } from 'components/Modals/WithdrawFromVaultsModal'
export { default as HlsModal } from 'components/Modals/HLS'
export { default as HlsManageModal } from 'components/Modals/HLS/Manage'

View File

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

View File

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

View File

@ -37,7 +37,9 @@ export default function DepositButton(props: Props) {
disabled={!hasBalance}
color='tertiary'
onClick={(e) => {
useStore.setState({ fundAndWithdrawModal: 'fund' })
useStore.setState({
v1DepositAndWithdrawModal: { type: 'deposit', data: props.data },
})
e.stopPropagation()
}}
text='Deposit'

View File

@ -22,17 +22,23 @@ export default function Manage(props: Props) {
{
icon: <ArrowUpLine />,
text: 'Deposit more',
onClick: () => openLend(props.data),
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: () => openReclaim(props.data),
onClick: () =>
useStore.setState({
v1DepositAndWithdrawModal: { type: 'withdraw', data: props.data },
}),
},
],
[hasBalance, openLend, openReclaim, props.data],
[hasBalance, props.data],
)
return (

View File

@ -3,10 +3,12 @@ import useSWR from 'swr'
import getAccount from 'api/accounts/getAccount'
import getV1Positions from 'api/v1/getV1Positions'
import useChainConfig from 'hooks/useChainConfig'
import useStore from 'store'
export default function useAccount(accountId?: string, suspense?: boolean) {
const chainConfig = useChainConfig()
const isV1 = isNaN(parseInt(accountId || ''))
const address = useStore((s) => s.address)
const isV1 = accountId === address
const cacheKey = isV1
? `chains/${chainConfig.id}/v1/user/${accountId}`

View File

@ -16,6 +16,7 @@ import {
ExecuteMsg as CreditManagerExecuteMsg,
ExecuteMsg,
} from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { ExecuteMsg as RedBankExecuteMsg } from 'types/generated/mars-red-bank/MarsRedBank.types'
import { AccountKind } from 'types/generated/mars-rover-health-types/MarsRoverHealthTypes.types'
import { byDenom, bySymbol } from 'utils/array'
import { generateErrorMessage, getSingleValueFromBroadcastResult } from 'utils/broadcast'
@ -30,7 +31,7 @@ import { getVaultDepositCoinsFromActions } from 'utils/vaults'
function generateExecutionMessage(
sender: string | undefined = '',
contract: string,
msg: CreditManagerExecuteMsg | AccountNftExecuteMsg | PythUpdateExecuteMsg,
msg: CreditManagerExecuteMsg | AccountNftExecuteMsg | RedBankExecuteMsg | PythUpdateExecuteMsg,
funds: Coin[],
) {
return new MsgExecuteContract({
@ -1065,5 +1066,69 @@ export default function createBroadcastSlice(
{ denom: get().chainConfig.assets[0].denom, amount: String(pythAssets.length) },
])
},
v1Action: async (type: V1ActionType, coin: BNCoin) => {
let msg: RedBankExecuteMsg
let toastOptions: ToastObjectOptions = {
action: type,
accountId: get().address,
changes: {},
}
let funds: Coin[] = []
switch (type) {
case 'withdraw':
msg = {
withdraw: {
amount: coin.amount.toString(),
denom: coin.denom,
},
}
toastOptions = {
...toastOptions,
changes: { deposits: [coin] },
target: 'wallet',
}
break
case 'repay':
msg = {
repay: {},
}
toastOptions.changes = { deposits: [coin] }
funds = [coin.toCoin()]
break
case 'borrow':
msg = {
borrow: {
amount: coin.amount.toString(),
denom: coin.denom,
},
}
toastOptions = {
...toastOptions,
changes: { debts: [coin] },
target: 'wallet',
}
break
default:
msg = {
deposit: {},
}
toastOptions.changes = { deposits: [coin] }
funds = [coin.toCoin()]
}
const redBankContract = get().chainConfig.contracts.redBank
const response = get().executeMsg({
messages: [generateExecutionMessage(get().address, redBankContract, msg, funds)],
})
get().setToast({
response,
options: toastOptions,
})
return response.then((response) => !!response.result)
},
}
}

View File

@ -19,5 +19,6 @@ export default function createModalSlice(set: SetState<ModalSlice>, get: GetStat
vaultModal: null,
walletAssetsModal: null,
withdrawFromVaultsModal: null,
v1DepositAndWithdrawModal: null,
}
}

View File

@ -167,4 +167,7 @@ interface BroadcastSlice {
borrow: BNCoin[]
reclaims: ActionCoin[]
}) => Promise<boolean>
v1Action: (type: V1ActionType, funds: BNCoin) => Promise<boolean>
}
type V1ActionType = 'withdraw' | 'deposit' | 'borrow' | 'repay'

View File

@ -7,6 +7,7 @@ interface ModalSlice {
hlsManageModal: HlsManageModal | null
borrowModal: BorrowModal | null
fundAndWithdrawModal: 'fund' | 'withdraw' | null
v1DepositAndWithdrawModal: V1DepositAndWithdrawModal | null
getStartedModal: boolean
hlsInformationModal: boolean | null
lendAndReclaimModal: LendAndReclaimModalConfig | null
@ -84,3 +85,8 @@ interface HlsManageModal {
}
type HlsStakingManageAction = 'deposit' | 'withdraw' | 'repay' | 'leverage'
interface V1DepositAndWithdrawModal {
type: 'deposit' | 'withdraw'
data: LendingMarketTableData
}