Auto repay on trade (#631)
This commit is contained in:
parent
28f27c3882
commit
8e976a5d70
@ -32,6 +32,7 @@ export default function Size(props: Props) {
|
||||
|
||||
const color = getAmountChangeColor(type, amountChange)
|
||||
const className = classNames('text-xs text-right', color)
|
||||
const allowZero = !amountChange.isZero()
|
||||
|
||||
if (size >= 1)
|
||||
return (
|
||||
@ -44,11 +45,12 @@ export default function Size(props: Props) {
|
||||
)
|
||||
|
||||
const formattedAmount = formatAmountToPrecision(size, MAX_AMOUNT_DECIMALS)
|
||||
const lowAmount = formattedAmount === 0 ? MIN_AMOUNT : Math.max(formattedAmount, MIN_AMOUNT)
|
||||
const minimumAmount = allowZero ? 0 : MIN_AMOUNT
|
||||
const lowAmount = formattedAmount === 0 ? minimumAmount : Math.max(formattedAmount, MIN_AMOUNT)
|
||||
return (
|
||||
<FormattedNumber
|
||||
className={className}
|
||||
smallerThanThreshold={formattedAmount < MIN_AMOUNT}
|
||||
smallerThanThreshold={!allowZero && formattedAmount < MIN_AMOUNT}
|
||||
amount={lowAmount}
|
||||
options={{
|
||||
maxDecimals: MAX_AMOUNT_DECIMALS,
|
||||
|
@ -24,12 +24,17 @@ export const valueSortingFn = (a: Row<AccountBalanceRow>, b: Row<AccountBalanceR
|
||||
export default function Value(props: Props) {
|
||||
const { amountChange, type, value } = props
|
||||
const color = getAmountChangeColor(type, amountChange)
|
||||
const allowZero = !amountChange.isZero()
|
||||
const coin = new BNCoin({
|
||||
denom: ORACLE_DENOM,
|
||||
amount: value,
|
||||
})
|
||||
|
||||
return (
|
||||
<DisplayCurrency coin={coin} className={classNames('text-xs text-right', color)} showZero />
|
||||
<DisplayCurrency
|
||||
coin={coin}
|
||||
className={classNames('text-xs text-right', color)}
|
||||
showZero={!allowZero}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
import Switch from 'components/Switch'
|
||||
import Text from 'components/Text'
|
||||
import { Tooltip } from 'components/Tooltip'
|
||||
|
||||
interface Props {
|
||||
checked: boolean
|
||||
onChange: (value: boolean) => void
|
||||
buyAssetSymbol: string
|
||||
}
|
||||
|
||||
export default function AutoRepayToggle(props: Props) {
|
||||
return (
|
||||
<div className='flex flex-row justify-between flex-1 px-4 py-2 bg-white/5'>
|
||||
<div className='flex items-center gap-1'>
|
||||
<Text size='sm'>Auto Repay Debt</Text>
|
||||
<Tooltip
|
||||
type='info'
|
||||
content={
|
||||
<Text size='sm'>
|
||||
Use the bought {props.buyAssetSymbol} directly to repay your {props.buyAssetSymbol}{' '}
|
||||
debt.
|
||||
</Text>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex flex-row'>
|
||||
<Switch {...props} name='repay' />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -7,6 +7,7 @@ import DepositCapMessage from 'components/DepositCapMessage'
|
||||
import Divider from 'components/Divider'
|
||||
import RangeInput from 'components/RangeInput'
|
||||
import AssetAmountInput from 'components/Trade/TradeModule/SwapForm/AssetAmountInput'
|
||||
import AutoRepayToggle from 'components/Trade/TradeModule/SwapForm/AutoRepayToggle'
|
||||
import MarginToggle from 'components/Trade/TradeModule/SwapForm/MarginToggle'
|
||||
import OrderTypeSelector from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector'
|
||||
import { AvailableOrderType } from 'components/Trade/TradeModule/SwapForm/OrderTypeSelector/types'
|
||||
@ -38,6 +39,7 @@ interface Props {
|
||||
export default function SwapForm(props: Props) {
|
||||
const { buyAsset, sellAsset } = props
|
||||
const useMargin = useStore((s) => s.useMargin)
|
||||
const useAutoRepay = useStore((s) => s.useAutoRepay)
|
||||
const account = useCurrentAccount()
|
||||
const swap = useStore((s) => s.swap)
|
||||
const [slippage] = useLocalStorage(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
|
||||
@ -46,7 +48,9 @@ export default function SwapForm(props: Props) {
|
||||
const { data: marketAssets } = useMarketAssets()
|
||||
const { data: route, isLoading: isRouteLoading } = useSwapRoute(sellAsset.denom, buyAsset.denom)
|
||||
const isBorrowEnabled = !!marketAssets.find(byDenom(sellAsset.denom))?.borrowEnabled
|
||||
const isRepayable = account?.debts.find(byDenom(buyAsset.denom))
|
||||
const [isMarginChecked, setMarginChecked] = useToggle(isBorrowEnabled ? useMargin : false)
|
||||
const [isAutoRepayChecked, setAutoRepayChecked] = useToggle(isRepayable ? useAutoRepay : false)
|
||||
const [buyAssetAmount, setBuyAssetAmount] = useState(BN_ZERO)
|
||||
const [sellAssetAmount, setSellAssetAmount] = useState(BN_ZERO)
|
||||
const [maxBuyableAmountEstimation, setMaxBuyableAmountEstimation] = useState(BN_ZERO)
|
||||
@ -157,6 +161,7 @@ export default function SwapForm(props: Props) {
|
||||
denomOut: buyAsset.denom,
|
||||
slippage,
|
||||
isMax: sellAssetAmount.isEqualTo(maxSellAmount),
|
||||
repay: isAutoRepayChecked,
|
||||
})
|
||||
}, [
|
||||
removedLends,
|
||||
@ -173,9 +178,15 @@ export default function SwapForm(props: Props) {
|
||||
const debouncedUpdateAccount = useMemo(
|
||||
() =>
|
||||
debounce((removeCoin: BNCoin, addCoin: BNCoin, debtCoin: BNCoin) => {
|
||||
simulateTrade(removeCoin, addCoin, debtCoin, isAutoLendEnabled ? 'lend' : 'deposit')
|
||||
simulateTrade(
|
||||
removeCoin,
|
||||
addCoin,
|
||||
debtCoin,
|
||||
isAutoLendEnabled ? 'lend' : 'deposit',
|
||||
isAutoRepayChecked,
|
||||
)
|
||||
}, 100),
|
||||
[simulateTrade, isAutoLendEnabled],
|
||||
[simulateTrade, isAutoLendEnabled, isAutoRepayChecked],
|
||||
)
|
||||
|
||||
const handleMarginToggleChange = useCallback(
|
||||
@ -185,6 +196,13 @@ export default function SwapForm(props: Props) {
|
||||
},
|
||||
[isBorrowEnabled, setMarginChecked],
|
||||
)
|
||||
const handleAutoRepayToggleChange = useCallback(
|
||||
(isAutoRepay: boolean) => {
|
||||
useStore.setState({ useAutoRepay: isAutoRepay })
|
||||
setAutoRepayChecked(isAutoRepay)
|
||||
},
|
||||
[setAutoRepayChecked],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setBuyAssetAmount(BN_ZERO)
|
||||
@ -195,6 +213,7 @@ export default function SwapForm(props: Props) {
|
||||
BNCoin.fromDenomAndBigNumber(sellAsset.denom, BN_ZERO),
|
||||
BNCoin.fromDenomAndBigNumber(sellAsset.denom, BN_ZERO),
|
||||
isAutoLendEnabled ? 'lend' : 'deposit',
|
||||
isAutoRepayChecked,
|
||||
)
|
||||
}, [
|
||||
isBorrowEnabled,
|
||||
@ -202,6 +221,7 @@ export default function SwapForm(props: Props) {
|
||||
buyAsset.denom,
|
||||
sellAsset.denom,
|
||||
isAutoLendEnabled,
|
||||
isAutoRepayChecked,
|
||||
simulateTrade,
|
||||
setMarginChecked,
|
||||
])
|
||||
@ -286,6 +306,15 @@ export default function SwapForm(props: Props) {
|
||||
borrowAssetSymbol={sellAsset.symbol}
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
{isRepayable && (
|
||||
<AutoRepayToggle
|
||||
checked={isAutoRepayChecked}
|
||||
onChange={handleAutoRepayToggleChange}
|
||||
buyAssetSymbol={buyAsset.symbol}
|
||||
/>
|
||||
)}
|
||||
<Divider />
|
||||
<OrderTypeSelector selected={selectedOrderType} onChange={setSelectedOrderType} />
|
||||
<div className='flex flex-col gap-6 px-3 mt-6'>
|
||||
<AssetAmountInput
|
||||
@ -318,7 +347,6 @@ export default function SwapForm(props: Props) {
|
||||
asset={borrowAsset}
|
||||
/>
|
||||
)}
|
||||
|
||||
<AssetAmountInput
|
||||
label='Sell'
|
||||
max={maxSellAmount}
|
||||
|
@ -135,7 +135,13 @@ export function useUpdatedAccount(account?: Account) {
|
||||
)
|
||||
|
||||
const simulateTrade = useCallback(
|
||||
(removeCoin: BNCoin, addCoin: BNCoin, debtCoin: BNCoin, target: 'deposit' | 'lend') => {
|
||||
(
|
||||
removeCoin: BNCoin,
|
||||
addCoin: BNCoin,
|
||||
debtCoin: BNCoin,
|
||||
target: 'deposit' | 'lend',
|
||||
repay: boolean,
|
||||
) => {
|
||||
removeDeposits([])
|
||||
removeLends([])
|
||||
addDebts([])
|
||||
@ -143,13 +149,29 @@ export function useUpdatedAccount(account?: Account) {
|
||||
addLends([])
|
||||
|
||||
const { deposit, lend } = getDepositAndLendCoinsToSpend(removeCoin, account)
|
||||
const currentDebtCoin = account?.debts.find(byDenom(addCoin.denom))
|
||||
let usedAmountForDebt = BN_ZERO
|
||||
|
||||
if (!deposit.amount.isZero()) removeDeposits([deposit])
|
||||
if (!lend.amount.isZero()) removeLends([lend])
|
||||
|
||||
if (target === 'deposit') addDeposits([addCoin])
|
||||
if (target === 'lend') addLends([addCoin])
|
||||
if (repay && currentDebtCoin) {
|
||||
if (currentDebtCoin.amount.isGreaterThanOrEqualTo(addCoin.amount)) {
|
||||
removeDebts([addCoin])
|
||||
usedAmountForDebt = addCoin.amount
|
||||
} else {
|
||||
removeDebts([currentDebtCoin])
|
||||
usedAmountForDebt = currentDebtCoin.amount
|
||||
}
|
||||
}
|
||||
|
||||
const remainingAddCoin = BNCoin.fromDenomAndBigNumber(
|
||||
addCoin.denom,
|
||||
addCoin.amount.minus(usedAmountForDebt),
|
||||
)
|
||||
|
||||
if (target === 'deposit') addDeposits(repay ? [remainingAddCoin] : [addCoin])
|
||||
if (target === 'lend') addLends(repay ? [remainingAddCoin] : [addCoin])
|
||||
if (debtCoin.amount.isGreaterThan(BN_ZERO)) addDebts([debtCoin])
|
||||
},
|
||||
[account, addDebts, addDeposits, addLends, removeDeposits, removeLends],
|
||||
|
@ -689,6 +689,7 @@ export default function createBroadcastSlice(
|
||||
denomOut: string
|
||||
slippage: number
|
||||
isMax?: boolean
|
||||
repay: boolean
|
||||
}) => {
|
||||
const msg: CreditManagerExecuteMsg = {
|
||||
update_credit_account: {
|
||||
@ -703,6 +704,17 @@ export default function createBroadcastSlice(
|
||||
slippage: options.slippage.toString(),
|
||||
},
|
||||
},
|
||||
...(options.repay
|
||||
? [
|
||||
{
|
||||
repay: {
|
||||
coin: BNCoin.fromDenomAndBigNumber(options.denomOut, BN_ZERO).toActionCoin(
|
||||
true,
|
||||
),
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ export default function createCommonSlice(set: SetState<CommonSlice>, get: GetSt
|
||||
migrationBanner: true,
|
||||
tutorial: true,
|
||||
useMargin: true,
|
||||
useAutoRepay: true,
|
||||
isOracleStale: false,
|
||||
}
|
||||
}
|
||||
|
1
src/types/interfaces/store/broadcast.d.ts
vendored
1
src/types/interfaces/store/broadcast.d.ts
vendored
@ -126,6 +126,7 @@ interface BroadcastSlice {
|
||||
denomOut: string
|
||||
slippage: number
|
||||
isMax?: boolean
|
||||
repay: boolean
|
||||
}) => ExecutableTx
|
||||
toast: ToastResponse | ToastPending | null
|
||||
unlock: (options: {
|
||||
|
1
src/types/interfaces/store/common.d.ts
vendored
1
src/types/interfaces/store/common.d.ts
vendored
@ -12,6 +12,7 @@ interface CommonSlice {
|
||||
migrationBanner: boolean
|
||||
tutorial: boolean
|
||||
useMargin: boolean
|
||||
useAutoRepay: boolean
|
||||
isOracleStale: boolean
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user