cyb/src/pages/teleport/swap/actionBar.swap.tsx

import { Coin } from '@cosmjs/launchpad';
import {
  Params,
  Pool,
} from '@cybercongress/cyber-js/build/codec/tendermint/liquidity/v1beta1/liquidity';
import BigNumber from 'bignumber.js';
import { useCallback, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useIbcDenom } from 'src/contexts/ibcDenom';
import { useSigningClient } from 'src/contexts/signerClient';
import useSetActiveAddress from 'src/hooks/useSetActiveAddress';
import { useAppSelector } from 'src/redux/hooks';
import { RootState } from 'src/redux/store';
import { Option } from 'src/types';
import { Account, ActionBar as ActionBarCenter, Confirmed, TransactionError } from '../../../components';
import { LEDGER } from '../../../utils/config';
import ActionBarPingTxs from '../components/actionBarPingTxs';
import { sortReserveCoinDenoms } from './utils';
import { friendlyErrorMessage } from 'src/utils/errorMessages';

const POOL_TYPE_INDEX = 1;

const { STAGE_INIT, STAGE_ERROR, STAGE_SUBMITTED, STAGE_CONFIRMED } = LEDGER;

const coinFunc = (amount: BigNumber | string | number, denom: string): Coin => {
  return { denom, amount: new BigNumber(amount).toFixed(0) };
};

type Props = {
  tokenAAmount: string;
  tokenA: string;
  tokenB: string;
  params: undefined | Params;
  selectedPool: Pool | undefined;
  updateFunc: () => void;
  isExceeded: boolean;
  swapPrice: number;
  poolPrice: number;
};

function ActionBar({ stateActionBar }: { stateActionBar: Props }) {
  const navigate = useNavigate();
  const { defaultAccount } = useAppSelector((state: RootState) => state.pocket);
  const { addressActive } = useSetActiveAddress(defaultAccount);
  const { signingClient, signer } = useSigningClient();
  const { tracesDenom } = useIbcDenom();
  const [stage, setStage] = useState(STAGE_INIT);
  const [txHash, setTxHash] = useState<Option<string>>(undefined);
  const [txHeight, setTxHeight] = useState<Option<number>>(undefined);
  const [errorMessage, setErrorMessage] = useState<Option<string | JSX.Element>>(undefined);
  const updateFuncCalledRef = useRef(false);

  const {
    tokenAAmount,
    tokenA,
    tokenB,
    params,
    selectedPool,
    updateFunc,
    isExceeded,
    swapPrice,
    poolPrice,
  } = stateActionBar;

  const swapWithinBatch = async () => {
    if (signer && selectedPool && params && signingClient && tracesDenom) {
      const [{ address }] = await signer.getAccounts();

      const [{ coinDecimals: coinDecimalsA }] = tracesDenom(tokenA);

      const amountTokenA = new BigNumber(tokenAAmount)
        .shiftedBy(coinDecimalsA)
        .dp(0, BigNumber.ROUND_FLOOR);

      setStage(STAGE_SUBMITTED);

      const swapFeeRate = new BigNumber(params.swapFeeRate).shiftedBy(-18);
      const offerCoinFee = coinFunc(
        amountTokenA.multipliedBy(swapFeeRate).multipliedBy(0.5).dp(0, BigNumber.ROUND_CEIL),
        tokenA
      );

      const offerCoin = coinFunc(amountTokenA, tokenA);
      const demandCoinDenom = tokenB;

      const exp = new BigNumber(10).pow(18).toString();
      const convertSwapPrice = new BigNumber(swapPrice)
        .multipliedBy(exp)
        .dp(0, BigNumber.ROUND_FLOOR)
        .toString(10);
      if (addressActive !== null && addressActive.bech32 === address) {
        try {
          const response = await signingClient.swapWithinBatch(
            address,
            selectedPool.id,
            POOL_TYPE_INDEX,
            offerCoin,
            demandCoinDenom,
            offerCoinFee,
            convertSwapPrice,
            'auto'
          );

          if (response.code === 0) {
            setTxHash(response.transactionHash);
            setTxHeight(response.height);
            setStage(STAGE_CONFIRMED);
            if (!updateFuncCalledRef.current) {
              updateFuncCalledRef.current = true;
              updateFunc();
            }
          } else {
            setTxHash(undefined);
            setErrorMessage(friendlyErrorMessage(response.rawLog));
            setStage(STAGE_ERROR);
          }
        } catch (error) {
          setTxHash(undefined);
          setErrorMessage(friendlyErrorMessage(error?.message || error));
          setStage(STAGE_ERROR);
        }
      } else {
        setErrorMessage(
          <span>
            Add address <Account margin="0 5px" address={address} /> to your pocket or make active{' '}
          </span>
        );
        setStage(STAGE_ERROR);
      }
    }
  };

  const clearState = () => {
    setStage(STAGE_INIT);
    setTxHash(undefined);
    setTxHeight(undefined);
    setErrorMessage(undefined);
    updateFuncCalledRef.current = false;
  };

  const createPool = useCallback(() => {
    const sortCoin = sortReserveCoinDenoms(tokenA, tokenB);
    navigate(`/warp/create-pool?from=${sortCoin[0]}&to=${sortCoin[1]}`);
  }, [tokenA, tokenB, navigate]);

  if (!selectedPool && stage === STAGE_INIT) {
    return (
      <ActionBarCenter
        button={{
          text: 'Create pool',
          onClick: createPool,
        }}
      />
    );
  }

  if (stage === STAGE_INIT) {
    return (
      <ActionBarCenter
        button={{
          text: 'Swap',
          onClick: swapWithinBatch,
          disabled: isExceeded,
        }}
      />
    );
  }

  if (stage === STAGE_CONFIRMED) {
    return <Confirmed txHash={txHash} txHeight={txHeight} onClickBtnClose={() => clearState()} />;
  }

  if (stage === STAGE_ERROR) {
    return (
      <TransactionError
        errorMessage={errorMessage}
        onClickBtn={() => clearState()}
      />
    );
  }

  const stageActionBarStaps = {
    stage,
    setStage,
    clearState,
    updateFunc,
    txHash,
    errorMessageProps: errorMessage,
  };

  return <ActionBarPingTxs stageActionBarStaps={stageActionBarStaps} />;
}

export default ActionBar;

Synonyms

pussy-ts/src/pages/teleport/swap/actionBar.swap.tsx

Neighbours