src/pages/transactions/_components/DfTx/DfTxCompositeSwap.tsx

Summary

Maintainability
A
0 mins
Test Coverage
import {
  CompositeSwap,
  DfTx,
  PoolId,
  PoolSwap,
} from "@defichain/jellyfish-transaction";
import { fromScript } from "@defichain/jellyfish-address";
import { useNetwork } from "@contexts/NetworkContext";
import { AdaptiveList } from "@components/commons/AdaptiveList";
import { AddressLink } from "@components/commons/link/AddressLink";
import { TokenSymbol } from "@components/commons/token/TokenSymbol";
import { PoolPairSymbol } from "@components/commons/token/PoolPairSymbol";
import { IoArrowForwardOutline } from "react-icons/io5";
import { NumericFormat } from "react-number-format";
import { Transaction } from "@defichain/whale-api-client/dist/api/transactions";
import { useEffect, useState } from "react";
import BigNumber from "bignumber.js";
import { useSwapToAmount } from "hooks/useSwapToAmount";
import { DfTxHeader } from "./DfTxHeader";

interface DftxCompositeSwapProps {
  dftx: DfTx<CompositeSwap>;
  transaction?: Transaction;
}

export function DfTxCompositeSwap({
  dftx: { data },
  transaction,
}: DftxCompositeSwapProps): JSX.Element {
  const network = useNetwork().name;
  const toAddress = fromScript(data.poolSwap.toScript, network);
  const fromAddress = fromScript(data.poolSwap.fromScript, network);
  const [toAmount, setToAmount] = useState<string>();
  const getSwapToAmount = useSwapToAmount();

  useEffect(() => {
    if (transaction?.block && toAddress !== undefined) {
      const tokenId = data.poolSwap.toTokenId.toString();
      getSwapToAmount({
        tokenId,
        address: toAddress.address,
        txnHeight: transaction.block.height,
        order: transaction.order,
      }).then(setToAmount);
    }
  }, [transaction]);

  const FromTokenSymbol = (
    <TokenSymbol
      tokenId={data.poolSwap.fromTokenId}
      testId="DfTxCompositeSwap.FromTokenSymbol"
      symbolLeft
    />
  );
  const ToTokenSymbol = (
    <TokenSymbol
      tokenId={data.poolSwap.toTokenId}
      testId="DfTxCompositeSwap.ToTokenSymbol"
      symbolLeft
    />
  );

  return (
    <div>
      <DfTxHeader name="Composite Swap" />
      <div className="mt-5 flex flex-wrap items-start lg:-mx-3">
        <PoolFrom
          poolswap={data.poolSwap}
          FromTokenSymbol={FromTokenSymbol}
          address={fromAddress?.address}
        />
        <PoolTo
          poolswap={data.poolSwap}
          ToTokenSymbol={ToTokenSymbol}
          address={toAddress?.address}
          toAmount={toAmount}
        />
        <Path
          pools={data.pools}
          FromTokenSymbol={FromTokenSymbol}
          ToTokenSymbol={ToTokenSymbol}
        />
      </div>
    </div>
  );
}

function PoolFrom({
  poolswap,
  FromTokenSymbol,
  address,
}: {
  poolswap: PoolSwap;
  FromTokenSymbol: JSX.Element;
  address: string | undefined;
}): JSX.Element {
  return (
    <div
      className="w-full lg:w-1/2 lg:pl-3 lg:pr-1.5"
      data-testid="DfTxCompositeSwap.SwapFrom"
    >
      <h2
        className="my-3 font-medium dark:text-gray-100"
        data-testid="DfTxCompositeSwap.SwapFromTitle"
      >
        Swap From
      </h2>
      <AdaptiveList>
        <AdaptiveList.Row name="Address">
          {(() => {
            if (address !== undefined) {
              return (
                <AddressLink
                  address={address}
                  testId="DfTxCompositeSwap.FromAddress"
                  className="break-all"
                />
              );
            }
            return "N/A";
          })()}
        </AdaptiveList.Row>
        <AdaptiveList.Row name="Token" testId="DfTxCompositeSwap.TokenFrom">
          {FromTokenSymbol}
        </AdaptiveList.Row>
        <AdaptiveList.Row name="Amount" testId="DfTxCompositeSwap.FromAmount">
          <NumericFormat
            value={poolswap.fromAmount.toFixed(8)}
            thousandSeparator
            displayType="text"
          />
        </AdaptiveList.Row>
      </AdaptiveList>
    </div>
  );
}

