Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import DialogHealthLeverageWarning from '@/loan/components/PageLoanCreate/LoanFo
import type { FormStatus, FormValues, PageLoanCreateProps, StepKey } from '@/loan/components/PageLoanCreate/types'
import { StyledInpChip } from '@/loan/components/PageLoanManage/styles'
import type { FormEstGas } from '@/loan/components/PageLoanManage/types'
import { DEFAULT_FORM_EST_GAS, DEFAULT_HEALTH_MODE, hasDeleverage } from '@/loan/components/PageLoanManage/utils'
import { DEFAULT_FORM_EST_GAS, DEFAULT_HEALTH_MODE } from '@/loan/components/PageLoanManage/utils'
import { DEFAULT_WALLET_BALANCES } from '@/loan/constants'
import networks from '@/loan/networks'
import { DEFAULT_FORM_STATUS } from '@/loan/store/createLoanCollateralIncreaseSlice'
import useStore from '@/loan/store/useStore'
import { CollateralAlert, LlamaApi, Llamma } from '@/loan/types/loan.types'
import { curveProps } from '@/loan/utils/helpers'
import { hasV1Deleverage } from '@/loan/utils/leverage'
import { getStepStatus, getTokenName } from '@/loan/utils/utilsLoan'
import { getLoanManagePathname } from '@/loan/utils/utilsRouter'
import Accordion from '@ui/Accordion'
Expand Down Expand Up @@ -437,7 +438,7 @@ const LoanCreate = ({
<Box grid gridRowGap={2}>
<p>{t`You can leverage your collateral up to 9x. This has the effect of repeat trading crvUSD to collateral and depositing to maximize your collateral position. Essentially, all borrowed crvUSD is utilized to purchase more collateral.`}</p>
<p>{t`Be careful, if the collateral price dips, you would need to repay the entire amount to reclaim your initial position.`}</p>
{!hasDeleverage(llamma) && (
{!hasV1Deleverage(llamma) && (
<p>{t`WARNING: The corresponding deleverage button is also not yet available.`}</p>
)}
</Box>
Expand Down
4 changes: 2 additions & 2 deletions apps/main/src/loan/components/PageLoanCreate/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import { DetailPageStack } from '@/llamalend/widgets/DetailPageStack'
import ChartOhlcWrapper from '@/loan/components/ChartOhlcWrapper'
import { MarketInformationComp } from '@/loan/components/MarketInformationComp'
import LoanCreate from '@/loan/components/PageLoanCreate/index'
import { hasLeverage } from '@/loan/components/PageLoanCreate/utils'
import { useMintMarket } from '@/loan/entities/mint-markets'
import { useMarketDetails } from '@/loan/hooks/useMarketDetails'
import networks from '@/loan/networks'
import useStore from '@/loan/store/useStore'
import { type CollateralUrlParams, type LlamaApi, Llamma } from '@/loan/types/loan.types'
import { hasV1Leverage } from '@/loan/utils/leverage'
import { getTokenName } from '@/loan/utils/utilsLoan'
import {
getLoanCreatePathname,
Expand Down Expand Up @@ -136,7 +136,7 @@ const Page = () => {

// redirect if form is leverage but no leverage option
useEffect(() => {
if (market && rFormType === 'leverage' && !hasLeverage(market)) {
if (market && rFormType === 'leverage' && !hasV1Leverage(market)) {
push(getLoanCreatePathname(params, market.id))
}
}, [loaded, rFormType, market, push, params])
Expand Down
5 changes: 3 additions & 2 deletions apps/main/src/loan/components/PageLoanCreate/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import type { OnBorrowFormUpdate } from '@/llamalend/features/borrow/types'
import type { CreateLoanOptions } from '@/llamalend/mutations/create-loan.mutation'
import LoanFormCreate from '@/loan/components/PageLoanCreate/LoanFormCreate'
import type { FormType, FormValues, PageLoanCreateProps } from '@/loan/components/PageLoanCreate/types'
import { DEFAULT_FORM_VALUES, hasLeverage } from '@/loan/components/PageLoanCreate/utils'
import { DEFAULT_FORM_VALUES } from '@/loan/components/PageLoanCreate/utils'
import useCollateralAlert from '@/loan/hooks/useCollateralAlert'
import networks from '@/loan/networks'
import useStore from '@/loan/store/useStore'
import { LlamaApi, Llamma } from '@/loan/types/loan.types'
import { hasV1Leverage } from '@/loan/utils/leverage'
import { getLoanCreatePathname, getLoanManagePathname } from '@/loan/utils/utilsRouter'
import Stack from '@mui/material/Stack'
import { AppFormContentWrapper } from '@ui/AppForm'
Expand Down Expand Up @@ -68,7 +69,7 @@ const LoanCreate = ({
[{ value: 'create' as const, label: t`Borrow` }]
: [
{ value: 'create' as const, label: t`Create Loan` },
...(hasLeverage(llamma) ? [{ value: 'leverage' as const, label: t`Leverage` }] : []),
...(hasV1Leverage(llamma) ? [{ value: 'leverage' as const, label: t`Leverage` }] : []),
],
[llamma, isBorrowUnifiedForm],
)
Expand Down
5 changes: 0 additions & 5 deletions apps/main/src/loan/components/PageLoanCreate/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { FormDetailInfoLeverage, FormStatus, FormValues } from '@/loan/components/PageLoanCreate/types'
import { DEFAULT_FORM_STATUS as FORM_STATUS } from '@/loan/components/PageLoanManage/utils'
import { Llamma } from '@/loan/types/loan.types'

export const DEFAULT_DETAIL_INFO_LEVERAGE: FormDetailInfoLeverage = {
collateral: '',
Expand Down Expand Up @@ -30,7 +29,3 @@ export const DEFAULT_FORM_VALUES: FormValues = {
debtError: '',
n: null,
}

export function hasLeverage(llamma: Llamma | null) {
return !!llamma && llamma?.leverageZap !== '0x0000000000000000000000000000000000000000'
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,13 @@ import type { FormDetailInfo, FormStatus, FormValues } from '@/loan/components/P
import { DEFAULT_FORM_VALUES } from '@/loan/components/PageLoanManage/LoanDeleverage/utils'
import { StyledDetailInfoWrapper, StyledInpChip } from '@/loan/components/PageLoanManage/styles'
import type { PageLoanManageProps } from '@/loan/components/PageLoanManage/types'
import {
DEFAULT_DETAIL_INFO,
DEFAULT_FORM_EST_GAS,
DEFAULT_HEALTH_MODE,
hasDeleverage,
} from '@/loan/components/PageLoanManage/utils'
import { DEFAULT_DETAIL_INFO, DEFAULT_FORM_EST_GAS, DEFAULT_HEALTH_MODE } from '@/loan/components/PageLoanManage/utils'
import { useUserLoanDetails } from '@/loan/hooks/useUserLoanDetails'
import networks from '@/loan/networks'
import useStore from '@/loan/store/useStore'
import { LlamaApi, Llamma } from '@/loan/types/loan.types'
import { curveProps } from '@/loan/utils/helpers'
import { hasDeleverage } from '@/loan/utils/leverage'
import { getStepStatus, getTokenName } from '@/loan/utils/utilsLoan'
import { getCollateralListPathname } from '@/loan/utils/utilsRouter'
import AlertBox from '@ui/AlertBox'
Expand Down
2 changes: 1 addition & 1 deletion apps/main/src/loan/components/PageLoanManage/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ import ChartOhlcWrapper from '@/loan/components/ChartOhlcWrapper'
import { MarketInformationComp } from '@/loan/components/MarketInformationComp'
import LoanMange from '@/loan/components/PageLoanManage/index'
import type { FormType } from '@/loan/components/PageLoanManage/types'
import { hasDeleverage } from '@/loan/components/PageLoanManage/utils'
import { useMintMarket } from '@/loan/entities/mint-markets'
import { useLoanPositionDetails } from '@/loan/hooks/useLoanPositionDetails'
import { useMarketDetails } from '@/loan/hooks/useMarketDetails'
import { useUserLoanDetails } from '@/loan/hooks/useUserLoanDetails'
import networks from '@/loan/networks'
import useStore from '@/loan/store/useStore'
import type { CollateralUrlParams } from '@/loan/types/loan.types'
import { hasDeleverage } from '@/loan/utils/leverage'
import { getLoanCreatePathname, parseCollateralParams, useChainId } from '@/loan/utils/utilsRouter'
import { isChain } from '@curvefi/prices-api'
import Stack from '@mui/material/Stack'
Expand Down
2 changes: 1 addition & 1 deletion apps/main/src/loan/components/PageLoanManage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type {
LoanFormType,
PageLoanManageProps,
} from '@/loan/components/PageLoanManage/types'
import { hasDeleverage } from '@/loan/components/PageLoanManage/utils'
import { hasDeleverage } from '@/loan/utils/leverage'
import { getLoanManagePathname } from '@/loan/utils/utilsRouter'
import Stack from '@mui/material/Stack'
import { AppFormContentWrapper } from '@ui/AppForm'
Expand Down
7 changes: 1 addition & 6 deletions apps/main/src/loan/components/PageLoanManage/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { zeroAddress } from 'viem'
import { FormDetailInfo, FormEstGas, FormStatus } from '@/loan/components/PageLoanManage/types'
import { Llamma, HealthMode, UserWalletBalances } from '@/loan/types/loan.types'
import { HealthMode, UserWalletBalances } from '@/loan/types/loan.types'

export const DEFAULT_HEALTH_MODE: HealthMode = {
percent: '',
Expand Down Expand Up @@ -36,7 +35,3 @@ export const DEFAULT_USER_WALLET_BALANCES: UserWalletBalances = {
stablecoin: '0',
error: '',
}

export function hasDeleverage(llamma: Llamma | null) {
return !!llamma && llamma?.deleverageZap !== zeroAddress
}
8 changes: 8 additions & 0 deletions apps/main/src/loan/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,11 @@ export const DEFAULT_WALLET_BALANCES: UserWalletBalances = {
collateral: '0',
error: '',
}

export enum RouteAggregator {
Odos = 'odos',
}

export const ROUTE_AGGREGATOR_LABELS: Record<RouteAggregator, string> = {
[RouteAggregator.Odos]: 'Odos',
}
171 changes: 141 additions & 30 deletions apps/main/src/loan/lib/apiCrvusd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import networks from '@/loan/networks'
import type { LiqRange, MaxRecvLeverage, Provider } from '@/loan/store/types'
import { ChainId, LlamaApi, Llamma, UserLoanDetails } from '@/loan/types/loan.types'
import { fulfilledValue, getErrorMessage, log } from '@/loan/utils/helpers'
import { hasV2Leverage } from '@/loan/utils/leverage'
import {
getChartBandBalancesData,
getIsUserCloseToLiquidation,
Expand All @@ -14,6 +15,8 @@ import {
} from '@/loan/utils/utilsCurvejs'
import type { TGas } from '@curvefi/llamalend-api/lib/interfaces'
import { waitForTransaction, waitForTransactions } from '@ui-kit/lib/ethers'
import { ROUTE_AGGREGATOR_LABELS, RouteAggregator } from '../constants'
import { getLeverageV2RepayArgs, isHigherThanMaxSlippage } from '../utils/utilsLoan'

export const network = {
1: {
Expand Down Expand Up @@ -953,9 +956,26 @@ const loanDeleverage = {
estGas: async (activeKey: string, llamma: Llamma, collateral: string, maxSlippage: string) => {
log('estGas', llamma.collateralSymbol, collateral, maxSlippage)
const resp = { activeKey, estimatedGas: initialGas, error: '' }
const slippage = Number(maxSlippage) || 0

try {
resp.estimatedGas = await llamma.deleverage.estimateGas.repay(collateral, +maxSlippage)
if (hasV2Leverage(llamma)) {
const repayArgs = getLeverageV2RepayArgs(collateral)
await llamma.leverageV2.repayExpectedBorrowed(
repayArgs.stateCollateral,
repayArgs.userCollateral,
repayArgs.userBorrowed,
slippage,
)
resp.estimatedGas = await llamma.leverageV2.estimateGas.repay(
repayArgs.stateCollateral,
repayArgs.userCollateral,
repayArgs.userBorrowed,
slippage,
)
} else {
resp.estimatedGas = await llamma.deleverage.estimateGas.repay(collateral, slippage)
}
return resp
} catch (error) {
console.error(error)
Expand Down Expand Up @@ -988,36 +1008,110 @@ const loanDeleverage = {
}

try {
// check if deleverage is available
const deleverageCollateral = +userState.collateral > 0 ? userState.collateral : collateral
if (+deleverageCollateral > 0) {
resp.isAvailable = await llamma.deleverage.isAvailable(deleverageCollateral)
}
const slippage = Number(maxSlippage) || 0
if (hasV2Leverage(llamma)) {
const repayArgs = getLeverageV2RepayArgs(collateral)
const availabilityCollateral = +userState.collateral > 0 ? userState.collateral : repayArgs.stateCollateral
const availabilityArgs = getLeverageV2RepayArgs(availabilityCollateral)

if (+availabilityArgs.stateCollateral > 0) {
resp.isAvailable = await llamma.leverageV2.repayIsAvailable(
availabilityArgs.stateCollateral,
availabilityArgs.userCollateral,
availabilityArgs.userBorrowed,
address,
)
}

if (resp.isAvailable && +collateral > 0) {
resp.isFullRepayment = await llamma.deleverage.isFullRepayment(collateral)

const [{ stablecoins, routeIdx }, priceImpact] = await Promise.all([
llamma.deleverage.repayStablecoins(collateral),
llamma.deleverage.priceImpact(collateral),
])
resp.receiveStablecoin = stablecoins
resp.routeName = await llamma.deleverage.getRouteName(routeIdx)
resp.priceImpact = priceImpact
resp.isHighImpact = +priceImpact > 0 && +maxSlippage > 0 ? +priceImpact > +maxSlippage : false

if (!resp.isFullRepayment) {
const [healthFullResult, healthNotFullResult, bandsResult, pricesResult] = await Promise.allSettled([
llamma.deleverage.repayHealth(collateral, true, address),
llamma.deleverage.repayHealth(collateral, false, address),
llamma.deleverage.repayBands(collateral, address),
llamma.deleverage.repayPrices(collateral, address),
])
if (resp.isAvailable && +repayArgs.stateCollateral > 0) {
const repayExpected = await llamma.leverageV2.repayExpectedBorrowed(
repayArgs.stateCollateral,
repayArgs.userCollateral,
repayArgs.userBorrowed,
slippage,
)
resp.receiveStablecoin = repayExpected.totalBorrowed
resp.isFullRepayment = await llamma.leverageV2.repayIsFull(
repayArgs.stateCollateral,
repayArgs.userCollateral,
repayArgs.userBorrowed,
address,
)

resp.priceImpact = await llamma.leverageV2.repayPriceImpact(
repayArgs.stateCollateral,
repayArgs.userCollateral,
)
resp.isHighImpact = isHigherThanMaxSlippage(resp.priceImpact, maxSlippage)
resp.routeName = ROUTE_AGGREGATOR_LABELS[RouteAggregator.Odos]

if (!resp.isFullRepayment) {
const [healthFullResult, healthNotFullResult, bandsResult, pricesResult] = await Promise.allSettled([
llamma.leverageV2.repayHealth(
repayArgs.stateCollateral,
repayArgs.userCollateral,
repayArgs.userBorrowed,
true,
address,
),
llamma.leverageV2.repayHealth(
repayArgs.stateCollateral,
repayArgs.userCollateral,
repayArgs.userBorrowed,
false,
address,
),
llamma.leverageV2.repayBands(
repayArgs.stateCollateral,
repayArgs.userCollateral,
repayArgs.userBorrowed,
address,
),
llamma.leverageV2.repayPrices(
repayArgs.stateCollateral,
repayArgs.userCollateral,
repayArgs.userBorrowed,
address,
),
])

resp.healthFull = fulfilledValue(healthFullResult) ?? ''
resp.healthNotFull = fulfilledValue(healthNotFullResult) ?? ''
resp.bands = reverseBands(fulfilledValue(bandsResult) ?? [0, 0])
resp.prices = fulfilledValue(pricesResult) ?? []
}
}
} else {
const deleverageCollateral = +userState.collateral > 0 ? userState.collateral : collateral
if (+deleverageCollateral > 0) {
resp.isAvailable = await llamma.deleverage.isAvailable(deleverageCollateral)
}

if (resp.isAvailable && +collateral > 0) {
resp.isFullRepayment = await llamma.deleverage.isFullRepayment(collateral)

resp.healthFull = fulfilledValue(healthFullResult) ?? ''
resp.healthNotFull = fulfilledValue(healthNotFullResult) ?? ''
resp.bands = reverseBands(fulfilledValue(bandsResult) ?? [0, 0])
resp.prices = fulfilledValue(pricesResult) ?? []
const [{ stablecoins, routeIdx }, priceImpact] = await Promise.all([
llamma.deleverage.repayStablecoins(collateral),
llamma.deleverage.priceImpact(collateral),
])
resp.receiveStablecoin = stablecoins
resp.routeName = await llamma.deleverage.getRouteName(routeIdx)
resp.priceImpact = priceImpact
resp.isHighImpact = isHigherThanMaxSlippage(priceImpact, maxSlippage)

if (!resp.isFullRepayment) {
const [healthFullResult, healthNotFullResult, bandsResult, pricesResult] = await Promise.allSettled([
llamma.deleverage.repayHealth(collateral, true, address),
llamma.deleverage.repayHealth(collateral, false, address),
llamma.deleverage.repayBands(collateral, address),
llamma.deleverage.repayPrices(collateral, address),
])

resp.healthFull = fulfilledValue(healthFullResult) ?? ''
resp.healthNotFull = fulfilledValue(healthNotFullResult) ?? ''
resp.bands = reverseBands(fulfilledValue(bandsResult) ?? [0, 0])
resp.prices = fulfilledValue(pricesResult) ?? []
}
}
}

Expand All @@ -1031,9 +1125,26 @@ const loanDeleverage = {
repay: async (activeKey: string, provider: Provider, llamma: Llamma, collateral: string, maxSlippage: string) => {
log('deleverageRepay', llamma.collateralSymbol, collateral, maxSlippage)
const resp = { activeKey, hash: '', error: '' }
const slippage = Number(maxSlippage) || 0

try {
resp.hash = await llamma.deleverage.repay(collateral, +maxSlippage)
if (hasV2Leverage(llamma)) {
const repayArgs = getLeverageV2RepayArgs(collateral)
await llamma.leverageV2.repayExpectedBorrowed(
repayArgs.stateCollateral,
repayArgs.userCollateral,
repayArgs.userBorrowed,
slippage,
)
resp.hash = await llamma.leverageV2.repay(
repayArgs.stateCollateral,
repayArgs.userCollateral,
repayArgs.userBorrowed,
slippage,
)
} else {
resp.hash = await llamma.deleverage.repay(collateral, slippage)
}
await waitForTransaction(resp.hash, provider)
return resp
} catch (error) {
Expand Down
16 changes: 16 additions & 0 deletions apps/main/src/loan/utils/leverage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { zeroAddress } from 'viem'
import type { Llamma } from '@/loan/types/loan.types'

/**
* Mint market version with id < 6 is using v1 (leverageZap)
* Mint market with id >= 6 is using v2 leverage
*/
export type MintMarketVersion = 'v1' | 'v2'

export const hasV1Leverage = (llamma: Llamma | null) => llamma?.leverageZap !== zeroAddress
export const hasV2Leverage = (llamma: Llamma | null) => !!llamma?.leverageV2.hasLeverage()
export const hasV1Deleverage = (llamma: Llamma | null) => llamma?.deleverageZap !== zeroAddress

export const hasLeverage = (llamma: Llamma | null) => hasV1Leverage(llamma) || hasV2Leverage(llamma)
// hasV2Leverage works for deleverage as well
export const hasDeleverage = (llamma: Llamma | null) => hasV1Deleverage(llamma) || hasV2Leverage(llamma)
Loading