diff --git a/src/api/vaults/getMinLpToReceive.ts b/src/api/vaults/getMinLpToReceive.ts deleted file mode 100644 index 36a876a4..00000000 --- a/src/api/vaults/getMinLpToReceive.ts +++ /dev/null @@ -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 { - 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 - } -} diff --git a/src/components/Earn/Farm/VaultExpanded.tsx b/src/components/Earn/Farm/VaultExpanded.tsx index fb334e78..1c84424c 100644 --- a/src/components/Earn/Farm/VaultExpanded.tsx +++ b/src/components/Earn/Farm/VaultExpanded.tsx @@ -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(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, }) } diff --git a/src/components/Earn/Farm/VaultUnlockBanner.tsx b/src/components/Earn/Farm/VaultUnlockBanner.tsx index 85af3f3c..a183da98 100644 --- a/src/components/Earn/Farm/VaultUnlockBanner.tsx +++ b/src/components/Earn/Farm/VaultUnlockBanner.tsx @@ -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(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) } diff --git a/src/components/Modals/WithdrawFromVaultsModal.tsx b/src/components/Modals/WithdrawFromVaultsModal.tsx index f14bacf7..266108a3 100644 --- a/src/components/Modals/WithdrawFromVaultsModal.tsx +++ b/src/components/Modals/WithdrawFromVaultsModal.tsx @@ -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(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() } diff --git a/src/hooks/broadcast/useDepositVault.ts b/src/hooks/broadcast/useDepositVault.ts index 09fa8596..acb577cd 100644 --- a/src/hooks/broadcast/useDepositVault.ts +++ b/src/hooks/broadcast/useDepositVault.ts @@ -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(BN_ZERO) const { data: prices } = usePrices() const [slippage] = useLocalStorage(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, } -} +} \ No newline at end of file diff --git a/src/store/slices/broadcast.ts b/src/store/slices/broadcast.ts index e295db11..689f39e8 100644 --- a/src/store/slices/broadcast.ts +++ b/src/store/slices/broadcast.ts @@ -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: { diff --git a/src/types/generated/mars-credit-manager/MarsCreditManager.client.ts b/src/types/generated/mars-credit-manager/MarsCreditManager.client.ts index ec409412..ae7d52be 100644 --- a/src/types/generated/mars-credit-manager/MarsCreditManager.client.ts +++ b/src/types/generated/mars-credit-manager/MarsCreditManager.client.ts @@ -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, diff --git a/src/types/generated/mars-credit-manager/MarsCreditManager.message-composer.ts b/src/types/generated/mars-credit-manager/MarsCreditManager.message-composer.ts index eeb49b48..1321ce03 100644 --- a/src/types/generated/mars-credit-manager/MarsCreditManager.message-composer.ts +++ b/src/types/generated/mars-credit-manager/MarsCreditManager.message-composer.ts @@ -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, diff --git a/src/types/generated/mars-credit-manager/MarsCreditManager.react-query.ts b/src/types/generated/mars-credit-manager/MarsCreditManager.react-query.ts index bad5a542..81988f5c 100644 --- a/src/types/generated/mars-credit-manager/MarsCreditManager.react-query.ts +++ b/src/types/generated/mars-credit-manager/MarsCreditManager.react-query.ts @@ -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, diff --git a/src/types/generated/mars-credit-manager/MarsCreditManager.types.ts b/src/types/generated/mars-credit-manager/MarsCreditManager.types.ts index 8f2d0a9a..2aaba901 100644 --- a/src/types/generated/mars-credit-manager/MarsCreditManager.types.ts +++ b/src/types/generated/mars-credit-manager/MarsCreditManager.types.ts @@ -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 diff --git a/src/types/interfaces/store/broadcast.d.ts b/src/types/interfaces/store/broadcast.d.ts index 23009ce0..0d5ef3ae 100644 --- a/src/types/interfaces/store/broadcast.d.ts +++ b/src/types/interfaces/store/broadcast.d.ts @@ -98,7 +98,11 @@ interface BroadcastSlice { vault: DepositedVault amount: string }) => Promise - withdrawFromVaults: (options: { accountId: string; vaults: DepositedVault[] }) => Promise + withdrawFromVaults: (options: { + accountId: string + vaults: DepositedVault[] + slippage: number + }) => Promise withdraw: (options: { accountId: string coins: Array<{ coin: BNCoin; isMax?: boolean }> diff --git a/src/utils/assets.ts b/src/utils/assets.ts index 3db59754..0bb052c0 100644 --- a/src/utils/assets.ts +++ b/src/utils/assets.ts @@ -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) +} diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 18101443..dc5add87 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -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 \ No newline at end of file +export const VAULT_DEPOSIT_BUFFER = 0.9999 diff --git a/src/utils/tokens.ts b/src/utils/tokens.ts index 8d66d763..4277b932 100644 --- a/src/utils/tokens.ts +++ b/src/utils/tokens.ts @@ -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) +} diff --git a/src/utils/vaults.ts b/src/utils/vaults.ts index a85c57ee..b7541f1a 100644 --- a/src/utils/vaults.ts +++ b/src/utils/vaults.ts @@ -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[]) { }) }) } -