Skip to content
Draft
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
@@ -1,7 +1,12 @@
import { useMemo } from 'react'
import { useLoanToValueFromUserState } from '@/llamalend/features/manage-loan/hooks/useLoanToValueFromUserState'
import { useHealthQueries } from '@/llamalend/hooks/useHealthQueries'
import type { LlamaMarketTemplate, NetworkDict } from '@/llamalend/llamalend.types'
import type { AddCollateralOptions } from '@/llamalend/mutations/add-collateral.mutation'
import { useMarketRates } from '@/llamalend/queries/market-rates'
import { getUserHealthOptions } from '@/llamalend/queries/user-health.query'
import { mapQuery } from '@/llamalend/queries/utils'
import type { Query } from '@/llamalend/widgets/manage-loan/loan.types'
import { LoanFormAlerts } from '@/llamalend/widgets/manage-loan/LoanFormAlerts'
import { LoanFormTokenInput } from '@/llamalend/widgets/manage-loan/LoanFormTokenInput'
import { LoanFormWrapper } from '@/llamalend/widgets/manage-loan/LoanFormWrapper'
Expand All @@ -11,9 +16,15 @@ import Button from '@mui/material/Button'
import Stack from '@mui/material/Stack'
import { useSwitch } from '@ui-kit/hooks/useSwitch'
import { t } from '@ui-kit/lib/i18n'
import { decimal } from '@ui-kit/utils'
import { InputDivider } from '../../../widgets/InputDivider'
import { useAddCollateralForm } from '../hooks/useAddCollateralForm'

const withTokenSymbol = <T,>(query: Query<T | null>, tokenSymbol?: string) => ({
...query,
tokenSymbol,
})

