Mp 2881 finalize the borrow flow table (#308)

* MP-2881: fixed the buttons and adjusted the marketDetails

* MP-2881: finished the borrow to wallet functionatlity

* fix: renamed withdraw to borrowToWallet

* fix: moved the useMemo return into functions

* tidy: refactor

* tidy: refactor
This commit is contained in:
Linkie Link 2023-07-19 12:10:23 +02:00 committed by GitHub
parent e1956ddbe9
commit ac0658224e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 149 additions and 102 deletions

View File

@ -1,7 +1,7 @@
import { useCallback } from 'react' import { useCallback } from 'react'
import Button from 'components/Button' import Button from 'components/Button'
import { Plus } from 'components/Icons' import { Plus, ReceiptCheck } from 'components/Icons'
import useStore from 'store' import useStore from 'store'
import { getEnabledMarketAssets } from 'utils/assets' import { getEnabledMarketAssets } from 'utils/assets'
@ -29,13 +29,15 @@ export default function BorrowActionButtons(props: Props) {
return ( return (
<div className='flex flex-row space-x-2'> <div className='flex flex-row space-x-2'>
<Button <Button
leftIcon={debt ? <Plus /> : undefined} leftIcon={<Plus className='w-3' />}
onClick={borrowHandler} onClick={borrowHandler}
color='secondary' color='secondary'
text={debt ? 'Borrow more' : 'Borrow'} text={debt ? 'Borrow more' : 'Borrow'}
className='min-w-40 text-center' className='min-w-40 text-center'
/> />
{debt && <Button color='secondary' text='Repay' onClick={repayHandler} />} {debt && (
<Button color='tertiary' leftIcon={<ReceiptCheck />} text='Repay' onClick={repayHandler} />
)}
</div> </div>
) )
} }

View File

@ -33,7 +33,7 @@ export default function BorrowTable(props: Props) {
resetExpanded={table.resetExpanded} resetExpanded={table.resetExpanded}
rowData={row} rowData={row}
expandedActionButtons={<BorrowActionButtons data={row.original} />} expandedActionButtons={<BorrowActionButtons data={row.original} />}
expandedDetails={<MarketDetails data={row.original} />} expandedDetails={<MarketDetails data={row.original} type='borrow' />}
/> />
) )
}, },

View File

@ -32,7 +32,7 @@ export default function LendingMarketsTable(props: Props) {
resetExpanded={table.resetExpanded} resetExpanded={table.resetExpanded}
rowData={row} rowData={row}
expandedActionButtons={<LendingActionButtons data={row.original} />} expandedActionButtons={<LendingActionButtons data={row.original} />}
expandedDetails={<MarketDetails data={row.original} />} expandedDetails={<MarketDetails data={row.original} type='lend' />}
/> />
) )
}, },

View File

@ -0,0 +1,8 @@
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M5.99984 7L7.33317 8.33333L10.3332 5.33333M13.3332 14V5.2C13.3332 4.0799 13.3332 3.51984 13.1152 3.09202C12.9234 2.71569 12.6175 2.40973 12.2412 2.21799C11.8133 2 11.2533 2 10.1332 2H5.8665C4.7464 2 4.18635 2 3.75852 2.21799C3.3822 2.40973 3.07624 2.71569 2.88449 3.09202C2.6665 3.51984 2.6665 4.0799 2.6665 5.2V14L4.49984 12.6667L6.1665 14L7.99984 12.6667L9.83317 14L11.4998 12.6667L13.3332 14Z"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>

After

Width:  |  Height:  |  Size: 578 B

View File

