Skip to content

Commit

Permalink
Close position panel tweaks (#557)
Browse files Browse the repository at this point in the history
- Update info box style
- Move info box messages to content.tsx
- Better errors
- UpdateBox: make the color explicit
  • Loading branch information
bpierre authored Nov 1, 2024
1 parent 3ecf586 commit 98f154f
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 68 deletions.
1 change: 1 addition & 0 deletions frontend/app/src/comps/UpdateBox/UpdateBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function UpdateBox({
gap: 16,
padding: 16,
fontSize: 16,
color: "content",
background: "infoSurface",
border: "1px solid token(colors.infoSurfaceBorder)",
borderRadius: 8,
Expand Down
14 changes: 14 additions & 0 deletions frontend/app/src/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ export default {
},
},

closeLoan: {
repayWithBoldMessage: (
<>
You are repaying your debt and closing the position. The deposit will be returned to your wallet.
</>
),
repayWithCollateralMessage: (
<>
To close your position, a part of your collateral will be sold to pay back the debt. The rest of your collateral
will be returned to your wallet.
</>
),
},

// Home screen
home: {
openPositionTitle: "Open your first position",
Expand Down
138 changes: 70 additions & 68 deletions frontend/app/src/screens/LoanScreen/PanelClosePosition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,66 +3,80 @@ import type { PositionLoan } from "@/src/types";
import { ConnectWarningBox } from "@/src/comps/ConnectWarningBox/ConnectWarningBox";
import { ErrorBox } from "@/src/comps/ErrorBox/ErrorBox";
import { Field } from "@/src/comps/Field/Field";
import content from "@/src/content";
import { getContracts } from "@/src/contracts";
import { fmtnum } from "@/src/formatting";
import { getPrefixedTroveId } from "@/src/liquity-utils";
import { useAccount, useBalance } from "@/src/services/Ethereum";
import { usePrice } from "@/src/services/Prices";
import { useTransactionFlow } from "@/src/services/TransactionFlow";
import { css } from "@/styled-system/css";
import { Button, Dropdown, TokenIcon, TOKENS_BY_SYMBOL, VFlex } from "@liquity2/uikit";
import { addressesEqual, Button, Dropdown, TokenIcon, TOKENS_BY_SYMBOL, VFlex } from "@liquity2/uikit";
import * as dn from "dnum";
import { useEffect, useState } from "react";
import { useState } from "react";

export function PanelClosePosition({ loan }: { loan: PositionLoan }) {
const account = useAccount();
const txFlow = useTransactionFlow();

const contracts = getContracts();
const { symbol } = contracts.collaterals[loan.collIndex];
const collToken = TOKENS_BY_SYMBOL[symbol];
const collateral = contracts.collaterals[loan.collIndex];
const collToken = TOKENS_BY_SYMBOL[collateral.symbol];

const collPrice = usePrice(symbol);
const collPriceUsd = usePrice(collToken.symbol);
const boldPriceUsd = usePrice("BOLD");
const boldBalance = useBalance(account.address, "BOLD");

const [repayTokenIndex, setRepayTokenIndex] = useState(0);
const repayWith = repayTokenIndex === 0 ? "BOLD" : symbol;
const [repayDropdownIndex, setRepayDropdownIndex] = useState(0);
const repayToken = TOKENS_BY_SYMBOL[repayDropdownIndex === 0 ? "BOLD" : collToken.symbol];

const boldBalance = useBalance(account.address, "BOLD");
// either in BOLD or in collateral
const amountToRepay = repayToken.symbol === "BOLD"
? loan.borrowed
: collPriceUsd && dn.div(loan.borrowed, collPriceUsd);

const amountToRepay = collPrice && (
repayWith === "BOLD"
? loan.borrowed
: dn.div(loan.borrowed, collPrice)
const amountToRepayUsd = amountToRepay && (
repayToken.symbol === "BOLD"
? boldPriceUsd && dn.mul(amountToRepay, boldPriceUsd)
: collPriceUsd && dn.mul(amountToRepay, collPriceUsd)
);

const collToReclaim = amountToRepay && (
repayWith === "BOLD"
? loan.deposit
: dn.sub(loan.deposit, amountToRepay)
// when repaying with collateral, subtract the amount used to repay
const collToReclaim = repayToken.symbol === "BOLD"
? loan.deposit
: amountToRepay && dn.sub(loan.deposit, amountToRepay);

const collToReclaimUsd = collToReclaim && collPriceUsd && dn.mul(
collToReclaim,
collPriceUsd,
);

let error: null | { name: string; message: string } = null;
if (account.address?.toLowerCase() !== loan.borrower.toLowerCase()) {
error = {
name: "INVALID_OWNER",
message: "The current account is not the owner of the loan.",
};
} else if (repayTokenIndex === 0 && amountToRepay && (!boldBalance.data || dn.lt(boldBalance.data, amountToRepay))) {
error = {
name: "INSUFFICIENT_BALANCE",
message: "Insufficient BOLD balance to repay the loan.",
};
}
const isOwner = Boolean(account.address && addressesEqual(account.address, loan.borrower));

const [showError, setShowError] = useState(false);
useEffect(() => {
if (error === null) {
setShowError(false);
const error = (() => {
if (!isOwner) {
return {
name: "Not the owner",
message: "The current account is not the owner of the loan.",
};
}
}, [error]);
if (
isOwner
&& repayToken.symbol === "BOLD"
&& amountToRepay
&& (!boldBalance.data || dn.lt(boldBalance.data, amountToRepay))
) {
return {
name: "Insufficient BOLD balance",
message: `The balance held by the account (${
fmtnum(boldBalance.data)
} BOLD) is insufficient to repay the loan.`,
};
}
return null;
})();

if (!collPrice || !boldPriceUsd || !amountToRepay || !collToReclaim) {
if (!collPriceUsd || !boldPriceUsd || !amountToRepay || !collToReclaim) {
return null;
}

Expand All @@ -72,6 +86,7 @@ export function PanelClosePosition({ loan }: { loan: PositionLoan }) {
<>
<VFlex gap={48}>
<Field
label="You repay with"
field={
<div
className={css({
Expand All @@ -89,29 +104,26 @@ export function PanelClosePosition({ loan }: { loan: PositionLoan }) {
lineHeight: 1,
})}
>
<div>{fmtnum(amountToRepay)}</div>
{fmtnum(amountToRepay)}
</div>
<Dropdown
menuPlacement="end"
buttonDisplay={() => ({
icon: <TokenIcon symbol={repayToken.symbol} />,
label: (
<>
{TOKENS_BY_SYMBOL[(["BOLD", symbol] as const)[repayTokenIndex]].name}
{repayToken.name}
<span
className={css({
color: "contentAlt",
fontWeight: 400,
})}
>
{TOKENS_BY_SYMBOL[(["BOLD", symbol] as const)[repayTokenIndex]].symbol === "BOLD"
? " account"
: " loan"}
{repayToken.symbol === "BOLD" ? " account" : " loan"}
</span>
</>
),
icon: <TokenIcon symbol={(["BOLD", symbol] as const)[repayTokenIndex]} />,
})}
items={(["BOLD", symbol] as const).map((symbol) => ({
items={(["BOLD", collToken.symbol] as const).map((symbol) => ({
icon: <TokenIcon symbol={symbol} />,
label: (
<div
Expand All @@ -125,22 +137,23 @@ export function PanelClosePosition({ loan }: { loan: PositionLoan }) {
value: symbol === "BOLD" ? fmtnum(boldBalance.data) : null,
}))}
menuWidth={300}
onSelect={setRepayTokenIndex}
selected={repayTokenIndex}
menuPlacement="end"
onSelect={setRepayDropdownIndex}
selected={repayDropdownIndex}
/>
</div>
}
footer={{
start: (
<Field.FooterInfo
label={`$${fmtnum(dn.mul(loan.borrowed, boldPriceUsd), 2)}`}
label={`$${fmtnum(amountToRepayUsd)}`}
value={null}
/>
),
}}
label="You repay with"
/>
<Field
label="You reclaim"
field={
<div
className={css({
Expand Down Expand Up @@ -174,25 +187,16 @@ export function PanelClosePosition({ loan }: { loan: PositionLoan }) {
userSelect: "none",
})}
>
<TokenIcon symbol={symbol} />
<TokenIcon symbol={collToken.symbol} />
<div>{collToken.name}</div>
</div>
</div>
</div>
}
label="You reclaim"
footer={{
start: (
<Field.FooterInfo
label={`$${
fmtnum(
dn.mul(
collToReclaim,
collPrice,
),
2,
)
}`}
label={`$${fmtnum(collToReclaimUsd)}`}
value={null}
/>
),
Expand All @@ -205,15 +209,17 @@ export function PanelClosePosition({ loan }: { loan: PositionLoan }) {
flexDirection: "column",
gap: 32,
padding: 16,
textAlign: "center",
textWrap: "balance",
color: "content",
background: "fieldSurface",
border: "1px solid token(colors.border)",
background: "infoSurface",
border: "1px solid token(colors.infoSurfaceBorder)",
borderRadius: 8,
})}
>
{repayWith === "BOLD"
? `You are repaying your debt and closing the position. The deposit will be returned to your wallet.`
: `To close your position, a part of your collateral will be sold to pay back the debt. The rest of your collateral will be returned to your wallet.`}
{repayToken.symbol === "BOLD"
? content.closeLoan.repayWithBoldMessage
: content.closeLoan.repayWithCollateralMessage}
</div>
<div
style={{
Expand All @@ -226,7 +232,7 @@ export function PanelClosePosition({ loan }: { loan: PositionLoan }) {
>
<ConnectWarningBox />

{error && showError && (
{error && (
<div>
<ErrorBox title={error?.name}>
{error?.message}
Expand All @@ -241,10 +247,6 @@ export function PanelClosePosition({ loan }: { loan: PositionLoan }) {
size="large"
wide
onClick={() => {
if (error) {
setShowError(true);
return;
}
if (account.address) {
txFlow.start({
flowId: "closeLoanPosition",
Expand All @@ -257,7 +259,7 @@ export function PanelClosePosition({ loan }: { loan: PositionLoan }) {

collIndex: loan.collIndex,
prefixedTroveId: getPrefixedTroveId(loan.collIndex, loan.troveId),
repayWithCollateral: repayWith === symbol,
repayWithCollateral: repayToken.symbol !== "BOLD",
});
}
}}
Expand Down

0 comments on commit 98f154f

Please sign in to comment.