pussy-ts/src/containers/Validators/ActionBarContainer.tsx

import { useState, useEffect, useMemo } from 'react';
import { Pane, Text } from '@cybercongress/gravity';
import { coin } from '@cosmjs/launchpad';
import { useNavigate } from 'react-router-dom';
import BigNumber from 'bignumber.js';
import { useQueryClient } from 'src/contexts/queryClient';
import { useSigningClient } from 'src/contexts/signerClient';
import Button from 'src/components/btnGrd';
import { routes } from 'src/routes';
import {
  Confirmed,
  TransactionSubmitted,
  Delegate,
  ReDelegate,
  TransactionError,
  Dots,
  ActionBar,
} from '../../components';

import { trimString } from '../../utils/utils';

import { LEDGER, CYBER } from '../../utils/config';
import useDelegation from 'src/features/staking/delegation/useDelegation';
import useGetHeroes from './getHeroesHook';

const {
  STAGE_INIT,
  STAGE_READY,
  STAGE_WAIT,
  STAGE_SUBMITTED,
  STAGE_CONFIRMING,
  STAGE_CONFIRMED,
  STAGE_ERROR,
} = LEDGER;

const fee = 'auto';
const TXTYPE_DELEGATE = 0;
const TXTYPE_UNDELEGATE = 1;
const TXTYPE_REDELEGATE = 2;
const LEDGER_GENERATION = 23;

function StatusTx({ stage, clearState, errorMessage, txHash, txHeight }) {
  if (stage === LEDGER_GENERATION) {
    return (
      <ActionBar>
        tx generation <Dots big />
      </ActionBar>
    );
  }

  if (stage === STAGE_WAIT) {
    return (
      <ActionBar>
        <div>
          approve tx
          <Dots big />
        </div>
      </ActionBar>
    );
  }

  if (stage === STAGE_SUBMITTED || stage === STAGE_CONFIRMING) {
    return <TransactionSubmitted />;
  }

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

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

  return null;
}

const getValidatorAddres = (validators) => {
  let validatorAddres = null;
  if (validators.operatorAddress) {
    validatorAddres = validators.operatorAddress;
  }
  if (validators.operator_address) {
    validatorAddres = validators.operator_address;
  }

  return validatorAddres;
};

const checkAddress = (addressPocket, addressKeplr, updateState) => {
  if (addressPocket !== null && addressPocket.bech32 === addressKeplr) {
    return true;
  }
  const { setStage, setErrorMessage } = updateState;
  const trimAdd = trimString(addressKeplr, 9, 5);
  setStage(STAGE_ERROR);
  setErrorMessage(`Add address ${trimAdd} to your pocket or make active `);
  return false;
};

const checkTxs = (response, updateState) => {
  console.log('response', response);
  const { setStage, setTxHash, setErrorMessage } = updateState;
  if (response.code === 0) {
    const hash = response.transactionHash;
    console.log('hash :>> ', hash);
    setStage(STAGE_SUBMITTED);
    setTxHash(hash);
  } else {
    setStage(STAGE_ERROR);
    setTxHash(null);
    setErrorMessage(response.rawLog.toString());
  }
};

