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
22 changes: 16 additions & 6 deletions apps/main/src/lend/components/PageLoanCreate/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,41 @@ import type { OnBorrowFormUpdate } from '@/llamalend/features/borrow/types'
import Stack from '@mui/material/Stack'
import { AppFormContentWrapper } from '@ui/AppForm'
import { useNavigate } from '@ui-kit/hooks/router'
import { useDebounced } from '@ui-kit/hooks/useDebounce'
import { useCreateLoanMuiForm } from '@ui-kit/hooks/useFeatureFlags'
import { t } from '@ui-kit/lib/i18n'
import { TabsSwitcher, type TabOption } from '@ui-kit/shared/ui/TabsSwitcher'
import { type TabOption, TabsSwitcher } from '@ui-kit/shared/ui/TabsSwitcher'
import { Duration } from '@ui-kit/themes/design/0_primitives'
import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces'

const { MaxWidth } = SizesAndSpaces

/**
* Callback that synchronizes the `ChartOhlc` component with the `RangeSlider` component in the new `BorrowTabContents`.
*/
const useOnFormUpdate = ({ api, market }: PageContentProps): OnBorrowFormUpdate =>
useCallback(
const useOnFormUpdate = ({ api, market }: PageContentProps): OnBorrowFormUpdate => {
const [setFormValues] = useDebounced(
useStore((store) => store.loanCreate.setFormValues),
Duration.FormDebounce,
)
const [setStateByKeys] = useDebounced(
useStore((store) => store.loanCreate.setStateByKeys),
Duration.FormDebounce,
)
return useCallback(
async ({ debt, userCollateral, range, slippage, leverageEnabled }) => {
const { setFormValues, setStateByKeys } = useStore.getState().loanCreate
const formValues: FormValues = {
...DEFAULT_FORM_VALUES,
n: range,
debt: `${debt ?? ''}`,
userCollateral: `${userCollateral ?? ''}`,
}
await setFormValues(api, market, formValues, `${slippage}`, leverageEnabled)
setFormValues(api, market, formValues, `${slippage}`, leverageEnabled)
setStateByKeys({ isEditLiqRange: true })
},
[api, market],
[api, market, setFormValues, setStateByKeys],
)
}

const LoanCreate = (pageProps: PageContentProps & { params: MarketUrlParams }) => {
const { rChainId, rOwmId, rFormType, market, params, api } = pageProps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,16 @@ export const CreateLoanForm = <ChainId extends IChainId>({
creationError,
txHash,
formErrors,
tooMuchDebt,
isApproved,
} = useCreateLoanForm({ market, network, preset, onCreated })
const setRange = useCallback((range: number) => form.setValue('range', range, setValueOptions), [form])
const setRange = useCallback(
(range: number) => {
// maxDebt is reset when query restarts, clear now to disable queries until recalculated
form.setValue('maxDebt', undefined, setValueOptions)
form.setValue('range', range, setValueOptions)
},
[form],
)
useFormSync(values, onUpdate)

return (
Expand All @@ -84,7 +90,6 @@ export const CreateLoanForm = <ChainId extends IChainId>({
values={values}
collateralToken={collateralToken}
borrowToken={borrowToken}
tooMuchDebt={tooMuchDebt}
networks={networks}
onSlippageChange={(value) => form.setValue('slippage', value, setValueOptions)}
/>
Expand Down Expand Up @@ -117,7 +122,11 @@ export const CreateLoanForm = <ChainId extends IChainId>({
symbol={borrowToken?.symbol}
balance={values.maxDebt}
loading={maxTokenValues.debt.isLoading}
onClick={() => form.setValue('debt', values.maxDebt, setValueOptions)}
onClick={() => {
form.setValue('debt', values.maxDebt, setValueOptions)
void form.trigger('maxDebt') // re-validate maxDebt when debt changes
}}
buttonTestId="borrow-set-debt-to-max"
/>
}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { NetworkDict } from '@/llamalend/llamalend.types'
import { useMarketRates } from '@/llamalend/queries/market-rates'
import type { IChainId } from '@curvefi/llamalend-api/lib/interfaces'
import type { UseQueryResult } from '@tanstack/react-query'
import { useSwitch } from '@ui-kit/hooks/useSwitch'
import type { Query } from '@ui-kit/types/util'
import { Decimal } from '@ui-kit/utils'
import { useCreateLoanEstimateGas } from '../../../queries/create-loan/create-loan-approve-estimate-gas.query'
import { useCreateLoanBands } from '../../../queries/create-loan/create-loan-bands.query'
Expand All @@ -15,6 +17,17 @@ import { LoanInfoAccordion } from '../../../widgets/manage-loan/LoanInfoAccordio
import { useLoanToValue } from '../hooks/useLoanToValue'
import { type BorrowForm, type BorrowFormQueryParams, type Token } from '../types'

/**
* Helper to extract only the relevant fields from a UseQueryResult into the Query type.
* This is necessary because passing UseQueryResult to any react component will crash the rendering due to
* react trying to serialize the react-query proxy object.
*/
const q = <T,>({ data, isLoading, error }: UseQueryResult<T>): Query<T> => ({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Souldn't we export it and place it in some utils to be reused elsewhere?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'll move it as soon as it's used in the follow-up PR

data,
isLoading,
error,
})

/**
* Accordion with action infos about the loan (like health, band range, price range, N, borrow APR, LTV, estimated gas, slippage)
* By default, only the health info is visible. The rest is visible when the accordion is expanded.
Expand All @@ -25,30 +38,27 @@ export const CreateLoanInfoAccordion = <ChainId extends IChainId>({
values: { range, slippage, leverageEnabled },
collateralToken,
borrowToken,
tooMuchDebt,
networks,
onSlippageChange,
}: {
params: BorrowFormQueryParams<ChainId>
values: BorrowForm
collateralToken: Token | undefined
borrowToken: Token | undefined
tooMuchDebt: boolean
networks: NetworkDict<ChainId>
onSlippageChange: (newSlippage: Decimal) => void
}) => {
const [isOpen, , , toggle] = useSwitch(false)

return (
<LoanInfoAccordion
isOpen={isOpen}
toggle={toggle}
range={range}
health={useCreateLoanHealth(params, !tooMuchDebt)}
bands={useCreateLoanBands(params, isOpen && !tooMuchDebt)}
prices={useCreateLoanPrices(params, isOpen && !tooMuchDebt)}
prevRates={useMarketRates(params, isOpen)}
rates={useMarketFutureRates(params, isOpen)}
health={q(useCreateLoanHealth(params))}
bands={q(useCreateLoanBands(params, isOpen))}
prices={q(useCreateLoanPrices(params, isOpen))}
prevRates={q(useMarketRates(params, isOpen))}
rates={q(useMarketFutureRates(params, isOpen))}
loanToValue={useLoanToValue(
{
params,
Expand All @@ -57,12 +67,12 @@ export const CreateLoanInfoAccordion = <ChainId extends IChainId>({
},
isOpen,
)}
gas={useCreateLoanEstimateGas(networks, params, isOpen && !tooMuchDebt)}
gas={useCreateLoanEstimateGas(networks, params, isOpen)}
leverage={{
enabled: leverageEnabled,
expectedCollateral: useCreateLoanExpectedCollateral(params, isOpen && leverageEnabled),
maxReceive: useCreateLoanMaxReceive(params, isOpen && leverageEnabled && !tooMuchDebt),
priceImpact: useCreateLoanPriceImpact(params, isOpen && leverageEnabled),
expectedCollateral: useCreateLoanExpectedCollateral(params, isOpen),
maxReceive: useCreateLoanMaxReceive(params, isOpen),
priceImpact: useCreateLoanPriceImpact(params, isOpen),
slippage,
onSlippageChange,
collateralSymbol: collateralToken?.symbol,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const LoanPresetSelector = ({
sx={{ width: '100%', paddingBottom: Spacing.sm }}
>
{Object.values(BorrowPreset).map((p) => (
<ToggleButton key={p} value={p} size="extraSmall" sx={{ flexGrow: 1 }}>
<ToggleButton key={p} value={p} size="extraSmall" sx={{ flexGrow: 1 }} data-testid={`loan-preset-${p}`}>
{PRESETS[p].title}
</ToggleButton>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ import { SLIPPAGE_PRESETS } from '@ui-kit/widgets/SlippageSettings/slippage.util
import { BORROW_PRESET_RANGES, BorrowPreset } from '../../../constants'
import { type CreateLoanOptions, useCreateLoanMutation } from '../../../mutations/create-loan.mutation'
import { useBorrowCreateLoanIsApproved } from '../../../queries/create-loan/borrow-create-loan-approved.query'
import { borrowFormValidationSuite } from '../../../queries/validation/borrow.validation'
import { borrowQueryValidationSuite } from '../../../queries/validation/borrow.validation'
import { useFormErrors } from '../react-form.utils'
import { type BorrowForm } from '../types'
import { useMaxTokenValues } from './useMaxTokenValues'

const useCallbackAfterFormUpdate = (form: UseFormReturn<BorrowForm>, callback: () => void) =>
useEffect(() => form.subscribe({ formState: { values: true }, callback }), [form, callback])

// to crete a loan we need the debt/maxDebt, but we skip the market validation as that's given separately to the mutation
const resolver = vestResolver(borrowQueryValidationSuite({ debtRequired: false, skipMarketValidation: true }))

export function useCreateLoanForm<ChainId extends LlamaChainId>({
market,
network,
Expand All @@ -36,8 +39,7 @@ export function useCreateLoanForm<ChainId extends LlamaChainId>({
const { address: userAddress } = useConnection()
const form = useForm<BorrowForm>({
...formDefaultOptions,
// todo: also validate maxLeverage and maxCollateral
resolver: vestResolver(borrowFormValidationSuite),
resolver,
defaultValues: {
userCollateral: undefined,
userBorrowed: `0` satisfies Decimal,
Expand Down Expand Up @@ -75,15 +77,14 @@ export function useCreateLoanForm<ChainId extends LlamaChainId>({
values,
params,
isPending: form.formState.isSubmitting || isCreating,
onSubmit: form.handleSubmit(onSubmit), // todo: handle form errors
onSubmit: form.handleSubmit(onSubmit),
maxTokenValues: useMaxTokenValues(collateralToken, params, form),
borrowToken,
collateralToken,
isCreated,
creationError,
txHash: data?.hash,
isApproved: useBorrowCreateLoanIsApproved(params),
tooMuchDebt: !!form.formState.errors['maxDebt'],
formErrors: useFormErrors(form.formState),
}
}
5 changes: 5 additions & 0 deletions apps/main/src/llamalend/features/borrow/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,10 @@ export type BorrowFormQuery<T = IChainId> = MarketQuery<T> & CompleteBorrowForm
/** Fields of the borrow form query before validation */
export type BorrowFormQueryParams<T = IChainId> = FieldsOf<BorrowFormQuery<T>>

/** Borrow form query including max debt field */
export type BorrowDebtQuery<T = IChainId> = BorrowFormQuery<T> & Pick<BorrowForm, 'maxDebt'>
/** Fields of the borrow debt query before validation */
export type BorrowDebtParams<T = IChainId> = FieldsOf<BorrowDebtQuery<T>>

/** A simple token representation */
export type Token = { symbol: string; address: Address }
23 changes: 0 additions & 23 deletions apps/main/src/llamalend/features/market-list/LlamaMarketSort.tsx

This file was deleted.

3 changes: 2 additions & 1 deletion apps/main/src/llamalend/mutations/add-collateral.mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export const useAddCollateralMutation = ({
mutationKey: [...rootKeys.userMarket({ chainId, marketId, userAddress }), 'add-collateral'] as const,
mutationFn: async (mutation, { market }) => {
await waitForApproval({
isApproved: () => fetchAddCollateralIsApproved({ chainId, marketId, userAddress, ...mutation }),
isApproved: () =>
fetchAddCollateralIsApproved({ chainId, marketId, userAddress, ...mutation }, { staleTime: 0 }),
onApprove: () => approve(market, mutation),
message: t`Approved collateral addition`,
config,
Expand Down
6 changes: 3 additions & 3 deletions apps/main/src/llamalend/mutations/create-loan.mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { formatTokenAmounts } from '@/llamalend/llama.utils'
import type { LlamaMarketTemplate } from '@/llamalend/llamalend.types'
import { type LlammaMutationOptions, useLlammaMutation } from '@/llamalend/mutations/useLlammaMutation'
import { fetchBorrowCreateLoanIsApproved } from '@/llamalend/queries/create-loan/borrow-create-loan-approved.query'
import { borrowFormValidationSuite } from '@/llamalend/queries/validation/borrow.validation'
import { borrowQueryValidationSuite } from '@/llamalend/queries/validation/borrow.validation'
import type {
IChainId as LlamaChainId,
IChainId,
Expand Down Expand Up @@ -72,14 +72,14 @@ export const useCreateLoanMutation = ({
mutationFn: async (mutation, { market }) => {
const params = { ...mutation, chainId, marketId }
await waitForApproval({
isApproved: () => fetchBorrowCreateLoanIsApproved(params),
isApproved: async () => await fetchBorrowCreateLoanIsApproved(params, { staleTime: 0 }),
onApprove: () => approve(market, mutation),
message: t`Approved loan creation`,
config,
})
return { hash: await create(market, mutation) }
},
validationSuite: borrowFormValidationSuite,
validationSuite: borrowQueryValidationSuite({ debtRequired: true }),
pendingMessage: (mutation, { market }) => t`Creating loan... ${formatTokenAmounts(market, mutation)}`,
successMessage: (mutation, { market }) => t`Loan created! ${formatTokenAmounts(market, mutation)}`,
onSuccess: onCreated,
Expand Down
21 changes: 12 additions & 9 deletions apps/main/src/llamalend/mutations/repay.mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,18 @@ export const useRepayMutation = ({

await waitForApproval({
isApproved: () =>
fetchRepayIsApproved({
chainId,
marketId,
userAddress,
stateCollateral,
userCollateral,
userBorrowed,
isFull,
}),
fetchRepayIsApproved(
{
chainId,
marketId,
userAddress,
stateCollateral,
userCollateral,
userBorrowed,
isFull,
},
{ staleTime: 0 },
),
onApprove: () => approveRepay(market, mutation),
message: t`Approved repayment`,
config,
Expand Down
1 change: 1 addition & 0 deletions apps/main/src/llamalend/mutations/useLlammaMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export function useLlammaMutation<TVariables extends object, TData extends Resul
await onSuccess(data, receipt, variables, context)
},
onError: (error, variables, context) => {
console.error(`Error in mutation ${mutationKey}:`, error)
logError(mutationKey, { error, variables, marketId: context?.market.id })
notify(error.message, 'error')
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getLlamaMarket } from '@/llamalend/llama.utils'
import { MintMarketTemplate } from '@curvefi/llamalend-api/lib/mintMarkets'
import { queryFactory, rootKeys } from '@ui-kit/lib/model'
import type { BorrowFormQuery, BorrowFormQueryParams } from '../../features/borrow/types'
import type { BorrowDebtQuery, BorrowFormQueryParams } from '../../features/borrow/types'
import { borrowQueryValidationSuite } from '../validation/borrow.validation'

export const { useQuery: useBorrowCreateLoanIsApproved, fetchQuery: fetchBorrowCreateLoanIsApproved } = queryFactory({
Expand All @@ -18,7 +18,7 @@ export const { useQuery: useBorrowCreateLoanIsApproved, fetchQuery: fetchBorrowC
userBorrowed = '0',
userCollateral = '0',
leverageEnabled,
}: BorrowFormQuery): Promise<boolean> => {
}: BorrowDebtQuery): Promise<boolean> => {
const market = getLlamaMarket(marketId)
return leverageEnabled
? market instanceof MintMarketTemplate && market.leverageV2.hasLeverage()
Expand All @@ -27,5 +27,5 @@ export const { useQuery: useBorrowCreateLoanIsApproved, fetchQuery: fetchBorrowC
: await market.createLoanIsApproved(userCollateral)
},
staleTime: '1m',
validationSuite: borrowQueryValidationSuite,
validationSuite: borrowQueryValidationSuite({ debtRequired: false }),
})
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Suite } from 'vest'
import { useEstimateGas } from '@/llamalend/hooks/useEstimateGas'
import { getLlamaMarket } from '@/llamalend/llama.utils'
import { type NetworkDict } from '@/llamalend/llamalend.types'
Expand Down Expand Up @@ -36,7 +37,10 @@ const { useQuery: useCreateLoanApproveEstimateGas } = queryFactory({
? await market.leverageV2.estimateGas.createLoanApprove(userCollateral, userBorrowed)
: await market.leverage.estimateGas.createLoanApprove(userCollateral)
},
validationSuite: borrowQueryValidationSuite,
validationSuite: borrowQueryValidationSuite({ debtRequired: false }) as Suite<
keyof CreateLoanApproveEstimateGasQuery,
string
>,
dependencies: (params) => [createLoanMaxReceiveKey(params)],
})

Expand Down
Loading