@ -31,6 +31,7 @@ export { default as OverlayMark } from 'components/Icons/OverlayMark.svg'
export { default as Plus } from 'components/Icons/Plus.svg' export { default as Plus } from 'components/Icons/Plus.svg'
export { default as PlusCircled } from 'components/Icons/PlusCircled.svg' export { default as PlusCircled } from 'components/Icons/PlusCircled.svg'
export { default as Questionmark } from 'components/Icons/Questionmark.svg' export { default as Questionmark } from 'components/Icons/Questionmark.svg'
export { default as ReceiptCheck } from 'components/Icons/ReceiptCheck.svg'
export { default as Search } from 'components/Icons/Search.svg' export { default as Search } from 'components/Icons/Search.svg'
export { default as Shield } from 'components/Icons/Shield.svg' export { default as Shield } from 'components/Icons/Shield.svg'
export { default as SortAsc } from 'components/Icons/SortAsc.svg' export { default as SortAsc } from 'components/Icons/SortAsc.svg'
@ -41,6 +42,6 @@ export { default as StarOutlined } from 'components/Icons/StarOutlined.svg'
export { default as Subtract } from 'components/Icons/Subtract.svg' export { default as Subtract } from 'components/Icons/Subtract.svg'
export { default as SwapIcon } from 'components/Icons/SwapIcon.svg' export { default as SwapIcon } from 'components/Icons/SwapIcon.svg'
export { default as TrashBin } from 'components/Icons/TrashBin.svg' export { default as TrashBin } from 'components/Icons/TrashBin.svg'
export { default as Wallet } from 'components/Icons/Wallet.svg'
export { default as VerticalThreeLine } from 'components/Icons/VerticalThreeLine.svg' export { default as VerticalThreeLine } from 'components/Icons/VerticalThreeLine.svg'
export { default as Wallet } from 'components/Icons/Wallet.svg'
// @endindex // @endindex

View File

