mars-interface/src/hooks/queries/useEditPosition.tsx
Linkie Link ec0eee27ec
v1.2.1
2023-02-18 13:58:48 +01:00

240 lines
7.2 KiB
TypeScript

import BigNumber from 'bignumber.js'
import { SWAP_THRESHOLD } from 'constants/appConstants'
import { coinsToActionCoins, orderCoinsByDenom } from 'functions/fields'
import { useProvideLiquidity } from 'hooks/queries'
import { useMemo } from 'react'
import useStore from 'store'
import { Action, Coin } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { useEstimateFarmFee } from './useEstimateFarmFee'
interface Props {
accountId?: null | string
prevPosition?: Position
position: Position
vault: Vault
isReducingPosition: boolean
isLoading: boolean
}
export const useEditPosition = (props: Props) => {
const convertToBaseCurrency = useStore((s) => s.convertToBaseCurrency)
const convertValueToAmount = useStore((s) => s.convertValueToAmount)
const slippage = useStore((s) => s.slippage)
const [editPosition, coinsAfterSwap] = useMemo<[Position, Coin[]]>(() => {
const primaryAmount =
props.position.amounts.primary - (props.prevPosition?.amounts.primary || 0)
const secondaryAmount =
props.position.amounts.secondary - (props.prevPosition?.amounts.secondary || 0)
const borrowedAmount =
props.position.amounts.borrowed - (props.prevPosition?.amounts.borrowed || 0)
const editPosition = {
...props.position,
amounts: {
primary: primaryAmount,
secondary: secondaryAmount,
borrowed: borrowedAmount,
lp: {
amount: '0',
primary: 0,
secondary: 0,
},
vault: '0',
},
}
const primaryBaseAmount = convertToBaseCurrency({
denom: props.vault.denoms.primary,
amount: primaryAmount.toString(),
})
const secondaryBaseAmount = convertToBaseCurrency({
denom: props.vault.denoms.secondary,
amount: (secondaryAmount + borrowedAmount).toString(),
})
let primaryAfterSwap = primaryAmount
let secondaryAfterSwap = secondaryAmount + borrowedAmount
// If difference is larger than threshold, initiate a swap
const difference = primaryBaseAmount - secondaryBaseAmount
if (Math.abs(difference) > SWAP_THRESHOLD) {
const swapInput = difference > 0 ? 'primary' : 'secondary'
const swapTarget = difference < 0 ? 'primary' : 'secondary'
const inputAmount = Math.floor(Math.abs(difference) / 2)
const amount = Math.floor(
convertValueToAmount({
denom: props.vault.denoms[swapTarget],
amount: inputAmount.toString(),
}),
)
const primaryBaseChange = swapInput === 'primary' ? -inputAmount : amount
const secondaryBaseChange = swapInput === 'secondary' ? -inputAmount : amount
const primaryAmountChange = Math.floor(
convertValueToAmount({
denom: props.vault.denoms.primary,
amount: primaryBaseChange.toString(),
}),
)
const secondaryAmountChange = Math.floor(
convertValueToAmount({
denom: props.vault.denoms.secondary,
amount: secondaryBaseChange.toString(),
}),
)
primaryAfterSwap = primaryAfterSwap + primaryAmountChange
secondaryAfterSwap = secondaryAfterSwap + secondaryAmountChange
}
const coins: Coin[] = [
{
denom: props.vault.denoms.secondary,
amount: secondaryAfterSwap.toString(),
},
{
denom: props.vault.denoms.primary,
amount: primaryAfterSwap.toString(),
},
]
return [editPosition, coins]
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
props.position.amounts.primary,
props.position.amounts.secondary,
props.position.amounts.borrowed,
])
const { data: minLpToReceive } = useProvideLiquidity({
coins: coinsAfterSwap,
vault: props.vault,
})
const { actions, funds } = useMemo<{ actions: Action[]; funds: Coin[] }>(() => {
if (!minLpToReceive || props.isReducingPosition) return { actions: [], funds: [] }
const coins: { supply: Coin[]; borrow?: Coin } = { supply: [], borrow: undefined }
const primary = editPosition.amounts.primary && {
denom: props.vault.denoms.primary,
amount: editPosition.amounts.primary.toString(),
}
const secondary = editPosition.amounts.secondary && {
denom: props.vault.denoms.secondary,
amount: editPosition.amounts.secondary.toString(),
}
const borrow = editPosition.amounts.borrowed && {
denom: props.vault.denoms.secondary,
amount: editPosition.amounts.borrowed.toString(),
}
if (primary) coins.supply.push(primary)
if (secondary) coins.supply.push(secondary)
if (borrow) coins.borrow = borrow
const primaryBaseAmount = convertToBaseCurrency({
denom: props.vault.denoms.primary,
amount: String(editPosition.amounts.primary),
})
const secondaryBaseAmount = convertToBaseCurrency({
denom: props.vault.denoms.secondary,
amount: String(editPosition.amounts.secondary + editPosition.amounts.borrowed),
})
const swapMessage: Action[] = []
// If difference is larger than 10 ubase, initiate a swap
const difference = primaryBaseAmount - secondaryBaseAmount
if (Math.abs(difference) > SWAP_THRESHOLD) {
const inputDenom = difference > 0 ? props.vault.denoms.primary : props.vault.denoms.secondary
const outputDenom = difference < 0 ? props.vault.denoms.primary : props.vault.denoms.secondary
const amount = new BigNumber(difference).abs().div(2).toString()
const swapAmount = Math.ceil(convertValueToAmount({ denom: inputDenom, amount: amount }))
swapMessage.push({
swap_exact_in: {
coin_in: {
denom: inputDenom,
amount: {
exact: swapAmount.toString(),
},
},
denom_out: outputDenom,
slippage: slippage.toString(),
},
})
}
BigNumber.config({ EXPONENTIAL_AT: [-7, 30] })
const minimumReceive = new BigNumber(minLpToReceive)
.times(1 - slippage)
.dp(0)
.toString()
const actions: Action[] = [
...(coins.supply[0]
? [
{
deposit: coins.supply[0],
},
]
: []),
...(coins.supply[1] ? [{ deposit: coins.supply[1] }] : []),
...(coins.borrow ? [{ borrow: coins.borrow }] : []),
...swapMessage,
{
provide_liquidity: {
coins_in: coinsToActionCoins(coinsAfterSwap),
lp_token_out: props.vault?.denoms.lpToken || '',
minimum_receive: minimumReceive,
},
},
{
enter_vault: {
coin: {
denom: props.vault?.denoms.lpToken || '',
amount: 'account_balance',
},
vault: {
address: props.vault?.address || '',
},
},
},
]
return {
actions,
funds: orderCoinsByDenom(coins.supply) || [],
}
}, [
editPosition,
minLpToReceive,
props.vault,
coinsAfterSwap,
convertToBaseCurrency,
convertValueToAmount,
slippage,
props.isReducingPosition,
])
const {
data: fee,
isLoading,
error,
} = useEstimateFarmFee({
accountId: props.accountId,
actions: actions,
funds,
isCreate: false,
isLoading: props.isLoading,
})
return { editActions: actions, editFunds: funds, editFee: fee, isLoading, error }
}