Add / update slippage for Vault Messages (#469)
* ✨replaced minLpToReceive with slippage * ✨Re-lend assets after entering vault * 🐛Fix vault unlock bug
This commit is contained in:
parent
141b522c56
commit
2f9f0f4b02
@ -1,25 +0,0 @@
|
||||
import { getCreditManagerQueryClient } from 'api/cosmwasm-client'
|
||||
import { ENV } from 'constants/env'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
export default async function getVaultConfigs(
|
||||
coins: Coin[],
|
||||
lpDenom: string,
|
||||
slippage: number,
|
||||
): Promise<BigNumber> {
|
||||
if (!ENV.ADDRESS_CREDIT_MANAGER) return BN(Infinity)
|
||||
|
||||
const creditManagerQueryClient = await getCreditManagerQueryClient()
|
||||
try {
|
||||
return BN(
|
||||
await creditManagerQueryClient.estimateProvideLiquidity({
|
||||
coinsIn: coins,
|
||||
lpTokenOut: lpDenom,
|
||||
}),
|
||||
)
|
||||
.multipliedBy(1 - slippage)
|
||||
.integerValue()
|
||||
} catch (ex) {
|
||||
throw ex
|
||||
}
|
||||
}
|
@ -6,6 +6,9 @@ import { useParams } from 'react-router-dom'
|
||||
import Button from 'components/Button'
|
||||
import { AccountArrowDown, LockLocked, LockUnlocked, Plus } from 'components/Icons'
|
||||
import { Tooltip } from 'components/Tooltip'
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { SLIPPAGE_KEY } from 'constants/localStore'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import useStore from 'store'
|
||||
import { VaultStatus } from 'types/enums/vault'
|
||||
|
||||
@ -19,6 +22,7 @@ export default function VaultExpanded(props: Props) {
|
||||
const { accountId } = useParams()
|
||||
const [isConfirming, setIsConfirming] = useState(false)
|
||||
const withdrawFromVaults = useStore((s) => s.withdrawFromVaults)
|
||||
const [slippage] = useLocalStorage<number>(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
|
||||
|
||||
function depositMoreHandler() {
|
||||
useStore.setState({
|
||||
@ -42,6 +46,7 @@ export default function VaultExpanded(props: Props) {
|
||||
await withdrawFromVaults({
|
||||
accountId: accountId,
|
||||
vaults,
|
||||
slippage,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,9 @@ import { useParams } from 'react-router-dom'
|
||||
import Button from 'components/Button'
|
||||
import { ChevronRight } from 'components/Icons'
|
||||
import NotificationBanner from 'components/NotificationBanner'
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { SLIPPAGE_KEY } from 'constants/localStore'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import useStore from 'store'
|
||||
|
||||
interface Props {
|
||||
@ -14,6 +17,7 @@ export default function VaultUnlockBanner(props: Props) {
|
||||
const { accountId } = useParams()
|
||||
const [isConfirming, setIsConfirming] = useState(false)
|
||||
const withdrawFromVaults = useStore((s) => s.withdrawFromVaults)
|
||||
const [slippage] = useLocalStorage<number>(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
|
||||
|
||||
async function handleWithdraw() {
|
||||
if (!accountId) return
|
||||
@ -26,6 +30,7 @@ export default function VaultUnlockBanner(props: Props) {
|
||||
await withdrawFromVaults({
|
||||
accountId: accountId,
|
||||
vaults: props.vaults,
|
||||
slippage,
|
||||
})
|
||||
setIsConfirming(false)
|
||||
}
|
||||
|
@ -7,6 +7,9 @@ import VaultLogo from 'components/Earn/Farm/VaultLogo'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import Modal from 'components/Modal'
|
||||
import Text from 'components/Text'
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { SLIPPAGE_KEY } from 'constants/localStore'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
@ -18,6 +21,7 @@ export default function WithdrawFromVaultsModal() {
|
||||
const showTxLoader = useStore((s) => s.showTxLoader)
|
||||
const withdrawFromVaults = useStore((s) => s.withdrawFromVaults)
|
||||
const baseCurrency = useStore((s) => s.baseCurrency)
|
||||
const [slippage] = useLocalStorage<number>(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
|
||||
|
||||
function onClose() {
|
||||
useStore.setState({ withdrawFromVaultsModal: null })
|
||||
@ -28,6 +32,7 @@ export default function WithdrawFromVaultsModal() {
|
||||
await withdrawFromVaults({
|
||||
accountId: accountId,
|
||||
vaults: modal,
|
||||
slippage,
|
||||
})
|
||||
onClose()
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import debounce from 'debounce-promise'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import getMinLpToReceive from 'api/vaults/getMinLpToReceive'
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { SLIPPAGE_KEY } from 'constants/localStore'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useAutoLend from 'hooks/useAutoLend'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import usePrices from 'hooks/usePrices'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||
import { getLendEnabledAssets } from 'utils/assets'
|
||||
import { getDenomsFromBNCoins } from 'utils/tokens'
|
||||
import {
|
||||
getEnterVaultActions,
|
||||
getVaultDepositCoinsAndValue,
|
||||
@ -24,13 +24,11 @@ interface Props {
|
||||
|
||||
export default function useDepositVault(props: Props): {
|
||||
actions: Action[]
|
||||
minLpToReceive: string
|
||||
totalValue: BigNumber
|
||||
} {
|
||||
const [minLpToReceive, setMinLpToReceive] = useState<BigNumber>(BN_ZERO)
|
||||
const { data: prices } = usePrices()
|
||||
const [slippage] = useLocalStorage<number>(SLIPPAGE_KEY, DEFAULT_SETTINGS.slippage)
|
||||
|
||||
const { isAutoLendEnabledForCurrentAccount: isAutoLend } = useAutoLend()
|
||||
const borrowings: BNCoin[] = useMemo(
|
||||
() => props.borrowings.filter((borrowing) => borrowing.amount.gt(0)),
|
||||
[props.borrowings],
|
||||
@ -44,8 +42,6 @@ export default function useDepositVault(props: Props): {
|
||||
[props.reclaims],
|
||||
)
|
||||
|
||||
const debouncedGetMinLpToReceive = useMemo(() => debounce(getMinLpToReceive, 500), [])
|
||||
|
||||
const { primaryCoin, secondaryCoin, totalValue } = useMemo(
|
||||
() => getVaultDepositCoinsAndValue(props.vault, deposits, borrowings, reclaims, prices),
|
||||
[reclaims, deposits, borrowings, props.vault, prices],
|
||||
@ -68,41 +64,42 @@ export default function useDepositVault(props: Props): {
|
||||
[totalValue, prices, props.vault, deposits, borrowings, slippage],
|
||||
)
|
||||
|
||||
useMemo(async () => {
|
||||
if (primaryCoin.amount.isZero() || secondaryCoin.amount.isZero()) return
|
||||
|
||||
const lpAmount = await debouncedGetMinLpToReceive(
|
||||
[secondaryCoin.toCoin(), primaryCoin.toCoin()],
|
||||
props.vault.denoms.lp,
|
||||
slippage,
|
||||
)
|
||||
|
||||
if (!lpAmount || lpAmount.isEqualTo(minLpToReceive)) return
|
||||
setMinLpToReceive(lpAmount)
|
||||
}, [
|
||||
primaryCoin,
|
||||
secondaryCoin,
|
||||
props.vault.denoms.lp,
|
||||
debouncedGetMinLpToReceive,
|
||||
minLpToReceive,
|
||||
slippage,
|
||||
])
|
||||
|
||||
const enterVaultActions: Action[] = useMemo(() => {
|
||||
if (primaryCoin.amount.isZero() || secondaryCoin.amount.isZero() || minLpToReceive.isZero())
|
||||
return []
|
||||
if (primaryCoin.amount.isZero() || secondaryCoin.amount.isZero()) return []
|
||||
|
||||
return getEnterVaultActions(props.vault, primaryCoin, secondaryCoin, minLpToReceive)
|
||||
}, [props.vault, primaryCoin, secondaryCoin, minLpToReceive])
|
||||
return getEnterVaultActions(props.vault, primaryCoin, secondaryCoin, slippage)
|
||||
}, [props.vault, primaryCoin, secondaryCoin, slippage])
|
||||
|
||||
const actions = useMemo(
|
||||
() => [...reclaimActions, ...borrowActions, ...swapActions, ...enterVaultActions],
|
||||
[reclaimActions, borrowActions, swapActions, enterVaultActions],
|
||||
)
|
||||
const lendActions: Action[] = useMemo(() => {
|
||||
if (!isAutoLend) return []
|
||||
|
||||
const denoms = Array.from(
|
||||
new Set(getDenomsFromBNCoins([...props.reclaims, ...props.deposits, ...props.borrowings])),
|
||||
)
|
||||
const denomsForLend = getLendEnabledAssets()
|
||||
.filter((asset) => denoms.includes(asset.denom))
|
||||
.map((asset) => asset.denom)
|
||||
|
||||
return denomsForLend.map((denom) => ({
|
||||
lend: {
|
||||
denom,
|
||||
amount: 'account_balance',
|
||||
},
|
||||
}))
|
||||
}, [isAutoLend, props.borrowings, props.deposits, props.reclaims])
|
||||
|
||||
const actions = useMemo(() => {
|
||||
return [
|
||||
...reclaimActions,
|
||||
...borrowActions,
|
||||
...swapActions,
|
||||
...enterVaultActions,
|
||||
...lendActions,
|
||||
]
|
||||
}, [reclaimActions, borrowActions, swapActions, enterVaultActions, lendActions])
|
||||
|
||||
return {
|
||||
actions,
|
||||
minLpToReceive: minLpToReceive.toString(),
|
||||
totalValue,
|
||||
}
|
||||
}
|
||||
}
|
@ -349,16 +349,27 @@ export default function createBroadcastSlice(
|
||||
return !!response.result
|
||||
},
|
||||
|
||||
withdrawFromVaults: async (options: { accountId: string; vaults: DepositedVault[] }) => {
|
||||
withdrawFromVaults: async (options: {
|
||||
accountId: string
|
||||
vaults: DepositedVault[]
|
||||
slippage: number
|
||||
}) => {
|
||||
const actions: CreditManagerAction[] = []
|
||||
options.vaults.forEach((vault) => {
|
||||
if (vault.unlockId)
|
||||
if (vault.unlockId) {
|
||||
actions.push({
|
||||
exit_vault_unlocked: {
|
||||
id: vault.unlockId,
|
||||
vault: { address: vault.address },
|
||||
},
|
||||
})
|
||||
actions.push({
|
||||
withdraw_liquidity: {
|
||||
lp_token: { denom: vault.denoms.lp, amount: 'account_balance' },
|
||||
slippage: options.slippage.toString(),
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
const msg: CreditManagerExecuteMsg = {
|
||||
update_credit_account: {
|
||||
|
@ -10,6 +10,7 @@ import { StdFee } from '@cosmjs/amino'
|
||||
import {
|
||||
HealthContractBaseForString,
|
||||
IncentivesUnchecked,
|
||||
Decimal,
|
||||
Uint128,
|
||||
OracleBaseForString,
|
||||
ParamsBaseForString,
|
||||
@ -23,7 +24,6 @@ import {
|
||||
ActionAmount,
|
||||
LiquidateRequestForVaultBaseForString,
|
||||
VaultPositionType,
|
||||
Decimal,
|
||||
AccountNftBaseForString,
|
||||
OwnerUpdate,
|
||||
CallbackMsg,
|
||||
@ -57,6 +57,7 @@ import {
|
||||
VaultPositionResponseItem,
|
||||
ConfigResponse,
|
||||
OwnerResponse,
|
||||
RewardsCollector,
|
||||
ArrayOfCoin,
|
||||
Positions,
|
||||
DebtAmount,
|
||||
|
@ -11,6 +11,7 @@ import { toUtf8 } from '@cosmjs/encoding'
|
||||
import {
|
||||
HealthContractBaseForString,
|
||||
IncentivesUnchecked,
|
||||
Decimal,
|
||||
Uint128,
|
||||
OracleBaseForString,
|
||||
ParamsBaseForString,
|
||||
@ -24,7 +25,6 @@ import {
|
||||
ActionAmount,
|
||||
LiquidateRequestForVaultBaseForString,
|
||||
VaultPositionType,
|
||||
Decimal,
|
||||
AccountNftBaseForString,
|
||||
OwnerUpdate,
|
||||
CallbackMsg,
|
||||
@ -58,6 +58,7 @@ import {
|
||||
VaultPositionResponseItem,
|
||||
ConfigResponse,
|
||||
OwnerResponse,
|
||||
RewardsCollector,
|
||||
ArrayOfCoin,
|
||||
Positions,
|
||||
DebtAmount,
|
||||
|
@ -11,6 +11,7 @@ import { StdFee } from '@cosmjs/amino'
|
||||
import {
|
||||
HealthContractBaseForString,
|
||||
IncentivesUnchecked,
|
||||
Decimal,
|
||||
Uint128,
|
||||
OracleBaseForString,
|
||||
ParamsBaseForString,
|
||||
@ -24,7 +25,6 @@ import {
|
||||
ActionAmount,
|
||||
LiquidateRequestForVaultBaseForString,
|
||||
VaultPositionType,
|
||||
Decimal,
|
||||
AccountNftBaseForString,
|
||||
OwnerUpdate,
|
||||
CallbackMsg,
|
||||
@ -58,6 +58,7 @@ import {
|
||||
VaultPositionResponseItem,
|
||||
ConfigResponse,
|
||||
OwnerResponse,
|
||||
RewardsCollector,
|
||||
ArrayOfCoin,
|
||||
Positions,
|
||||
DebtAmount,
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
export type HealthContractBaseForString = string
|
||||
export type IncentivesUnchecked = string
|
||||
export type Decimal = string
|
||||
export type Uint128 = string
|
||||
export type OracleBaseForString = string
|
||||
export type ParamsBaseForString = string
|
||||
@ -16,6 +17,7 @@ export type ZapperBaseForString = string
|
||||
export interface InstantiateMsg {
|
||||
health_contract: HealthContractBaseForString
|
||||
incentives: IncentivesUnchecked
|
||||
max_slippage: Decimal
|
||||
max_unlocking_positions: Uint128
|
||||
oracle: OracleBaseForString
|
||||
owner: string
|
||||
@ -124,13 +126,13 @@ export type Action =
|
||||
provide_liquidity: {
|
||||
coins_in: ActionCoin[]
|
||||
lp_token_out: string
|
||||
minimum_receive: Uint128
|
||||
slippage: Decimal
|
||||
}
|
||||
}
|
||||
| {
|
||||
withdraw_liquidity: {
|
||||
lp_token: ActionCoin
|
||||
minimum_receive: Coin[]
|
||||
slippage: Decimal
|
||||
}
|
||||
}
|
||||
| {
|
||||
@ -155,7 +157,6 @@ export type LiquidateRequestForVaultBaseForString =
|
||||
}
|
||||
}
|
||||
export type VaultPositionType = 'u_n_l_o_c_k_e_d' | 'l_o_c_k_e_d' | 'u_n_l_o_c_k_i_n_g'
|
||||
export type Decimal = string
|
||||
export type AccountNftBaseForString = string
|
||||
export type OwnerUpdate =
|
||||
| {
|
||||
@ -297,14 +298,14 @@ export type CallbackMsg =
|
||||
account_id: string
|
||||
coins_in: ActionCoin[]
|
||||
lp_token_out: string
|
||||
minimum_receive: Uint128
|
||||
slippage: Decimal
|
||||
}
|
||||
}
|
||||
| {
|
||||
withdraw_liquidity: {
|
||||
account_id: string
|
||||
lp_token: ActionCoin
|
||||
minimum_receive: Coin[]
|
||||
slippage: Decimal
|
||||
}
|
||||
}
|
||||
| {
|
||||
@ -313,7 +314,7 @@ export type CallbackMsg =
|
||||
}
|
||||
}
|
||||
| {
|
||||
assert_account_reqs: {
|
||||
assert_hls_rules: {
|
||||
account_id: string
|
||||
}
|
||||
}
|
||||
@ -358,6 +359,7 @@ export interface ConfigUpdates {
|
||||
account_nft?: AccountNftBaseForString | null
|
||||
health_contract?: HealthContractBaseForString | null
|
||||
incentives?: IncentivesUnchecked | null
|
||||
max_slippage?: Decimal | null
|
||||
max_unlocking_positions?: Uint128 | null
|
||||
oracle?: OracleBaseForString | null
|
||||
red_bank?: RedBankUnchecked | null
|
||||
@ -366,6 +368,7 @@ export interface ConfigUpdates {
|
||||
zapper?: ZapperBaseForString | null
|
||||
}
|
||||
export interface NftConfigUpdates {
|
||||
credit_manager_contract_addr?: string | null
|
||||
health_contract_addr?: string | null
|
||||
max_value_for_burn?: Uint128 | null
|
||||
}
|
||||
@ -494,12 +497,13 @@ export interface ConfigResponse {
|
||||
account_nft?: string | null
|
||||
health_contract: string
|
||||
incentives: string
|
||||
max_slippage: Decimal
|
||||
max_unlocking_positions: Uint128
|
||||
oracle: string
|
||||
ownership: OwnerResponse
|
||||
params: string
|
||||
red_bank: string
|
||||
rewards_collector?: string | null
|
||||
rewards_collector?: RewardsCollector | null
|
||||
swapper: string
|
||||
zapper: string
|
||||
}
|
||||
@ -510,6 +514,10 @@ export interface OwnerResponse {
|
||||
owner?: string | null
|
||||
proposed?: string | null
|
||||
}
|
||||
export interface RewardsCollector {
|
||||
account_id: string
|
||||
address: string
|
||||
}
|
||||
export type ArrayOfCoin = Coin[]
|
||||
export interface Positions {
|
||||
account_id: string
|
||||
|
6
src/types/interfaces/store/broadcast.d.ts
vendored
6
src/types/interfaces/store/broadcast.d.ts
vendored
@ -98,7 +98,11 @@ interface BroadcastSlice {
|
||||
vault: DepositedVault
|
||||
amount: string
|
||||
}) => Promise<boolean>
|
||||
withdrawFromVaults: (options: { accountId: string; vaults: DepositedVault[] }) => Promise<boolean>
|
||||
withdrawFromVaults: (options: {
|
||||
accountId: string
|
||||
vaults: DepositedVault[]
|
||||
slippage: number
|
||||
}) => Promise<boolean>
|
||||
withdraw: (options: {
|
||||
accountId: string
|
||||
coins: Array<{ coin: BNCoin; isMax?: boolean }>
|
||||
|
@ -31,3 +31,7 @@ export function getAllAssets(): Asset[] {
|
||||
export function findCoinByDenom(denom: string, coins: BigNumberCoin[]) {
|
||||
return coins.find((coin) => coin.denom === denom)
|
||||
}
|
||||
|
||||
export function getLendEnabledAssets() {
|
||||
return ASSETS.filter((asset) => asset.isAutoLendEnabled)
|
||||
}
|
||||
|
@ -13,4 +13,4 @@ export const SECONDS_IN_A_YEAR = 31540000
|
||||
export const LTV_BUFFER = 0.01
|
||||
|
||||
export const DEPOSIT_CAP_BUFFER = 0.999
|
||||
export const VAULT_DEPOSIT_BUFFER = 0.9999
|
||||
export const VAULT_DEPOSIT_BUFFER = 0.9999
|
||||
|
@ -24,3 +24,7 @@ export function getTokenPrice(denom: string, prices: BNCoin[]): BigNumber {
|
||||
export function getDebtAmountWithInterest(debt: BigNumber, apr: number) {
|
||||
return debt.times(1 + apr / 365 / 24).integerValue()
|
||||
}
|
||||
|
||||
export function getDenomsFromBNCoins(coins: BNCoin[]) {
|
||||
return coins.map((coin) => coin.denom)
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ export function getEnterVaultActions(
|
||||
vault: Vault,
|
||||
primaryCoin: BNCoin,
|
||||
secondaryCoin: BNCoin,
|
||||
minLpToReceive: BigNumber,
|
||||
slippage: number,
|
||||
): Action[] {
|
||||
return [
|
||||
{
|
||||
@ -156,7 +156,7 @@ export function getEnterVaultActions(
|
||||
// Smart Contact demands that secondary coin is first
|
||||
coins_in: [secondaryCoin.toActionCoin(), primaryCoin.toActionCoin()],
|
||||
lp_token_out: vault.denoms.lp,
|
||||
minimum_receive: minLpToReceive.toString(),
|
||||
slippage: slippage.toString(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -204,4 +204,3 @@ export function getVaultDepositCoinsFromActions(actions: Action[]) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user