@ -6,6 +6,7 @@ import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice'
interface Props { interface Props {
data: BorrowMarketTableData | LendingMarketTableData data: BorrowMarketTableData | LendingMarketTableData
type: 'borrow' | 'lend'
} }
interface Detail { interface Detail {
@ -14,12 +15,13 @@ interface Detail {
title: string title: string
} }
export default function MarketDetails({ data }: Props) { export default function MarketDetails({ data, type }: Props) {
const { const {
convertAmount, convertAmount,
getConversionRate, getConversionRate,
symbol: displayCurrencySymbol, symbol: displayCurrencySymbol,
} = useDisplayCurrencyPrice() } = useDisplayCurrencyPrice()
const { const {
asset, asset,
marketMaxLtv, marketMaxLtv,
@ -30,48 +32,73 @@ export default function MarketDetails({ data }: Props) {
const totalBorrowed = marketDepositAmount.minus(marketLiquidityAmount) const totalBorrowed = marketDepositAmount.minus(marketLiquidityAmount)
const details: Detail[] = useMemo( const details: Detail[] = useMemo(() => {
() => [ function getLendingMarketDetails() {
{ return [
amount: convertAmount(asset, marketDepositAmount).toNumber(), {
options: { abbreviated: true, suffix: ` ${displayCurrencySymbol}` }, amount: convertAmount(asset, marketDepositAmount).toNumber(),
title: 'Total Supplied', options: { abbreviated: true, suffix: ` ${displayCurrencySymbol}` },
}, title: 'Total Supplied',
{ },
amount: marketMaxLtv * 100, {
options: { minDecimals: 2, maxDecimals: 2, suffix: '%' }, amount: marketMaxLtv * 100,
title: 'Max LTV', options: { minDecimals: 2, maxDecimals: 2, suffix: '%' },
}, title: 'Max LTV',
{ },
amount: marketLiquidationThreshold * 100, {
options: { minDecimals: 2, maxDecimals: 2, suffix: '%' }, amount: marketLiquidationThreshold * 100,
title: 'Liquidation Threshold', options: { minDecimals: 2, maxDecimals: 2, suffix: '%' },
}, title: 'Liquidation Threshold',
{ },
amount: getConversionRate(asset.denom).toNumber(), {
options: { minDecimals: 2, maxDecimals: 2, suffix: ` ${displayCurrencySymbol}` }, amount: getConversionRate(asset.denom).toNumber(),
title: 'Oracle Price', options: { minDecimals: 2, maxDecimals: 2, suffix: ` ${displayCurrencySymbol}` },
}, title: 'Oracle Price',
{ },
amount: totalBorrowed.dividedBy(marketDepositAmount).multipliedBy(100).toNumber(), {
options: { minDecimals: 2, maxDecimals: 2, suffix: '%' }, amount: totalBorrowed.dividedBy(marketDepositAmount).multipliedBy(100).toNumber(),
title: 'Utilization Rate', options: { minDecimals: 2, maxDecimals: 2, suffix: '%' },
}, title: 'Utilization Rate',
], },
[ ]
asset, }
marketDepositAmount,
marketLiquidationThreshold, function getBorrowMarketDetails() {
marketMaxLtv, return [
displayCurrencySymbol, {
convertAmount, amount: convertAmount(asset, totalBorrowed).toNumber(),
getConversionRate, options: { abbreviated: true, suffix: ` ${displayCurrencySymbol}` },
totalBorrowed, title: 'Total Borrowed',
], },
) {
amount: getConversionRate(asset.denom).toNumber(),
options: { minDecimals: 2, maxDecimals: 2, suffix: ` ${displayCurrencySymbol}` },
title: 'Oracle Price',
},
{
amount: totalBorrowed.dividedBy(marketDepositAmount).multipliedBy(100).toNumber(),
options: { minDecimals: 2, maxDecimals: 2, suffix: '%' },
title: 'Utilization Rate',
},
]
}
if (type === 'lend') return getLendingMarketDetails()
return getBorrowMarketDetails()
}, [
type,
asset,
marketDepositAmount,
marketMaxLtv,
marketLiquidationThreshold,
totalBorrowed,
displayCurrencySymbol,
convertAmount,
getConversionRate,
])
return ( return (
<div className='flex flex-1 justify-center rounded-md bg-white bg-opacity-5'> <div className='flex flex-1 justify-between rounded-md bg-white bg-opacity-5'>
{details.map((detail, index) => ( {details.map((detail, index) => (
<TitleAndSubCell <TitleAndSubCell
key={index} key={index}

View File

@ -7,7 +7,7 @@ import Card from 'components/Card'
import Divider from 'components/Divider' import Divider from 'components/Divider'
import { ArrowRight } from 'components/Icons' import { ArrowRight } from 'components/Icons'
import Modal from 'components/Modal' import Modal from 'components/Modal'
import Select from 'components/Select' import Switch from 'components/Switch'
import Text from 'components/Text' 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'
@ -34,16 +34,12 @@ export default function BorrowModal() {
const [percentage, setPercentage] = useState(0) const [percentage, setPercentage] = useState(0)
const [amount, setAmount] = useState(BN(0)) const [amount, setAmount] = useState(BN(0))
const [change, setChange] = useState<AccountChange | undefined>() const [change, setChange] = useState<AccountChange | undefined>()
const [selectedAccount, setSelectedAccount] = useState(currentAccount)
const [isConfirming, setIsConfirming] = useToggle() const [isConfirming, setIsConfirming] = useToggle()
const [borrowToWallet, setBorrowToWallet] = useToggle()
const modal = useStore((s) => s.borrowModal) const modal = useStore((s) => s.borrowModal)
const borrow = useStore((s) => s.borrow) const borrow = useStore((s) => s.borrow)
const repay = useStore((s) => s.repay) const repay = useStore((s) => s.repay)
const asset = modal?.asset ?? ASSETS[0] const asset = modal?.asset ?? ASSETS[0]
const accounts = useStore((s) => s.accounts)
const accountOptions = accounts?.map((account) => {
return { value: account.id, label: `Account ${account.id}` }
})
const isRepay = modal?.isRepay ?? false const isRepay = modal?.isRepay ?? false
function resetState() { function resetState() {
@ -53,21 +49,22 @@ export default function BorrowModal() {
} }
async function onConfirmClick() { async function onConfirmClick() {
if (!modal?.asset) return if (!modal?.asset || !currentAccount) return
setIsConfirming(true) setIsConfirming(true)
let result let result
if (isRepay) { if (isRepay) {
result = await repay({ result = await repay({
fee: hardcodedFee, fee: hardcodedFee,
accountId: selectedAccount?.id ?? '0', accountId: currentAccount.id,
coin: BNCoin.fromDenomAndBigNumber(modal.asset.denom, amount), coin: BNCoin.fromDenomAndBigNumber(modal.asset.denom, amount),
accountBalance: percentage === 100, accountBalance: percentage === 100,
}) })
} else { } else {
result = await borrow({ result = await borrow({
fee: hardcodedFee, fee: hardcodedFee,
accountId: selectedAccount?.id ?? '0', accountId: currentAccount.id,
coin: { denom: modal.asset.denom, amount: amount.toString() }, coin: { denom: modal.asset.denom, amount: amount.toString() },
borrowToWallet,
}) })
} }
@ -95,10 +92,6 @@ export default function BorrowModal() {
const max = BN(isRepay ? getDebtAmount(modal) : modal?.marketData?.liquidity?.amount ?? '0') const max = BN(isRepay ? getDebtAmount(modal) : modal?.marketData?.liquidity?.amount ?? '0')
useEffect(() => {
if (!selectedAccount) setSelectedAccount(currentAccount)
}, [selectedAccount, currentAccount])
useEffect(() => { useEffect(() => {
if (!modal?.asset) return if (!modal?.asset) return
@ -167,19 +160,17 @@ export default function BorrowModal() {
maxText='Max' maxText='Max'
/> />
<Divider className='my-6' /> <Divider className='my-6' />
<Text size='lg' className='pb-2'> <div className='flex flex-1 flex-wrap'>
{isRepay ? 'Repay for' : 'Borrow to'} <Text className='mb-1 w-full'>Receive funds to Wallet</Text>
</Text> <Text size='xs' className='text-white/50'>
<div className='relative flex w-full'> Your borrowed funds will directly go to your wallet
<Select </Text>
options={accountOptions ?? []} </div>
title='Accounts' <div className='flex flex-wrap items-center justify-end'>
defaultValue={selectedAccount?.id} <Switch
containerClassName='w-full' name='borrow-to-wallet'
onChange={(account) => { checked={borrowToWallet}
accounts && setSelectedAccount(accounts?.find((a) => a.id === account)) onChange={setBorrowToWallet}
}}
className='w-full rounded-base border border-white/10 bg-white/5'
/> />
</div> </div>
</div> </div>
@ -192,7 +183,7 @@ export default function BorrowModal() {
rightIcon={<ArrowRight />} rightIcon={<ArrowRight />}
/> />
</Card> </Card>
<AccountSummary account={selectedAccount} change={change} /> <AccountSummary account={currentAccount} change={change} />
</div> </div>
</Modal> </Modal>
) )

View File

@ -42,11 +42,20 @@ export default function createBroadcastSlice(
return { return {
toast: null, toast: null,
borrow: async (options: { fee: StdFee; accountId: string; coin: Coin }) => { borrow: async (options: {
fee: StdFee
accountId: string
coin: Coin
borrowToWallet: boolean
}) => {
const borrowAction: Action = { borrow: options.coin }
const withdrawAction: Action = { withdraw: options.coin }
const actions = options.borrowToWallet ? [borrowAction, withdrawAction] : [borrowAction]
const msg: CreditManagerExecuteMsg = { const msg: CreditManagerExecuteMsg = {
update_credit_account: { update_credit_account: {
account_id: options.accountId, account_id: options.accountId,
actions: [{ borrow: options.coin }], actions,
}, },
} }
@ -54,7 +63,9 @@ export default function createBroadcastSlice(
handleResponseMessages( handleResponseMessages(
response, response,
`Borrowed ${formatAmountWithSymbol(options.coin)} to Account ${options.accountId}`, `Borrowed ${formatAmountWithSymbol(options.coin)} to ${
options.borrowToWallet ? 'Wallet' : `Account ${options.accountId}`
}`,
) )
return !!response.result return !!response.result
}, },

View File

@ -5,25 +5,26 @@
* and run the @cosmwasm/ts-codegen generate command to regenerate this file. * and run the @cosmwasm/ts-codegen generate command to regenerate this file.
*/ */
import { CosmWasmClient, SigningCosmWasmClient, ExecuteResult } from '@cosmjs/cosmwasm-stargate' import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import { Coin, StdFee } from '@cosmjs/amino' import { Coin, StdFee } from '@cosmjs/amino'
import { import {
InstantiateMsg, ArrayOfPriceResponse,
ExecuteMsg, ArrayOfPriceSourceResponseForString,
OsmosisPriceSourceForString, ConfigResponse,
Decimal, Decimal,
Downtime, Downtime,
Identifier,
OwnerUpdate,
DowntimeDetector, DowntimeDetector,
ExecuteMsg,
GeometricTwap, GeometricTwap,
RedemptionRateForString, Identifier,
QueryMsg, InstantiateMsg,
ConfigResponse, OsmosisPriceSourceForString,
OwnerUpdate,
PriceResponse, PriceResponse,
PriceSourceResponseForString, PriceSourceResponseForString,
ArrayOfPriceSourceResponseForString, QueryMsg,
ArrayOfPriceResponse, RedemptionRateForString,
} from './MarsOracleOsmosis.types' } from './MarsOracleOsmosis.types'
export interface MarsOracleOsmosisReadOnlyInterface { export interface MarsOracleOsmosisReadOnlyInterface {
contractAddress: string contractAddress: string

View File

@ -5,28 +5,29 @@
* and run the @cosmwasm/ts-codegen generate command to regenerate this file. * and run the @cosmwasm/ts-codegen generate command to regenerate this file.
*/ */
import { UseQueryOptions, useQuery, useMutation, UseMutationOptions } from '@tanstack/react-query' import { useMutation, UseMutationOptions, useQuery, UseQueryOptions } from '@tanstack/react-query'
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate' import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { StdFee, Coin } from '@cosmjs/amino' import { Coin, StdFee } from '@cosmjs/amino'
import { import {
InstantiateMsg, ArrayOfPriceResponse,
ExecuteMsg, ArrayOfPriceSourceResponseForString,
OsmosisPriceSourceForString, ConfigResponse,
Decimal, Decimal,
Downtime, Downtime,
Identifier,
OwnerUpdate,
DowntimeDetector, DowntimeDetector,
ExecuteMsg,
GeometricTwap, GeometricTwap,
RedemptionRateForString, Identifier,
QueryMsg, InstantiateMsg,
ConfigResponse, OsmosisPriceSourceForString,
OwnerUpdate,
PriceResponse, PriceResponse,
PriceSourceResponseForString, PriceSourceResponseForString,
ArrayOfPriceSourceResponseForString, QueryMsg,
ArrayOfPriceResponse, RedemptionRateForString,
} from './MarsOracleOsmosis.types' } from './MarsOracleOsmosis.types'
import { MarsOracleOsmosisQueryClient, MarsOracleOsmosisClient } from './MarsOracleOsmosis.client' import { MarsOracleOsmosisClient, MarsOracleOsmosisQueryClient } from './MarsOracleOsmosis.client'
export const marsOracleOsmosisQueryKeys = { export const marsOracleOsmosisQueryKeys = {
contract: [ contract: [
{ {

View File

@ -12,7 +12,12 @@ interface BroadcastSlice {
fee: StdFee fee: StdFee
funds?: Coin[] funds?: Coin[]
}) => Promise<BroadcastResult> }) => Promise<BroadcastResult>
borrow: (options: { fee: StdFee; accountId: string; coin: Coin }) => Promise<boolean> borrow: (options: {
fee: StdFee
accountId: string
coin: Coin
borrowToWallet: boolean
}) => Promise<boolean>
createAccount: (options: { fee: StdFee }) => Promise<string | null> createAccount: (options: { fee: StdFee }) => Promise<string | null>
deleteAccount: (options: { fee: StdFee; accountId: string }) => Promise<boolean> deleteAccount: (options: { fee: StdFee; accountId: string }) => Promise<boolean>
deposit: (options: { fee: StdFee; accountId: string; coin: Coin }) => Promise<boolean> deposit: (options: { fee: StdFee; accountId: string; coin: Coin }) => Promise<boolean>