const useCheckStatusTx = (txHash, setStage, setErrorMessage, updateFnc) => {
  const queryClient = useQueryClient();
  const [txHeight, setTxHeight] = useState(null);

  useEffect(() => {
    const confirmTx = async () => {
      if (queryClient && txHash !== null) {
        setStage(STAGE_CONFIRMING);
        const response = await queryClient.getTx(txHash);
        console.log('response :>> ', response);
        if (response && response !== null) {
          if (response.code === 0) {
            setStage(STAGE_CONFIRMED);
            setTxHeight(response.height);
            if (updateFnc) {
              updateFnc();
            }
            return;
          }
          if (response.code) {
            setStage(STAGE_ERROR);
            setTxHeight(response.height);
            setErrorMessage(response.rawLog);
            return;
          }
        }
        setTimeout(confirmTx, 1500);
      }
    };
    confirmTx();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [queryClient, txHash]);

  return { txHeight };
};

function ActionBarContainer({
  addressPocket,
  validators,
  balance,
  loadingBalanceInfo,
  balanceToken,
  unStake,
  updateFnc,
}) {
  const { signer, signingClient } = useSigningClient();
  const { validators: validatorsAll } = useGetHeroes();
  const queryClient = useQueryClient();
  const navigate = useNavigate();
  const [stage, setStage] = useState(STAGE_INIT);
  const [txType, setTxType] = useState(null);
  const [errorMessage, setErrorMessage] = useState(null);
  const [txHash, setTxHash] = useState(null);
  const [amount, setAmount] = useState<string>('');
  const [valueSelect, setValueSelect] = useState('');
  const { txHeight } = useCheckStatusTx(
    txHash,
    setStage,
    setErrorMessage,
    updateFnc
  );

  const validatorSelected =
    validators.operator_address || validators.operatorAddress;

  const { data } = useDelegation(validatorSelected);
  const staked = data?.balance?.amount || 0;

  const errorState = (error) => {
    setTxHash(null);
    setStage(STAGE_ERROR);
    setErrorMessage(error.toString());
  };

  useEffect(() => {
    clearFunc();
  }, [validatorSelected]);

  const clearFunc = () => {
    setTxHash(null);
    setAmount('');
    setValueSelect('');
    setErrorMessage(null);
    setTxType(null);
    setStage(STAGE_INIT);
  };

  const delegateTokens = async () => {
    if (signer && signingClient) {
      try {
        const [{ address: addressKeplr }] = await signer.getAccounts();
        const validatorAddres = getValidatorAddres(validators);
        if (
          checkAddress(addressPocket, addressKeplr, {
            setErrorMessage,
            setStage,
          })
        ) {
          setStage(STAGE_WAIT);
          const response = await signingClient.delegateTokens(
            addressKeplr,
            validatorAddres,
            coin(amount, CYBER.DENOM_CYBER),
            fee,
            CYBER.MEMO_KEPLR
          );
          checkTxs(response, { setTxHash, setErrorMessage, setStage });
        }
      } catch (error) {
        errorState(error);
      }
    }
  };

  const undelegateTokens = async () => {
    if (signer && signingClient) {
      try {
        const [{ address: addressKeplr }] = await signer.getAccounts();

        const validatorAddres = getValidatorAddres(validators);
        if (
          checkAddress(addressPocket, addressKeplr, {
            setErrorMessage,
            setStage,
          })
        ) {
          setStage(STAGE_WAIT);
          const response = await signingClient.undelegateTokens(
            addressKeplr,
            validatorAddres,
            coin(amount, CYBER.DENOM_CYBER),
            fee,
            CYBER.MEMO_KEPLR
          );
          checkTxs(response, { setTxHash, setErrorMessage, setStage });
        }
      } catch (error) {
        errorState(error);
      }
    }
  };

  const redelegateTokens = async () => {
    if (signer && signingClient) {
      try {
        const [{ address: addressKeplr }] = await signer.getAccounts();
        if (
          checkAddress(addressPocket, addressKeplr, {
            setErrorMessage,
            setStage,
          })
        ) {
          setStage(STAGE_WAIT);
          const validatorAddres = getValidatorAddres(validators);
          const response = await signingClient.redelegateTokens(
            addressKeplr,
            validatorAddres,
            valueSelect,
            coin(amount, CYBER.DENOM_CYBER),
            fee,
            CYBER.MEMO_KEPLR
          );
          checkTxs(response, { setTxHash, setErrorMessage, setStage });
        }
      } catch (error) {
        errorState(error);
      }
    }
  };

  const claimRewards = async () => {
    if (signer && signingClient && queryClient) {
      try {
        const [{ address: addressKeplr }] = await signer.getAccounts();
        const validatorAddress: string[] = [];
        if (
          checkAddress(addressPocket, addressKeplr, {
            setErrorMessage,
            setStage,
          })
        ) {
          setStage(LEDGER_GENERATION);
          const delegationTotalRewards =
            await queryClient.delegationTotalRewards(addressKeplr);
          if (
            delegationTotalRewards !== null &&
            delegationTotalRewards.rewards
          ) {
            const { rewards } = delegationTotalRewards;
            Object.keys(rewards).forEach((key) => {
              if (rewards[key].reward !== null) {
                validatorAddress.push(rewards[key].validatorAddress);
              }
            });

            setStage(STAGE_WAIT);
            const response = await signingClient.withdrawAllRewards(
              addressKeplr,
              validatorAddress,
              fee
            );
            checkTxs(response, { setTxHash, setErrorMessage, setStage });
          }
        }
      } catch (error) {
        errorState(error);
      }
    }
  };

  const funcSetTxType = (type) => {
    setTxType(type);
    setStage(STAGE_READY);
  };

  const validRestakeBtn = parseFloat(amount) > 0 && valueSelect.length > 0;

  const validRewards = useMemo(() => {
    if (
      balance &&
      balance.delegation &&
      balance.delegation !== 0 &&
      balance.rewards &&
      balance.rewards !== 0
    ) {
      const delegation = new BigNumber(balance.delegation);
      const rewards = new BigNumber(balance.rewards);
      const procentRewards = rewards
        .div(delegation)
        .multipliedBy(100)
        .toNumber();

      if (procentRewards > 0.01) {
        return true;
      }
      return false;
    }
    return false;
  }, [balance]);

  const handleHistory = (to) => {
    navigate(to);
  };

  const amountChangeHandler = (values: string) => {
    setAmount(values);
  };

  const onClickBackToChoseHandler = () => {
    setStage(STAGE_INIT);
    setTxType(null);
    amountChangeHandler('');
  };

  // loadingBalanceInfo
  if (
    Object.keys(validators).length === 0 &&
    stage === STAGE_INIT &&
    loadingBalanceInfo
  ) {
    return (
      <ActionBar>
        <Pane fontSize="18px">
          <Dots />
        </Pane>
      </ActionBar>
    );
  }

  // stage balance.delegation === 0
  if (
    Object.keys(validators).length === 0 &&
    stage === STAGE_INIT &&
    balance?.delegation === 0
  ) {
    return (
      <ActionBar>
        <Pane fontSize="18px">Choose hero to get H and earn rewards</Pane>
      </ActionBar>
    );
  }

  // stage balance.delegation === 0
  if (Object.keys(validators).length === 0 && stage === STAGE_INIT) {
    return (
      <ActionBar>
        <Pane fontSize="18px" display="flex" alignItems="center">
          {balanceToken[CYBER.DENOM_LIQUID_TOKEN] &&
            balanceToken[CYBER.DENOM_LIQUID_TOKEN].liquid !== 0 && (
              <Pane>
                <Button
                  link={routes.hfr.path}
                  style={{
                    marginRight: 15,
                  }}
                >
                  Investmint
                </Button>
                yor free H to get A and V
              </Pane>
            )}
          {balanceToken[CYBER.DENOM_LIQUID_TOKEN].liquid === 0 &&
            balance.available !== 0 &&
            'Choose hero to get H'}
          {validRewards && (
            <Pane marginLeft={15}>
              or
              <Button
                style={{
                  marginLeft: 15,
                }}
                onClick={claimRewards}
              >
                Claim rewards
              </Button>
            </Pane>
          )}
        </Pane>
      </ActionBar>
    );
  }

  if (
    Object.keys(validators).length !== 0 &&
    stage === STAGE_INIT &&
    addressPocket !== null &&
    addressPocket.keys === 'read-only'
  ) {
    return (
      <ActionBar>
        <Pane fontSize="18px">
          this {trimString(addressPocket.bech32, 8, 6)} address is read-only
        </Pane>
      </ActionBar>
    );
  }

  if (
    Object.keys(validators).length !== 0 &&
    stage === STAGE_INIT &&
    txType === null
  ) {
    return (
      <ActionBar
        button={{
          text: 'Stake',
          onClick: () => funcSetTxType(TXTYPE_DELEGATE),
        }}
      >
        <Text fontSize="18px" color="#fff" marginRight={20} fontWeight={600}>
          {validators.description.moniker}
        </Text>
        {unStake && (
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <Button
              style={{
                margin: '0 25px',
              }}
              onClick={() => funcSetTxType(TXTYPE_UNDELEGATE)}
            >
              Unstake
            </Button>
            <Button onClick={() => funcSetTxType(TXTYPE_REDELEGATE)}>
              Restake
            </Button>
          </div>
        )}
      </ActionBar>
    );
  }

  if (
    stage === STAGE_READY &&
    (txType === TXTYPE_DELEGATE || txType === TXTYPE_UNDELEGATE)
  ) {
    return (
      <Delegate
        moniker={
          Object.keys(validators).length ? validators.description.moniker : ''
        }
        onChangeInputAmount={amountChangeHandler}
        toSend={amount}
        available={txType === TXTYPE_DELEGATE ? balance?.available : staked}
        generateTx={
          txType === TXTYPE_DELEGATE ? delegateTokens : undelegateTokens
        }
        disabledBtn={!amount}
        delegate={txType === TXTYPE_DELEGATE}
        onClickBack={onClickBackToChoseHandler}
      />
    );
  }

  if (stage === STAGE_READY && txType === TXTYPE_REDELEGATE) {
    return (
      <ReDelegate
        generateTx={() => redelegateTokens()}
        onChangeInputAmount={amountChangeHandler}
        toSend={amount}
        disabledBtn={!validRestakeBtn}
        validatorsAll={validatorsAll}
        validators={validators}
        available={staked}
        onChangeReDelegate={(e) => setValueSelect(e.target.value)}
        valueSelect={valueSelect}
        onClickBack={onClickBackToChoseHandler}
      />
    );
  }

  if (stage !== STAGE_READY) {
    return (
      <StatusTx
        stage={stage}
        clearState={clearFunc}
        errorMessage={errorMessage}
        txHash={txHash}
        txHeight={txHeight}
      />
    );
  }

  return null;
}

export default ActionBarContainer;

Synonyms

pussy-ts/src/containers/Search/ActionBarContainer.tsx
cyb/src/containers/Search/ActionBarContainer.tsx
pussy-ts/src/containers/market/actionBarContainer.tsx
cyb/src/containers/market/actionBarContainer.tsx
cyb/src/pages/Sphere/pages/components/ActionBarContainer/ActionBarContainer.tsx

Neighbours