DeFiCh/wallet

View on GitHub
mobile-app/app/screens/AppNavigator/screens/Loans/components/CollateralizationRatioDisplay.tsx

Summary

Maintainability
A
2 hrs
Test Coverage
import * as Progress from "react-native-progress";
import BigNumber from "bignumber.js";
import { getColor, tailwind } from "@tailwind";
import { useThemeContext } from "@waveshq/walletkit-ui";
import { ThemedTextV2 } from "@components/themed";
import { translate } from "@translations";
import { useCollateralRatioStats } from "@screens/AppNavigator/screens/Loans/hooks/CollateralizationRatio";
import { NumericFormat as NumberFormat } from "react-number-format";
import { Text, View } from "react-native";

interface CollateralizationRatioDisplayProps {
  collateralizationRatio: string;
  minCollateralizationRatio: string;
  collateralValue: string;
  totalLoanAmount: string;
  testID: string;
  showProgressBar?: boolean;
  customReadyText?: string;
}

export function CollateralizationRatioDisplay(
  props: CollateralizationRatioDisplayProps
): JSX.Element {
  const { isLight } = useThemeContext();
  const atRiskThresholdMultiplier = 1.5;
  const minColRatio = new BigNumber(props.minCollateralizationRatio);
  const maxRatio = getMaxRatio(
    minColRatio.multipliedBy(atRiskThresholdMultiplier)
  );
  const isInvalidCollateralRatio =
    new BigNumber(props.collateralizationRatio).isLessThan(0) ||
    new BigNumber(props.collateralizationRatio).isNaN() ||
    !new BigNumber(props.collateralizationRatio).isFinite();

  const normalizedNextFactor = isInvalidCollateralRatio
    ? new BigNumber(new BigNumber(props.collateralValue).gt(0) ? 1 : 0)
    : new BigNumber(props.collateralizationRatio).dividedBy(maxRatio);

  const stats = useCollateralRatioStats({
    colRatio: new BigNumber(props.collateralizationRatio),
    minColRatio: new BigNumber(props.minCollateralizationRatio),
    totalLoanAmount: new BigNumber(props.totalLoanAmount),
  });

  const ratioTextColor = stats.isInLiquidation
    ? "red-v2"
    : stats.isAtRisk
    ? "orange-v2"
    : "green-v2";

  return (
    <View testID={`${props.testID}_collateralization_bar`}>
      <View style={tailwind("flex-row items-start justify-between")}>
        <View style={tailwind("w-5/12")}>
          <ThemedTextV2
            style={tailwind("text-sm font-normal-v2")}
            light={tailwind("text-mono-light-v2-500")}
            dark={tailwind("text-mono-dark-v2-500")}
          >
            {translate(
              "components/CollateralizationRatioDisplay",
              "Collateral ratio"
            )}
          </ThemedTextV2>
        </View>
        {isInvalidCollateralRatio ? (
          <View style={tailwind("flex-row justify-end flex-1")}>
            {new BigNumber(props.collateralValue).gt(0) ? (
              <Text
                style={tailwind(
                  "text-sm font-normal-v2 text-green-v2 text-right"
                )}
                testID="resulting_collateralization"
              >
                {props.customReadyText ??
                  translate(
                    "components/CollateralizationRatioDisplay",
                    "Ready"
                  )}
              </Text>
            ) : (
              <ThemedTextV2
                light={tailwind("text-mono-light-v2-900")}
                dark={tailwind("text-mono-dark-v2-900")}
                style={tailwind("text-sm font-normal-v2 text-right")}
              >
                {translate("components/CollateralizationRatioDisplay", "Empty")}
              </ThemedTextV2>
            )}
          </View>
        ) : (
          <NumberFormat
            value={new BigNumber(props.collateralizationRatio).toFixed(2)}
            thousandSeparator
            displayType="text"
            suffix="%"
            renderText={(value) => (
              <Text
                testID={`${props.testID ?? ""}_col_ratio`}
                style={tailwind(
                  `text-sm font-normal-v2 text-right text-${ratioTextColor}`
                )}
              >
                {value}
              </Text>
            )}
          />
        )}
      </View>
      {props.showProgressBar && (
        <View style={tailwind("mt-2.5")}>
          <Progress.Bar
            progress={normalizedNextFactor.toNumber()}
            color={getColor(
              isInvalidCollateralRatio &&
                new BigNumber(props.collateralValue).gt(0)
                ? "green-v2"
                : ratioTextColor
            )}
            unfilledColor={getColor(
              isLight ? "mono-light-v2-200" : "mono-dark-v2-200"
            )}
            height={4}
            borderWidth={0}
            width={null}
          />
        </View>
      )}
    </View>
  );
}

function getMaxRatio(atRiskThreshold: BigNumber): number {
  const healthyScaleRatio = 0.75;
  return atRiskThreshold
    .dividedBy(new BigNumber(1).minus(healthyScaleRatio))
    .toNumber();
}