export const AddCollateralForm = <ChainId extends IChainId>({
market,
networks,
Expand All @@ -37,15 +48,14 @@ export const AddCollateralForm = <ChainId extends IChainId>({
action,
params,
values,
bands,
health,
prices,
gas,
isApproved,
formErrors,
collateralToken,
borrowToken,
txHash,
userState,
} = useAddCollateralForm({
market,
network,
Expand All @@ -54,36 +64,80 @@ export const AddCollateralForm = <ChainId extends IChainId>({
onAdded,
})

const marketRates = useMarketRates(params, isOpen)
const prevCollateral = useMemo(
() =>
withTokenSymbol(
mapQuery(userState, (state) => state?.collateral),
collateralToken?.symbol,
),
[collateralToken?.symbol, userState],
)
const prevDebt = useMemo(
() =>
withTokenSymbol(
mapQuery(userState, (state) => state?.debt),
borrowToken?.symbol,
),
[borrowToken?.symbol, userState],
)
const prevLoanToValue = useLoanToValueFromUserState({
chainId,
marketId: params.marketId,
userAddress: params.userAddress,
collateralToken,
borrowToken,
enabled: isOpen,
})
const prevHealth = useHealthQueries((isFull) => getUserHealthOptions({ ...params, isFull }, undefined))

const collateral = useMemo(
() =>
withTokenSymbol(
{
...mapQuery(userState, (state) => state?.collateral),
data: decimal(
values.userCollateral
? +values.userCollateral + (userState.data?.collateral ? +userState.data?.collateral : 0)
: null,
),
},
collateralToken?.symbol,
),
[collateralToken?.symbol, userState, values.userCollateral],
)
const marketRates = useMarketRates(params, isOpen)
const loanToValue = useLoanToValueFromUserState({
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Can be moved to the useAddCollateralForm as well

chainId: params.chainId!,
marketId: params.marketId,
userAddress: params.userAddress,
collateralToken,
borrowToken,
enabled: !!enabled && !!values.userCollateral,
collateralDelta: values.userCollateral,
})
return (
<LoanFormWrapper
{...form}
onSubmit={onSubmit}
infoAccordion={
<LoanInfoAccordion // todo: prevHealth, prevRates, debt, prevDebt
<LoanInfoAccordion // todo: prevRates
isOpen={isOpen}
toggle={toggle}
prevHealth={prevHealth}
health={health}
bands={bands}
prices={prices}
rates={marketRates}
loanToValue={useLoanToValueFromUserState({
chainId: params.chainId!,
marketId: params.marketId,
userAddress: params.userAddress,
collateralToken,
borrowToken,
enabled: isOpen,
collateralDelta: values.userCollateral,
})}
prevLoanToValue={prevLoanToValue}
loanToValue={loanToValue}
prevDebt={prevDebt}
gas={gas}
prevCollateral={prevCollateral}
collateral={collateral}
/>
}
>
<Stack divider={<InputDivider />}>
<LoanFormTokenInput
label={t`Collateral`}
label={t`Collateral to Add`}
token={collateralToken}
blockchainId={network.id}
name="userCollateral"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useAddCollateralBands } from '@/llamalend/queries/add-collateral/add-co
import { useAddCollateralEstimateGas } from '@/llamalend/queries/add-collateral/add-collateral-gas-estimate.query'
import { getAddCollateralHealthOptions } from '@/llamalend/queries/add-collateral/add-collateral-health.query'
import { useAddCollateralPrices } from '@/llamalend/queries/add-collateral/add-collateral-prices.query'
import { useUserState } from '@/llamalend/queries/user-state.query'
import type { CollateralParams } from '@/llamalend/queries/validation/manage-loan.types'
import {
collateralFormValidationSuite,
Expand Down Expand Up @@ -81,6 +82,7 @@ export const useAddCollateralForm = <ChainId extends LlamaChainId>({

useCallbackAfterFormUpdate(form, action.reset)

const userState = useUserState(params, enabled)
const bands = useAddCollateralBands(params, enabled)
const health = useHealthQueries((isFull) => getAddCollateralHealthOptions({ ...params, isFull }, enabled))
const prices = useAddCollateralPrices(params, enabled)
Expand All @@ -104,5 +106,6 @@ export const useAddCollateralForm = <ChainId extends LlamaChainId>({
collateralToken,
borrowToken,
txHash: action.data?.hash,
userState,
}
}
16 changes: 16 additions & 0 deletions apps/main/src/llamalend/queries/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Query } from '../widgets/manage-loan/loan.types'

/**
* Maps a Query type to extract partial data from it.
* Preserves error and loading states while transforming the data.
*/
export function mapQuery<TSource, TResult>(
query: Query<TSource>,
selector: (data: TSource) => TResult,
): Query<TResult> {
return {
data: query.data === undefined ? undefined : selector(query.data),
isLoading: query.isLoading,
error: query.error,
}
}
29 changes: 22 additions & 7 deletions apps/main/src/llamalend/widgets/manage-loan/LoanFormTokenInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import type { Query } from '@/llamalend/widgets/manage-loan/loan.types'
import type { INetworkName } from '@curvefi/llamalend-api/lib/interfaces'
import type { PartialRecord } from '@curvefi/prices-api/objects.util'
import { useTokenBalance } from '@ui-kit/hooks/useTokenBalance'
import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate'
import { LargeTokenInput } from '@ui-kit/shared/ui/LargeTokenInput'
import { TokenLabel } from '@ui-kit/shared/ui/TokenLabel'
import { Decimal } from '@ui-kit/utils'
import { decimal, Decimal } from '@ui-kit/utils'

/**
* A large token input field for loan forms, with balance and max handling.
Expand Down Expand Up @@ -51,9 +52,26 @@ export const LoanFormTokenInput = <
error: balanceError,
} = useTokenBalance({ chainId: network?.chainId, userAddress }, token)

const { data: usdRate } = useTokenUsdRate(
{ chainId: network?.chainId, tokenAddress: token?.address },
!!token?.address,
)

const walletBalance = useMemo(
// todo: support separate isLoading for balance and for maxBalance in LargeTokenInput
() => ({
balance,
symbol: token?.symbol,
loading: isBalanceLoading,
usdRate,
}),
[balance, isBalanceLoading, token?.symbol, usdRate],
)

const errors = form.formState.errors as PartialRecord<FieldPath<TFieldValues>, Error>
const relatedMaxFieldError = max?.fieldName && errors[max.fieldName]
const error = errors[name] || max?.error || balanceError || relatedMaxFieldError
const value = form.getValues(name)
return (
<LargeTokenInput
name={name}
Expand All @@ -67,19 +85,16 @@ export const LoanFormTokenInput = <
label={token?.symbol ?? '?'}
/>
}
balance={form.getValues(name)}
balance={value}
onBalance={useCallback(
(v?: Decimal) => form.setValue(name, v as FieldPathValue<TFieldValues, TFieldName>, setValueOptions),
[form, name],
)}
isError={!!error}
message={error?.message ?? message}
walletBalance={useMemo(
// todo: support separate isLoading for balance and for maxBalance in LargeTokenInput
() => ({ balance, symbol: token?.symbol, loading: isBalanceLoading }),
[balance, isBalanceLoading, token?.symbol],
)}
walletBalance={walletBalance}
maxBalance={useMemo(() => max && { balance: max.data, chips: 'max' }, [max])}
inputBalanceUsd={decimal(value && usdRate && usdRate * +value)}
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const LoanFormWrapper = <TFieldValues extends FieldValues, TContext = any
} & FormProviderProps<TFieldValues, TContext, TTransformedValues>) => (
<FormProvider {...form}>
<form onSubmit={onSubmit} style={{ overflowWrap: 'break-word' }}>
<Stack gap={Spacing.md}>
<Stack gap={Spacing.sm}>
<Stack sx={{ backgroundColor: (t) => t.design.Layer[1].Fill }}>
<AppFormContentWrapper>
<Stack gap={Spacing.md}>{children}</Stack>
Expand Down
Loading