From acc1f7fe2761d4b993ec30bf6f3c6b158686b872 Mon Sep 17 00:00:00 2001 From: Pearce Date: Mon, 15 Dec 2025 11:25:09 +0100 Subject: [PATCH 1/3] feat: create leveraged position for mint markets v2 for Stable and Legacy channels --- apps/main/src/loan/lib/apiCrvusd.ts | 96 +++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 5 deletions(-) diff --git a/apps/main/src/loan/lib/apiCrvusd.ts b/apps/main/src/loan/lib/apiCrvusd.ts index d12aff2abd..9f82e2dc23 100644 --- a/apps/main/src/loan/lib/apiCrvusd.ts +++ b/apps/main/src/loan/lib/apiCrvusd.ts @@ -331,8 +331,19 @@ const loanCreate = { ) => { log('loanEstGas', llamma.collateralSymbol, collateral, debt, n, maxSlippage) const resp = { activeKey, isApproved: false, estimatedGas: initialGas, error: '' } + const isV2Leverage = hasV2Leverage(llamma) + const userBorrowed = '0' try { + if (isLeverage && isV2Leverage) { + await llamma.leverageV2.createLoanExpectedCollateral(collateral, userBorrowed, debt, +maxSlippage) + resp.isApproved = await llamma.leverageV2.createLoanIsApproved(collateral, userBorrowed) + resp.estimatedGas = resp.isApproved + ? await llamma.leverageV2.estimateGas.createLoan(collateral, userBorrowed, debt, n, +maxSlippage) + : await llamma.leverageV2.estimateGas.createLoanApprove(collateral, userBorrowed) + return resp + } + resp.isApproved = isLeverage ? await llamma.leverage.createLoanIsApproved(collateral) : await llamma.createLoanIsApproved(collateral) @@ -390,8 +401,55 @@ const loanCreate = { maxSlippage: string, ) => { log('detailInfoLeverage', llamma.collateralSymbol, userCollateral, debt, n, maxSlippage) + const isV2Leverage = hasV2Leverage(llamma) + const userBorrowed = '0' try { + if (isV2Leverage) { + // Expected collateral must run first to populate swap data cache used by other calls + const expectedCollateralResult = await llamma.leverageV2.createLoanExpectedCollateral( + userCollateral, + userBorrowed, + debt, + +maxSlippage, + ) + const [ + maxRangeResult, + loanBandsResult, + loanPricesResult, + loanHealthFullResult, + loanHealthNotFullResult, + priceImpactResult, + ] = await Promise.allSettled([ + llamma.leverageV2.createLoanMaxRange(userCollateral, userBorrowed, debt), + llamma.leverageV2.createLoanBands(userCollateral, userBorrowed, debt, n), + llamma.leverageV2.createLoanPrices(userCollateral, userBorrowed, debt, n), + llamma.leverageV2.createLoanHealth(userCollateral, userBorrowed, debt, n, true), + llamma.leverageV2.createLoanHealth(userCollateral, userBorrowed, debt, n, false), + llamma.leverageV2.createLoanPriceImpact(userBorrowed, debt), + ]) + + const expectedCollateral = expectedCollateralResult + const priceImpact = fulfilledValue(priceImpactResult) ?? '' + + return { + activeKey, + resp: { + collateral: expectedCollateral?.totalCollateral ?? '', + leverage: expectedCollateral?.leverage ?? '', + routeName: '', + maxRange: fulfilledValue(maxRangeResult) ?? null, + bands: reverseBands(fulfilledValue(loanBandsResult) ?? [0, 0]), + prices: fulfilledValue(loanPricesResult) ?? [], + healthFull: fulfilledValue(loanHealthFullResult) ?? '', + healthNotFull: fulfilledValue(loanHealthNotFullResult) ?? '', + priceImpact, + isHighImpact: +priceImpact > 0 && +maxSlippage > 0 ? +priceImpact > +maxSlippage : false, + error: '', + }, + } + } + const { collateral, leverage, routeIdx } = await llamma.leverage.createLoanCollateral(userCollateral, debt) const [ routeNameResult, @@ -459,21 +517,29 @@ const loanCreate = { const liqRangesList: LiqRange[] = [] const liqRangesListMapper: { [n: string]: LiqRange & { sliderIdx: number } } = {} let sliderIdx = 0 + const isV2Leverage = hasV2Leverage(llamma) + const userBorrowed = '0' const [maxRecvsResults, loanBandsResults, loanPricesResults] = await Promise.allSettled([ haveCollateral ? isLeverage - ? llamma.leverage.createLoanMaxRecvAllRanges(collateral) + ? isV2Leverage + ? llamma.leverageV2.createLoanMaxRecvAllRanges(collateral, userBorrowed) + : llamma.leverage.createLoanMaxRecvAllRanges(collateral) : llamma.createLoanMaxRecvAllRanges(collateral) : null, haveCollateral && haveDebt ? isLeverage - ? llamma.leverage.createLoanBandsAllRanges(collateral, debt) + ? isV2Leverage + ? llamma.leverageV2.createLoanBandsAllRanges(collateral, userBorrowed, debt) + : llamma.leverage.createLoanBandsAllRanges(collateral, debt) : llamma.createLoanBandsAllRanges(collateral, debt) : null, haveCollateral && haveDebt ? isLeverage - ? llamma.leverage.createLoanPricesAllRanges(collateral, debt) + ? isV2Leverage + ? llamma.leverageV2.createLoanPricesAllRanges(collateral, userBorrowed, debt) + : llamma.leverage.createLoanPricesAllRanges(collateral, debt) : llamma.createLoanPricesAllRanges(collateral, debt) : null, ]) @@ -522,8 +588,20 @@ const loanCreate = { }, maxRecvLeverage: async (activeKey: string, llamma: Llamma, collateral: string, n: number) => { log('maxRecvLeverage', llamma.collateralSymbol, collateral, n) + const isV2Leverage = hasV2Leverage(llamma) let resp: MaxRecvLeverageForm = { maxBorrowable: '', maxCollateral: '', leverage: '', routeIdx: null } try { + if (isV2Leverage) { + const result = await llamma.leverageV2.createLoanMaxRecv(collateral, '0', n) + resp = { + maxBorrowable: result.maxDebt, + maxCollateral: result.maxTotalCollateral, + leverage: result.maxLeverage, + routeIdx: null, + } + return { activeKey, resp, error: '' } + } + resp = await llamma.leverage.createLoanMaxRecv(collateral, n) return { activeKey, resp, error: '' } } catch (error) { @@ -536,7 +614,9 @@ const loanCreate = { const resp = { activeKey, hashes: [] as string[], error: '' } try { resp.hashes = isLeverage - ? await llamma.leverage.createLoanApprove(collateral) + ? hasV2Leverage(llamma) + ? await llamma.leverageV2.createLoanApprove(collateral, '0') + : await llamma.leverage.createLoanApprove(collateral) : await llamma.createLoanApprove(collateral) await waitForTransactions(resp.hashes, provider) return resp @@ -560,7 +640,13 @@ const loanCreate = { const resp = { activeKey, hash: '', error: '' } try { resp.hash = isLeverage - ? await llamma.leverage.createLoan(collateral, debt, n, +maxSlippage) + ? hasV2Leverage(llamma) + ? await (async () => { + // Ensure swap data cache is populated before submitting tx + await llamma.leverageV2.createLoanExpectedCollateral(collateral, '0', debt, +maxSlippage) + return llamma.leverageV2.createLoan(collateral, '0', debt, n, +maxSlippage) + })() + : await llamma.leverage.createLoan(collateral, debt, n, +maxSlippage) : await llamma.createLoan(collateral, debt, n) await waitForTransaction(resp.hash, provider) return resp From c3099d988d1903017be8ce3d79739f8fb2f04a2b Mon Sep 17 00:00:00 2001 From: Pearce Date: Mon, 15 Dec 2025 12:47:41 +0100 Subject: [PATCH 2/3] refactor: inlined hasV2Leverage + docs --- apps/main/src/loan/lib/apiCrvusd.ts | 36 +++++++++++++---------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/apps/main/src/loan/lib/apiCrvusd.ts b/apps/main/src/loan/lib/apiCrvusd.ts index 9f82e2dc23..43cc3a148e 100644 --- a/apps/main/src/loan/lib/apiCrvusd.ts +++ b/apps/main/src/loan/lib/apiCrvusd.ts @@ -331,11 +331,10 @@ const loanCreate = { ) => { log('loanEstGas', llamma.collateralSymbol, collateral, debt, n, maxSlippage) const resp = { activeKey, isApproved: false, estimatedGas: initialGas, error: '' } - const isV2Leverage = hasV2Leverage(llamma) - const userBorrowed = '0' + const userBorrowed = '0' // hardcode to zero as that's not displayed in the form try { - if (isLeverage && isV2Leverage) { + if (isLeverage && hasV2Leverage(llamma)) { await llamma.leverageV2.createLoanExpectedCollateral(collateral, userBorrowed, debt, +maxSlippage) resp.isApproved = await llamma.leverageV2.createLoanIsApproved(collateral, userBorrowed) resp.estimatedGas = resp.isApproved @@ -401,11 +400,10 @@ const loanCreate = { maxSlippage: string, ) => { log('detailInfoLeverage', llamma.collateralSymbol, userCollateral, debt, n, maxSlippage) - const isV2Leverage = hasV2Leverage(llamma) - const userBorrowed = '0' + const userBorrowed = '0' // hardcode to zero as that's not displayed in the form try { - if (isV2Leverage) { + if (hasV2Leverage(llamma)) { // Expected collateral must run first to populate swap data cache used by other calls const expectedCollateralResult = await llamma.leverageV2.createLoanExpectedCollateral( userCollateral, @@ -517,27 +515,27 @@ const loanCreate = { const liqRangesList: LiqRange[] = [] const liqRangesListMapper: { [n: string]: LiqRange & { sliderIdx: number } } = {} let sliderIdx = 0 - const isV2Leverage = hasV2Leverage(llamma) - const userBorrowed = '0' + const isV2LeverageSupported = hasV2Leverage(llamma) + const userBorrowed = '0' // hardcode to zero as that's not displayed in the form const [maxRecvsResults, loanBandsResults, loanPricesResults] = await Promise.allSettled([ haveCollateral ? isLeverage - ? isV2Leverage + ? isV2LeverageSupported ? llamma.leverageV2.createLoanMaxRecvAllRanges(collateral, userBorrowed) : llamma.leverage.createLoanMaxRecvAllRanges(collateral) : llamma.createLoanMaxRecvAllRanges(collateral) : null, haveCollateral && haveDebt ? isLeverage - ? isV2Leverage + ? isV2LeverageSupported ? llamma.leverageV2.createLoanBandsAllRanges(collateral, userBorrowed, debt) : llamma.leverage.createLoanBandsAllRanges(collateral, debt) : llamma.createLoanBandsAllRanges(collateral, debt) : null, haveCollateral && haveDebt ? isLeverage - ? isV2Leverage + ? isV2LeverageSupported ? llamma.leverageV2.createLoanPricesAllRanges(collateral, userBorrowed, debt) : llamma.leverage.createLoanPricesAllRanges(collateral, debt) : llamma.createLoanPricesAllRanges(collateral, debt) @@ -588,11 +586,11 @@ const loanCreate = { }, maxRecvLeverage: async (activeKey: string, llamma: Llamma, collateral: string, n: number) => { log('maxRecvLeverage', llamma.collateralSymbol, collateral, n) - const isV2Leverage = hasV2Leverage(llamma) let resp: MaxRecvLeverageForm = { maxBorrowable: '', maxCollateral: '', leverage: '', routeIdx: null } + const userBorrowed = '0' // hardcode to zero as that's not displayed in the form try { - if (isV2Leverage) { - const result = await llamma.leverageV2.createLoanMaxRecv(collateral, '0', n) + if (hasV2Leverage(llamma)) { + const result = await llamma.leverageV2.createLoanMaxRecv(collateral, userBorrowed, n) resp = { maxBorrowable: result.maxDebt, maxCollateral: result.maxTotalCollateral, @@ -612,10 +610,11 @@ const loanCreate = { approve: async (activeKey: string, provider: Provider, llamma: Llamma, isLeverage: boolean, collateral: string) => { log('createLoanApprove', llamma.collateralSymbol, isLeverage ? 'leverage' : '', collateral) const resp = { activeKey, hashes: [] as string[], error: '' } + const userBorrowed = '0' // hardcode to zero as that's not displayed in the form try { resp.hashes = isLeverage ? hasV2Leverage(llamma) - ? await llamma.leverageV2.createLoanApprove(collateral, '0') + ? await llamma.leverageV2.createLoanApprove(collateral, userBorrowed) : await llamma.leverage.createLoanApprove(collateral) : await llamma.createLoanApprove(collateral) await waitForTransactions(resp.hashes, provider) @@ -638,14 +637,11 @@ const loanCreate = { ) => { log('loanCreate', llamma.collateralSymbol, isLeverage ? 'isLeverage' : '', collateral, debt, n, maxSlippage) const resp = { activeKey, hash: '', error: '' } + const userBorrowed = '0' // hardcode to zero as that's not displayed in the form try { resp.hash = isLeverage ? hasV2Leverage(llamma) - ? await (async () => { - // Ensure swap data cache is populated before submitting tx - await llamma.leverageV2.createLoanExpectedCollateral(collateral, '0', debt, +maxSlippage) - return llamma.leverageV2.createLoan(collateral, '0', debt, n, +maxSlippage) - })() + ? await llamma.leverageV2.createLoan(collateral, userBorrowed, debt, n, +maxSlippage) : await llamma.leverage.createLoan(collateral, debt, n, +maxSlippage) : await llamma.createLoan(collateral, debt, n) await waitForTransaction(resp.hash, provider) From e687e5fe06d0fca55f317031968b85d522322cd0 Mon Sep 17 00:00:00 2001 From: Pearce Date: Mon, 15 Dec 2025 12:48:13 +0100 Subject: [PATCH 3/3] feat: include V2 deleverage to warning --- .../loan/components/PageLoanCreate/LoanFormCreate/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/main/src/loan/components/PageLoanCreate/LoanFormCreate/index.tsx b/apps/main/src/loan/components/PageLoanCreate/LoanFormCreate/index.tsx index 0ecb0146cc..da7248e120 100644 --- a/apps/main/src/loan/components/PageLoanCreate/LoanFormCreate/index.tsx +++ b/apps/main/src/loan/components/PageLoanCreate/LoanFormCreate/index.tsx @@ -17,7 +17,7 @@ import { DEFAULT_FORM_STATUS } from '@/loan/store/createLoanCollateralIncreaseSl 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 { hasDeleverage } from '@/loan/utils/leverage' import { getStepStatus, getTokenName } from '@/loan/utils/utilsLoan' import { getLoanManagePathname } from '@/loan/utils/utilsRouter' import Accordion from '@ui/Accordion' @@ -439,7 +439,7 @@ const LoanCreate = ({

{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.`}

{t`Be careful, if the collateral price dips, you would need to repay the entire amount to reclaim your initial position.`}

- {!hasV1Deleverage(llamma) && ( + {!hasDeleverage(llamma) && (

{t`WARNING: The corresponding deleverage button is also not yet available.`}

)}