function PoolTo({
  poolswap,
  ToTokenSymbol,
  address,
  toAmount,
}: {
  poolswap: PoolSwap;
  ToTokenSymbol: JSX.Element;
  address: string | undefined;
  toAmount?: string;
}): JSX.Element {
  return (
    <div
      className="w-full lg:w-1/2 lg:pr-3 lg:pl-1.5 mt-4 lg:mt-0"
      data-testid="DfTxCompositeSwap.SwapTo"
    >
      <h2
        className="my-3 font-medium dark:text-gray-100"
        data-testid="DfTxCompositeSwap.SwapToTitle"
      >
        Swap To
      </h2>
      <AdaptiveList>
        <AdaptiveList.Row name="Address">
          {(() => {
            if (address !== undefined) {
              return (
                <AddressLink
                  address={address}
                  testId="DfTxCompositeSwap.ToAddress"
                  className="break-all"
                />
              );
            }
            return "N/A";
          })()}
        </AdaptiveList.Row>
        <AdaptiveList.Row name="Token" testId="DfTxCompositeSwap.TokenTo">
          {ToTokenSymbol}
        </AdaptiveList.Row>
        <AdaptiveList.Row name="Max Price" testId="DfTxCompositeSwap.MaxPrice">
          <NumericFormat
            value={poolswap.maxPrice.toFixed(8)}
            thousandSeparator
            displayType="text"
          />
        </AdaptiveList.Row>
        <AdaptiveList.Row name="Amount" testId="DfTxCompositeSwap.toAmount">
          {toAmount === undefined ? (
            <div>pending</div>
          ) : (
            <NumericFormat
              value={new BigNumber(toAmount).toFixed(8)}
              thousandSeparator
              displayType="text"
            />
          )}
        </AdaptiveList.Row>
      </AdaptiveList>
    </div>
  );
}

function Path(props: {
  pools: PoolId[];
  FromTokenSymbol: JSX.Element;
  ToTokenSymbol: JSX.Element;
}): JSX.Element {
  return (
    <div
      className="w-full lg:px-3 mt-4"
      data-testid="DfTxCompositeSwap.SwapPath"
    >
      <h2
        className="my-3 font-medium dark:text-dark-gray-900"
        data-testid="DfTxCompositeSwap.SwapPathTitle"
      >
        Swap Path
      </h2>
      <div
        className="px-2 py-1.5 border-gray-200 border rounded-lg flex flex-wrap justify-center items-center space-x-4 dark:border-gray-700 dark:bg-gray-800"
        data-testid="DfTxCompositeSwap.SwapPathDiv"
      >
        <div className="my-1.5 dark:text-gray-100">{props.FromTokenSymbol}</div>
        <IoArrowForwardOutline size={18} className="dark:text-gray-100" />
        {props.pools.map((pool) => (
          <div
            className="inline-block flex items-center space-x-4 dark:text-gray-100"
            key={pool.id}
          >
            <PoolPairSymbol
              poolPairId={pool.id}
              symbolSizeClassName="h-6 w-6"
              symbolMarginClassName="ml-3.5"
              textClassName="ml-11"
            />
            <IoArrowForwardOutline size={18} />
          </div>
        ))}
        <div className="my-1.5 dark:text-gray-100">{props.ToTokenSymbol}</div>
      </div>
    </div>
  );
}