Skip to content

Commit

Permalink
Fix available in repay forms
Browse files Browse the repository at this point in the history
  • Loading branch information
sophialittlejohn committed Aug 5, 2024
1 parent 292c31b commit 3fea8c4
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 158 deletions.
290 changes: 138 additions & 152 deletions centrifuge-app/src/pages/Loan/ExternalRepayForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,6 @@ export function ExternalRepayForm({ loan, destination }: { loan: ExternalLoan; d
}
)

const { execute: doCloseTransaction, isLoading: isCloseLoading } = useCentrifugeTransaction(
'Close asset',
(cent) => cent.pools.closeLoan
)

const currentFace =

Check warning on line 90 in centrifuge-app/src/pages/Loan/ExternalRepayForm.tsx

View workflow job for this annotation

GitHub Actions / build-app

'currentFace' is assigned a value but never used

Check warning on line 90 in centrifuge-app/src/pages/Loan/ExternalRepayForm.tsx

View workflow job for this annotation

GitHub Actions / ff-prod / build-app

'currentFace' is assigned a value but never used
loan?.pricing && 'outstandingQuantity' in loan.pricing
? loan.pricing.outstandingQuantity.toDecimal().mul(loan.pricing.notional.toDecimal())
Expand Down Expand Up @@ -125,6 +120,7 @@ export function ExternalRepayForm({ loan, destination }: { loan: ExternalLoan; d
const debt = ('outstandingDebt' in loan && loan.outstandingDebt?.toDecimal()) || Dec(0)
const { maxAvailable, maxInterest, totalRepay } = React.useMemo(() => {
const outstandingInterest = 'outstandingInterest' in loan ? loan.outstandingInterest.toDecimal() : Dec(0)
const outstandingDebt = 'outstandingDebt' in loan ? loan.outstandingDebt.toDecimal() : Dec(0)
const { quantity, interest, amountAdditional, price } = repayForm.values
const totalRepay = Dec(price || 0)
.mul(quantity || 0)
Expand All @@ -133,8 +129,8 @@ export function ExternalRepayForm({ loan, destination }: { loan: ExternalLoan; d
let maxAvailable = min(balance, debt)
let maxInterest = min(balance, outstandingInterest)
if (destination !== 'reserve') {
maxAvailable = destinationLoan.outstandingDebt?.toDecimal() || Dec(0)
maxInterest = outstandingInterest || Dec(0)
maxAvailable = outstandingDebt
maxInterest = outstandingInterest
}
return {
maxAvailable,
Expand All @@ -144,172 +140,162 @@ export function ExternalRepayForm({ loan, destination }: { loan: ExternalLoan; d
),
totalRepay,
}
}, [loan, destinationLoan, balance, repayForm.values])
}, [loan, balance, repayForm.values])

Check warning on line 143 in centrifuge-app/src/pages/Loan/ExternalRepayForm.tsx

View workflow job for this annotation

GitHub Actions / build-app

React Hook React.useMemo has missing dependencies: 'debt' and 'destination'. Either include them or remove the dependency array

Check warning on line 143 in centrifuge-app/src/pages/Loan/ExternalRepayForm.tsx

View workflow job for this annotation

GitHub Actions / ff-prod / build-app

React Hook React.useMemo has missing dependencies: 'debt' and 'destination'. Either include them or remove the dependency array

if (loan.status === 'Closed' || ('valuationMethod' in loan.pricing && loan.pricing.valuationMethod !== 'oracle')) {
return null
}

return (
<>
{currentFace ? (
<Stack>
<Shelf justifyContent="space-between">
<Text variant="label2">Current face</Text>
<Text variant="label2">{formatBalance(currentFace, pool.currency.symbol, 2, 2)}</Text>
</Shelf>
</Stack>
) : null}
<FormikProvider value={repayForm}>
<Stack as={Form} gap={2} noValidate ref={repayFormRef}>
<Field validate={combine(nonNegativeNumberNotRequired())} name="quantity">
{({ field, meta, form }: FieldProps) => {
return (
<CurrencyInput
{...field}
label="Quantity"
disabled={isRepayLoading}
errorMessage={meta.touched ? meta.error : undefined}
decimals={8}
onChange={(value) => form.setFieldValue('quantity', value)}
currency={pool.currency.symbol}
/>
)
}}
</Field>
<Field
validate={combine(
nonNegativeNumberNotRequired(),
maxNotRequired(
maxAvailable.toNumber(),
`Quantity x price (${formatBalance(
Dec(repayForm.values.price || 0).mul(repayForm.values.quantity || 0),
pool.currency.symbol
)}) exceeds available debt (${formatBalance(maxAvailable, pool.currency.symbol)})`
)
)}
name="price"
>
{({ field, form }: FieldProps) => {
return (
<CurrencyInput
{...field}
label="Settlement price"
disabled={isRepayLoading}
currency={pool.currency.symbol}
onChange={(value) => form.setFieldValue('price', value)}
decimals={8}
/>
)
}}
</Field>
{'outstandingInterest' in loan && loan.outstandingInterest.toDecimal().gt(0) && (
<Field
validate={combine(nonNegativeNumberNotRequired(), maxNotRequired(maxInterest.toNumber()))}
name="interest"
>
{({ field, meta, form }: FieldProps) => {
return (
<CurrencyInput
{...field}
value={field.value instanceof Decimal ? field.value.toNumber() : field.value}
label="Interest"
errorMessage={meta.touched ? meta.error : undefined}
secondaryLabel={`${formatBalance(
loan.outstandingInterest,
pool?.currency.symbol,
2
)} interest accrued`}
disabled={isRepayLoading}
currency={pool?.currency.symbol}
onChange={(value) => form.setFieldValue('interest', value)}
onSetMax={() => form.setFieldValue('interest', maxInterest.toNumber())}
/>
)
}}
</Field>
<FormikProvider value={repayForm}>
<Stack as={Form} gap={2} noValidate ref={repayFormRef}>
<Field validate={combine(nonNegativeNumberNotRequired())} name="quantity">
{({ field, meta, form }: FieldProps) => {
return (
<CurrencyInput
{...field}
label="Quantity"
disabled={isRepayLoading}
errorMessage={meta.touched ? meta.error : undefined}
decimals={8}
onChange={(value) => form.setFieldValue('quantity', value)}
currency={pool.currency.symbol}
/>
)
}}
</Field>
<Field
validate={combine(
nonNegativeNumberNotRequired(),
maxNotRequired(
maxAvailable.toNumber(),
`Quantity x price (${formatBalance(
Dec(repayForm.values.price || 0).mul(repayForm.values.quantity || 0),
pool.currency.symbol
)}) exceeds available debt (${formatBalance(maxAvailable, pool.currency.symbol)})`
)
)}
name="price"
>
{({ field, form }: FieldProps) => {
return (
<CurrencyInput
{...field}
label="Settlement price"
disabled={isRepayLoading}
currency={pool.currency.symbol}
onChange={(value) => form.setFieldValue('price', value)}
decimals={8}
/>
)
}}
</Field>
{'outstandingInterest' in loan && loan.outstandingInterest.toDecimal().gt(0) && (
<Field
name="amountAdditional"
validate={combine(nonNegativeNumberNotRequired(), maxNotRequired(maxAvailable.toNumber()))}
validate={combine(nonNegativeNumberNotRequired(), maxNotRequired(maxInterest.toNumber()))}
name="interest"
>
{({ field, form }: FieldProps) => {
{({ field, meta, form }: FieldProps) => {
return (
<CurrencyInput
{...field}
value={field.value instanceof Decimal ? field.value.toNumber() : field.value}
label="Additional amount"
label="Interest"
errorMessage={meta.touched ? meta.error : undefined}
secondaryLabel={`${formatBalance(
loan.outstandingInterest,
pool?.currency.symbol,
2
)} interest accrued`}
disabled={isRepayLoading}
currency={pool?.currency.symbol}
onChange={(value) => form.setFieldValue('amountAdditional', value)}
onChange={(value) => form.setFieldValue('interest', value)}
onSetMax={() => form.setFieldValue('interest', maxInterest.toNumber())}
/>
)
}}
</Field>
<Box bg="statusDefaultBg" p={1}>
{destination === 'reserve' ? (
<InlineFeedback status="default">
<Text color="statusDefault">Stablecoins will be transferred to the onchain reserve.</Text>
</InlineFeedback>
) : (
<InlineFeedback status="default">
<Text color="statusDefault">
Virtual accounting process. No onchain stablecoin transfers are expected.
</Text>
</InlineFeedback>
)}
</Box>
{poolFees.render()}
<Stack gap={1}>
<Shelf justifyContent="space-between">
<Text variant="emphasized">Total amount</Text>
<Text variant="emphasized">{formatBalance(totalRepay, pool?.currency.symbol, 2)}</Text>
</Shelf>
)}
<Field
name="amountAdditional"
validate={combine(nonNegativeNumberNotRequired(), maxNotRequired(maxAvailable.toNumber()))}
>
{({ field, form }: FieldProps) => {
return (
<CurrencyInput
{...field}
value={field.value instanceof Decimal ? field.value.toNumber() : field.value}
label="Additional amount"
disabled={isRepayLoading}
currency={pool?.currency.symbol}
onChange={(value) => form.setFieldValue('amountAdditional', value)}
/>
)
}}
</Field>
<Box bg="statusDefaultBg" p={1}>
{destination === 'reserve' ? (
<InlineFeedback status="default">
<Text color="statusDefault">Stablecoins will be transferred to the onchain reserve.</Text>
</InlineFeedback>
) : (
<InlineFeedback status="default">
<Text color="statusDefault">
Virtual accounting process. No onchain stablecoin transfers are expected.
</Text>
</InlineFeedback>
)}
</Box>
{poolFees.render()}
<Stack gap={1}>
<Shelf justifyContent="space-between">
<Text variant="emphasized">Total amount</Text>
<Text variant="emphasized">{formatBalance(totalRepay, pool?.currency.symbol, 2)}</Text>
</Shelf>

{poolFees.renderSummary()}
{poolFees.renderSummary()}

<Shelf justifyContent="space-between">
<Text variant="emphasized">Available</Text>
<Text variant="emphasized">{formatBalance(maxAvailable, pool?.currency.symbol, 2)}</Text>
</Shelf>
</Stack>
{balance.lessThan(debt) && destination === 'reserve' && (
<Box bg="statusWarningBg" p={1}>
<InlineFeedback status="warning">
<Text color="statusWarning">
Your wallet balance ({formatBalance(roundDown(balance), pool?.currency.symbol, 2)}) is smaller than
the outstanding balance ({formatBalance(debt, pool.currency.symbol)}).
</Text>
</InlineFeedback>
</Box>
)}
{totalRepay.gt(maxAvailable) && (
<Box bg="statusCriticalBg" p={1}>
<InlineFeedback status="critical">
<Text color="statusCritical">
The amount ({formatBalance(roundDown(totalRepay), pool?.currency.symbol, 2)}) is greater than the
available debt ({formatBalance(maxAvailable, pool.currency.symbol)}).
</Text>
</InlineFeedback>
</Box>
)}
<Stack gap={1} px={1}>
<Button
type="submit"
disabled={
isRepayLoading ||
!poolFees.isValid(repayForm) ||
!repayForm.isValid ||
totalRepay.greaterThan(maxAvailable) ||
maxAvailable.eq(0)
}
loading={isRepayLoading}
>
Sell
</Button>
</Stack>
<Shelf justifyContent="space-between">
<Text variant="emphasized">Available</Text>
<Text variant="emphasized">{formatBalance(maxAvailable, pool?.currency.symbol, 2)}</Text>
</Shelf>
</Stack>
{balance.lessThan(debt) && destination === 'reserve' && (
<Box bg="statusWarningBg" p={1}>
<InlineFeedback status="warning">
<Text color="statusWarning">
Your wallet balance ({formatBalance(roundDown(balance), pool?.currency.symbol, 2)}) is smaller than the
outstanding balance ({formatBalance(debt, pool.currency.symbol)}).
</Text>
</InlineFeedback>
</Box>
)}
{totalRepay.gt(maxAvailable) && (
<Box bg="statusCriticalBg" p={1}>
<InlineFeedback status="critical">
<Text color="statusCritical">
The amount ({formatBalance(roundDown(totalRepay), pool?.currency.symbol, 2)}) is greater than the
available debt ({formatBalance(maxAvailable, pool.currency.symbol)}).
</Text>
</InlineFeedback>
</Box>
)}
<Stack gap={1} px={1}>
<Button
type="submit"
disabled={
isRepayLoading ||
!poolFees.isValid(repayForm) ||
!repayForm.isValid ||
totalRepay.greaterThan(maxAvailable) ||
maxAvailable.eq(0)
}
loading={isRepayLoading}
>
Sell
</Button>
</Stack>
</FormikProvider>
</>
</Stack>
</FormikProvider>
)
}
10 changes: 4 additions & 6 deletions centrifuge-app/src/pages/Loan/RepayForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,22 +146,20 @@ function InternalRepayForm({ loan, destination }: { loan: ActiveLoan | CreatedLo
const { maxAvailable, maxPrincipal, maxInterest, totalRepay } = React.useMemo(() => {
const { interest, principal, amountAdditional } = repayForm.values
const outstandingInterest = 'outstandingInterest' in loan ? loan.outstandingInterest.toDecimal() : Dec(0)
const destinationOutstandingDebt =
destinationLoan && 'outstandingDebt' in destinationLoan ? destinationLoan.outstandingDebt.toDecimal() : Dec(0)
let maxAvailable
let maxPrincipal
let maxInterest
if (destination === 'reserve') {
maxAvailable = min(balance, loan?.outstandingDebt.toDecimal() || Dec(0))
maxAvailable = min(balance, loan.outstandingDebt.toDecimal())
maxPrincipal = min(balance, loan.outstandingDebt.toDecimal())
maxInterest = min(balance, outstandingInterest)
} else if (destination === 'other') {
maxAvailable = min(balance, loan.outstandingDebt.toDecimal())
maxPrincipal = min(balance, loan.outstandingDebt.toDecimal())
maxInterest = Dec(0)
} else {
maxAvailable = destinationOutstandingDebt
maxPrincipal = destinationOutstandingDebt
maxAvailable = loan.outstandingDebt.toDecimal()
maxPrincipal = loan.outstandingDebt.toDecimal()
maxInterest = outstandingInterest
}
const totalRepay = Dec(principal || 0)
Expand All @@ -173,7 +171,7 @@ function InternalRepayForm({ loan, destination }: { loan: ActiveLoan | CreatedLo
maxInterest: maxDec(min(maxInterest, maxAvailable.sub(principal || 0).sub(amountAdditional || 0)), Dec(0)),
totalRepay,
}
}, [loan, destinationLoan, balance, repayForm.values])
}, [loan, balance, repayForm.values])

Check warning on line 174 in centrifuge-app/src/pages/Loan/RepayForm.tsx

View workflow job for this annotation

GitHub Actions / build-app

React Hook React.useMemo has a missing dependency: 'destination'. Either include it or remove the dependency array

Check warning on line 174 in centrifuge-app/src/pages/Loan/RepayForm.tsx

View workflow job for this annotation

GitHub Actions / ff-prod / build-app

React Hook React.useMemo has a missing dependency: 'destination'. Either include it or remove the dependency array

return (
<>
Expand Down

0 comments on commit 3fea8c4

Please sign in to comment.