Repay modal (#146)
* enable repay * update formatters * remove suffix from numberInput * implement repay
This commit is contained in:
parent
8e0bb97839
commit
a747a585af
@ -30,7 +30,10 @@ export default function AssetExpanded(props: AssetRowProps) {
|
||||
}
|
||||
|
||||
function repayHandler() {
|
||||
useStore.setState({ repayModal: true })
|
||||
if (!asset) return null
|
||||
useStore.setState({
|
||||
borrowModal: { asset: asset, marketData: props.row.original, isRepay: true },
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
@ -51,7 +54,7 @@ export default function AssetExpanded(props: AssetRowProps) {
|
||||
color='primary'
|
||||
text={isActive ? 'Borrow more' : 'Borrow'}
|
||||
/>
|
||||
{isActive && <Button color='primary' text='Repay' />}
|
||||
{isActive && <Button color='primary' text='Repay' onClick={repayHandler} />}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -24,6 +24,7 @@ export default function BorrowModal() {
|
||||
const [selectedAccount, setSelectedAccount] = useState(params.account)
|
||||
const modal = useStore((s) => s.borrowModal)
|
||||
const borrow = useStore((s) => s.borrow)
|
||||
const repay = useStore((s) => s.repay)
|
||||
const creditAccounts = useStore((s) => s.creditAccounts)
|
||||
|
||||
function onAccountSelect(accountId: string) {
|
||||
@ -32,13 +33,25 @@ export default function BorrowModal() {
|
||||
|
||||
function setOpen(isOpen: boolean) {
|
||||
useStore.setState({ borrowModal: null })
|
||||
setValue(0)
|
||||
setPercentage(0)
|
||||
}
|
||||
|
||||
function onBorrowClick() {
|
||||
function onConfirmClick() {
|
||||
if (!modal?.asset) return
|
||||
|
||||
const amount = new BigNumber(value).shiftedBy(modal.asset.decimals)
|
||||
|
||||
if (modal.isRepay) {
|
||||
repay({
|
||||
fee: hardcodedFee,
|
||||
accountId: selectedAccount,
|
||||
coin: { denom: modal.asset.denom, amount: amount.toString() },
|
||||
accountBalance: percentage === 100,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
borrow({
|
||||
fee: hardcodedFee,
|
||||
accountId: selectedAccount,
|
||||
@ -47,15 +60,22 @@ export default function BorrowModal() {
|
||||
}
|
||||
|
||||
const onSliderChange = useCallback(
|
||||
(percentage: number, liquidityAmount: number) =>
|
||||
setValue(new BigNumber(percentage).div(100).times(liquidityAmount).toNumber()),
|
||||
[],
|
||||
(percentage: number, maxAmount: number) => {
|
||||
const amount = new BigNumber(percentage).div(100).times(maxAmount).toNumber()
|
||||
|
||||
setValue(amount)
|
||||
setPercentage(percentage)
|
||||
},
|
||||
[modal?.asset.decimals],
|
||||
)
|
||||
|
||||
const onInputChange = useCallback((value: number, liquidityAmount: number) => {
|
||||
setValue(value)
|
||||
setPercentage(new BigNumber(value).div(liquidityAmount).times(100).toNumber())
|
||||
}, [])
|
||||
const onInputChange = useCallback(
|
||||
(value: number, maxAmount: number) => {
|
||||
setValue(value)
|
||||
setPercentage(new BigNumber(value).div(maxAmount).times(100).toNumber())
|
||||
},
|
||||
[modal?.asset.decimals],
|
||||
)
|
||||
|
||||
if (!modal) return null
|
||||
|
||||
@ -71,6 +91,15 @@ export default function BorrowModal() {
|
||||
decimals: 6,
|
||||
})
|
||||
|
||||
let debtAmount = 0
|
||||
|
||||
if ((modal.marketData as BorrowAssetActive)?.debt)
|
||||
debtAmount = Number((modal.marketData as BorrowAssetActive).debt)
|
||||
|
||||
const maxAmount = new BigNumber(modal.isRepay ? debtAmount : liquidityAmount)
|
||||
.shiftedBy(-modal.asset.decimals)
|
||||
.toNumber()
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={true}
|
||||
@ -78,7 +107,9 @@ export default function BorrowModal() {
|
||||
header={
|
||||
<span className='flex items-center gap-4 px-4'>
|
||||
<Image src={modal?.asset.logo} alt='token' width={24} height={24} />
|
||||
<Text>Borrow {modal.asset.symbol}</Text>
|
||||
<Text>
|
||||
{modal.isRepay ? 'Repay' : 'Borrow'} {modal.asset.symbol}
|
||||
</Text>
|
||||
</span>
|
||||
}
|
||||
headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b'
|
||||
@ -90,7 +121,10 @@ export default function BorrowModal() {
|
||||
sub={'Borrow rate'}
|
||||
/>
|
||||
<div className='h-100 w-[1px] bg-white/10'></div>
|
||||
<TitleAndSubCell title={'$0'} sub={'Borrowed'} />
|
||||
<TitleAndSubCell
|
||||
title={formatValue(debtAmount, { abbreviated: true, decimals: modal.asset.decimals })}
|
||||
sub={'Borrowed'}
|
||||
/>
|
||||
<div className='h-100 w-[1px] bg-white/10'></div>
|
||||
<TitleAndSubCell
|
||||
title={`${liquidityAmountString} (${liquidityValueString})`}
|
||||
@ -104,13 +138,13 @@ export default function BorrowModal() {
|
||||
>
|
||||
<TokenInput
|
||||
asset={modal.asset}
|
||||
onChange={(value) => onInputChange(value, liquidityAmount)}
|
||||
onChange={(value) => onInputChange(value, maxAmount)}
|
||||
value={value}
|
||||
max={liquidityAmount}
|
||||
max={maxAmount}
|
||||
/>
|
||||
<Slider value={percentage} onChange={(value) => onSliderChange(value, liquidityAmount)} />
|
||||
<Slider value={percentage} onChange={(value) => onSliderChange(value, maxAmount)} />
|
||||
<Divider />
|
||||
<Text size='lg'>Borrow to</Text>
|
||||
<Text size='lg'>{modal.isRepay ? 'Repay for' : 'Borrow to'}</Text>
|
||||
<select
|
||||
name='creditAccount'
|
||||
value={selectedAccount}
|
||||
@ -124,9 +158,9 @@ export default function BorrowModal() {
|
||||
))}
|
||||
</select>
|
||||
<Button
|
||||
onClick={onBorrowClick}
|
||||
onClick={onConfirmClick}
|
||||
className='w-full'
|
||||
text='Borrow'
|
||||
text={modal.isRepay ? 'Repay' : 'Borrow'}
|
||||
rightIcon={<ArrowRight />}
|
||||
/>
|
||||
</Card>
|
||||
|
@ -11,7 +11,6 @@ interface Props {
|
||||
max?: number
|
||||
maxLength?: number
|
||||
allowNegative?: boolean
|
||||
suffix?: string
|
||||
style?: {}
|
||||
onChange: (value: number) => void
|
||||
onBlur?: () => void
|
||||
@ -73,9 +72,6 @@ export default function NumberInput(props: Props) {
|
||||
}, [inputValue, inputRef])
|
||||
|
||||
const onInputChange = (value: string) => {
|
||||
if (props.suffix) {
|
||||
value = value.replace(props.suffix, '')
|
||||
}
|
||||
const numberCount = value.match(/[0-9]/g)?.length || 0
|
||||
const decimals = value.split('.')[1]?.length || 0
|
||||
const lastChar = value.charAt(value.length - 1)
|
||||
@ -136,7 +132,7 @@ export default function NumberInput(props: Props) {
|
||||
<input
|
||||
ref={inputRef}
|
||||
type='text'
|
||||
value={`${inputValue.formatted}${props.suffix ? props.suffix : ''}`}
|
||||
value={inputValue.formatted === '0' ? '' : inputValue.formatted}
|
||||
onFocus={onInputFocus}
|
||||
onChange={(e) => onInputChange(e.target.value)}
|
||||
onBlur={props.onBlur}
|
||||
|
@ -7,8 +7,7 @@ import { ENV, ENV_MISSING_MESSAGE } from 'constants/env'
|
||||
import { Store } from 'store'
|
||||
import { getMarketAssets } from 'utils/assets'
|
||||
import { getSingleValueFromBroadcastResult } from 'utils/broadcast'
|
||||
import { convertFromGwei } from 'utils/formatters'
|
||||
import { getTokenSymbol } from 'utils/tokens'
|
||||
import { formatAmountWithSymbol } from 'utils/formatters'
|
||||
|
||||
interface BroadcastResult {
|
||||
result?: TxBroadcastResult
|
||||
@ -26,6 +25,12 @@ export interface BroadcastSlice {
|
||||
createCreditAccount: (options: { fee: StdFee }) => Promise<string | null>
|
||||
deleteCreditAccount: (options: { fee: StdFee; accountId: string }) => Promise<boolean>
|
||||
deposit: (options: { fee: StdFee; accountId: string; coin: Coin }) => Promise<boolean>
|
||||
repay: (options: {
|
||||
fee: StdFee
|
||||
accountId: string
|
||||
coin: Coin
|
||||
accountBalance?: boolean
|
||||
}) => Promise<boolean>
|
||||
}
|
||||
|
||||
export function createBroadcastSlice(set: SetState<Store>, get: GetState<Store>): BroadcastSlice {
|
||||
@ -45,7 +50,9 @@ export function createBroadcastSlice(set: SetState<Store>, get: GetState<Store>)
|
||||
if (response.result?.response.code === 0) {
|
||||
set({
|
||||
toast: {
|
||||
message: `Borrowed ${options.coin.amount} ${options.coin.denom} to Account ${options.accountId}`,
|
||||
message: `Borrowed ${formatAmountWithSymbol(options.coin)} to Account ${
|
||||
options.accountId
|
||||
}`,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
@ -119,7 +126,9 @@ export function createBroadcastSlice(set: SetState<Store>, get: GetState<Store>)
|
||||
if (response.result) {
|
||||
set({
|
||||
toast: {
|
||||
message: `Deposited ${options.coin} to Account ${options.accountId}`,
|
||||
message: `Deposited ${formatAmountWithSymbol(options.coin)} to Account ${
|
||||
options.accountId
|
||||
}`,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
@ -171,5 +180,44 @@ export function createBroadcastSlice(set: SetState<Store>, get: GetState<Store>)
|
||||
return { result: undefined, error }
|
||||
}
|
||||
},
|
||||
repay: async (options: {
|
||||
fee: StdFee
|
||||
accountId: string
|
||||
coin: Coin
|
||||
accountBalance?: boolean
|
||||
}) => {
|
||||
const msg = {
|
||||
update_credit_account: {
|
||||
account_id: options.accountId,
|
||||
actions: [
|
||||
{
|
||||
repay: {
|
||||
denom: options.coin.denom,
|
||||
amount: options.accountBalance ? 'account_balance' : { exact: options.coin.amount },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
const response = await get().executeMsg({ msg, fee: options.fee, funds: [] })
|
||||
if (response.result?.response.code === 0) {
|
||||
set({
|
||||
toast: {
|
||||
message: `Repayed ${formatAmountWithSymbol(options.coin)} to Account ${
|
||||
options.accountId
|
||||
}`,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
set({
|
||||
toast: {
|
||||
message: response.error ?? `Transaction failed: ${response.error}`,
|
||||
isError: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
return !!response.result
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ export interface CommonSlice {
|
||||
borrowModal: {
|
||||
asset: Asset
|
||||
marketData: BorrowAsset | BorrowAssetActive
|
||||
isRepay?: boolean
|
||||
} | null
|
||||
client?: WalletClient
|
||||
clients: {
|
||||
@ -30,7 +31,6 @@ export interface CommonSlice {
|
||||
fundAccountModal: boolean
|
||||
isOpen: boolean
|
||||
prices: Coin[]
|
||||
repayModal: boolean
|
||||
selectedAccount: string | null
|
||||
signingClient?: SigningCosmWasmClient
|
||||
status: WalletConnectionStatus
|
||||
|
@ -1,6 +1,7 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
|
||||
import { getTokenDecimals } from 'utils/tokens'
|
||||
import { getMarketAssets } from './assets'
|
||||
import { Coin } from '@cosmjs/stargate'
|
||||
|
||||
export function truncate(text = '', [h, t]: [number, number] = [6, 6]): string {
|
||||
const head = text.slice(0, h)
|
||||
@ -10,18 +11,6 @@ export function truncate(text = '', [h, t]: [number, number] = [6, 6]): string {
|
||||
return text.length > h + t ? [head, tail].join('...') : text
|
||||
}
|
||||
|
||||
export const convertFromGwei = (amount: string | number, denom: string, marketAssets: Asset[]) => {
|
||||
return BigNumber(amount)
|
||||
.div(10 ** getTokenDecimals(denom, marketAssets))
|
||||
.toNumber()
|
||||
}
|
||||
|
||||
export const convertToGwei = (amount: string | number, denom: string, marketAssets: Asset[]) => {
|
||||
return BigNumber(amount)
|
||||
.times(10 ** getTokenDecimals(denom, marketAssets))
|
||||
.toNumber()
|
||||
}
|
||||
|
||||
export interface FormatOptions {
|
||||
decimals?: number
|
||||
minDecimals?: number
|
||||
@ -135,3 +124,16 @@ export function formatPercent(percent: number | string) {
|
||||
suffix: '%',
|
||||
})
|
||||
}
|
||||
|
||||
export function formatAmountWithSymbol(coin: Coin) {
|
||||
const marketAssets = getMarketAssets()
|
||||
|
||||
const asset = marketAssets.find((asset) => asset.denom === coin.denom)
|
||||
|
||||
return formatValue(coin.amount, {
|
||||
decimals: asset?.decimals,
|
||||
suffix: ` ${asset?.symbol}`,
|
||||
abbreviated: true,
|
||||
rounded: true,
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user