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