import React, { useEffect, useState, useCallback, useRef } from 'react';
import { useSelector } from 'react-redux';
import {
useParams,
createSearchParams,
useSearchParams,
} from 'react-router-dom';
import { Pane } from '@cybercongress/gravity';
import BigNumber from 'bignumber.js';
import useGetTotalSupply from 'src/hooks/useGetTotalSupply';
import { MainContainer } from 'src/components';
import { useQueryClient } from 'src/contexts/queryClient';
import { Pool } from '@cybercongress/cyber-js/build/codec/tendermint/liquidity/v1beta1/liquidity';
import { Option } from 'src/types';
import usePoolListInterval from 'src/hooks/usePoolListInterval';
import { useIbcDenom } from 'src/contexts/ibcDenom';
import { RootState } from 'src/redux/store';
import useGetBalances from 'src/hooks/getBalances';
import { CYBER } from '../../utils/config';
import useSetActiveAddress from '../../hooks/useSetActiveAddress';
import { reduceBalances, getDisplayAmountReverce } from '../../utils/utils';
import TabList from './components/tabList';
import { useGetSwapPrice } from '../../pages/teleport/hooks';
import { sortReserveCoinDenoms } from '../../pages/teleport/swap/utils';
import DepositCreatePool from './components/DepositCreatePool';
import Withdraw from './components/withdraw';
import ActionBar from './ActionBar';
import { TypeTab, MyPoolsT } from './type';
import {
getPoolToken,
getMyTokenBalanceNumber,
calculateCounterPairAmount,
} from './utils';
import { useAdviser } from 'src/features/adviser/context';
const tokenADefaultValue = CYBER.DENOM_CYBER;
const tokenBDefaultValue = CYBER.DENOM_LIQUID_TOKEN;
function Warp() {
const queryClient = useQueryClient();
const { traseDenom } = useIbcDenom();
const { defaultAccount } = useSelector((state: RootState) => state.pocket);
const [searchParams, setSearchParams] = useSearchParams();
const { tab = 'add-liquidity' } = useParams<{ tab: TypeTab }>();
const { addressActive } = useSetActiveAddress(defaultAccount);
const [update, setUpdate] = useState(0);
const { liquidBalances: accountBalances, refresh } =
useGetBalances(addressActive);
const { totalSupplyProofList: totalSupply } = useGetTotalSupply();
const poolsData = usePoolListInterval({ refetchInterval: 50000 });
const [tokenA, setTokenA] = useState<string>(tokenADefaultValue);
const [tokenB, setTokenB] = useState<string>(tokenBDefaultValue);
const [tokenAAmount, setTokenAAmount] = useState<string | number>('');
const [tokenBAmount, setTokenBAmount] = useState<string | number>('');
const [tokenAPoolAmount, setTokenAPoolAmount] = useState<number>(0);
const [tokenBPoolAmount, setTokenBPoolAmount] = useState<number>(0);
const [selectedPool, setSelectedPool] = useState<Pool | undefined>(undefined);
const [isExceeded, setIsExceeded] = useState<boolean>(false);
const [isEmptyPool, setIsEmptyPool] = useState<boolean>(false);
const [amountPoolCoin, setAmountPoolCoin] = useState<string | number>('');
const [myPools, setMyPools] =
useState<Option<{ [key: string]: MyPoolsT }>>(undefined);
const [selectMyPool, setSelectMyPool] = useState('');
const swapPrice = useGetSwapPrice(
tokenA,
tokenB,
tokenAPoolAmount,
tokenBPoolAmount
);
const firstEffectOccured = useRef(false);
const { setAdviser } = useAdviser();
useEffect(() => {
let text;
switch (tab) {
case 'add-liquidity':
text = 'play with pools earn more values';
break;
case 'create-pool':
text = (
<>
the unlimited number of variations. combine your favorite tokens{' '}
<br /> cultivate your values. place of cyber alchemists
</>
);
break;
case 'sub-liquidity':
text = 'manage your liquidity';
break;
default:
break;
}
setAdviser(text);
}, [setAdviser, tab]);
useEffect(() => {
if (firstEffectOccured.current) {
// eslint-disable-next-line react-hooks/exhaustive-deps
const query = {
from: tokenA,
to: tokenB,
};
setSearchParams(createSearchParams(query));
} else {
firstEffectOccured.current = true;
const param = Object.fromEntries(searchParams.entries());
if (Object.keys(param).length > 0) {
const { from, to } = param;
setTokenA(from);
setTokenB(to);
}
}
if (tab === 'sub-liquidity') {
setSearchParams(createSearchParams({}));
}
}, [tokenA, tokenB, setSearchParams, searchParams, tab]);
useEffect(() => {
// find pool for current pair
setSelectedPool(undefined);
if (!poolsData || !poolsData.length) {
return;
}
if (tokenA.length > 0 && tokenB.length > 0) {
const findPool = poolsData.find(
(item) =>
sortReserveCoinDenoms(
item.reserveCoinDenoms[0],
item.reserveCoinDenoms[1]
).join() === sortReserveCoinDenoms(tokenA, tokenB).join()
);
setSelectedPool(findPool);
}
}, [poolsData, tokenA, tokenB]);
useEffect(() => {
(async () => {
setTokenAPoolAmount(0);
setTokenBPoolAmount(0);
setIsEmptyPool(false);
if (!queryClient || !selectedPool) {
return;
}
const getAllBalancesPromise = await queryClient.getAllBalances(
selectedPool.reserveAccountAddress
);
setIsEmptyPool(!getAllBalancesPromise.length);
const dataReduceBalances = reduceBalances(getAllBalancesPromise);
setTokenAPoolAmount(dataReduceBalances[tokenA] || 0);
setTokenBPoolAmount(dataReduceBalances[tokenB] || 0);
})();
}, [queryClient, tokenA, tokenB, selectedPool, update]);
useEffect(() => {
if (accountBalances !== null && poolsData && poolsData !== null) {
const poolTokenData = getPoolToken(poolsData, accountBalances);
let poolTokenDataIndexer = {};
poolTokenDataIndexer = poolTokenData.reduce(
(obj, item) => ({
...obj,
[item.poolCoinDenom]: item,
}),
{}
);
setMyPools(poolTokenDataIndexer);
}
}, [accountBalances, poolsData]);
useEffect(() => {
let exceeded = true;
const myATokenBalance = getMyTokenBalanceNumber(tokenA, accountBalances);
const myATokenBalanceB = getMyTokenBalanceNumber(tokenB, accountBalances);
if (accountBalances !== null) {
const [{ coinDecimals: coinDecimalsA }] = traseDenom(tokenA);
const [{ coinDecimals: coinDecimalsB }] = traseDenom(tokenB);
const validTokensAB =
Object.prototype.hasOwnProperty.call(accountBalances, tokenA) &&
Object.prototype.hasOwnProperty.call(accountBalances, tokenB) &&
accountBalances[tokenA] > 0 &&
accountBalances[tokenB] > 0;
const validTokenAmountAB =
parseFloat(getDisplayAmountReverce(tokenAAmount, coinDecimalsA)) <=
myATokenBalance &&
Number(tokenAAmount) > 0 &&
parseFloat(getDisplayAmountReverce(tokenBAmount, coinDecimalsB)) <=
myATokenBalanceB &&
Number(tokenBAmount) > 0;
const resultValidSelectTokens = validTokensAB && validTokenAmountAB;
if (
tab === 'add-liquidity' &&
resultValidSelectTokens &&
swapPrice !== 0
) {
exceeded = false;
}
//valid add-liquidity in empty pool
if (
tab === 'add-liquidity' &&
isEmptyPool &&
resultValidSelectTokens &&
swapPrice === 0
) {
exceeded = false;
}
if (tab === 'create-pool' && resultValidSelectTokens) {
exceeded = false;
}
}
setIsExceeded(exceeded);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
accountBalances,
tokenA,
tokenB,
tab,
tokenAAmount,
tokenBAmount,
swapPrice,
]);
const amountChangeHandler = useCallback(
(values: string, e: React.ChangeEvent) => {
const inputAmount = values;
const isReverse = e.target.id !== 'tokenAAmount';
const state = { tokenAPoolAmount, tokenBPoolAmount, tokenB, tokenA };
let { counterPairAmount } = calculateCounterPairAmount(
inputAmount,
e,
state
);
counterPairAmount = Math.abs(
parseFloat(Number(counterPairAmount).toFixed(4))
);
if (isReverse) {
setTokenBAmount(new BigNumber(inputAmount).toNumber());
setTokenAAmount(counterPairAmount);
} else {
setTokenAAmount(new BigNumber(inputAmount).toNumber());
setTokenBAmount(counterPairAmount);
}
},
[tokenAPoolAmount, tokenBPoolAmount, tokenB, tokenA]
);
const amountChangeHandlerCreatePool = useCallback(
(values: string, e: React.ChangeEvent) => {
const inputAmount = values;
const isReverse = e.target.id !== 'tokenAAmount';
if (isReverse) {
setTokenBAmount(new BigNumber(inputAmount).toNumber());
} else {
setTokenAAmount(new BigNumber(inputAmount).toNumber());
}
},
[]
);
const onChangeInputWithdraw = (values: string) => {
const inputAmount = values;
const myATokenBalance = getMyTokenBalanceNumber(
selectMyPool,
accountBalances
);
let exceeded = true;
if (
parseFloat(inputAmount) <= myATokenBalance &&
parseFloat(inputAmount) > 0
) {
exceeded = false;
}
setIsExceeded(exceeded);
setAmountPoolCoin(new BigNumber(inputAmount).toNumber());
};
function tokenChange() {
const A = tokenB;
const B = tokenA;
const AP = tokenBPoolAmount;
const BP = tokenAPoolAmount;
setTokenA(A);
setTokenB(B);
setTokenAAmount('');
setTokenBAmount('');
setTokenAPoolAmount(AP);
setTokenBPoolAmount(BP);
}
const updateFunc = useCallback(() => {
setUpdate((item) => item + 1);
refresh();
}, [refresh]);
const stateProps = {
accountBalances,
tokenAAmount,
tokenBAmount,
tokenA,
tokenB,
setTokenA,
setTokenB,
totalSupply,
tokenChange,
};
const stateWithdraw = {
accountBalances,
myPools,
selectMyPool,
setSelectMyPool,
amountPoolCoin,
onChangeInputWithdraw,
};
const stateActionBar = {
tokenAAmount,
tokenBAmount,
tokenA,
tokenB,
selectedPool,
updateFunc,
isExceeded,
tab,
amountPoolCoin,
myPools,
selectMyPool,
};
return (
<>
<MainContainer width="62%">
<TabList selected={tab} />
<Pane
width="375px"
display="flex"
alignItems="center"
flexDirection="column"
marginX="auto"
>
<Pane
width="100%"
display="flex"
alignItems="center"
flexDirection="column"
>
{tab === 'add-liquidity' && (
<DepositCreatePool
stateProps={stateProps}
amountChangeHandler={
isEmptyPool
? amountChangeHandlerCreatePool
: amountChangeHandler
}
/>
)}
{tab === 'create-pool' && (
<DepositCreatePool
stateProps={stateProps}
amountChangeHandler={amountChangeHandlerCreatePool}
/>
)}
{tab === 'sub-liquidity' && <Withdraw stateProps={stateWithdraw} />}
</Pane>
</Pane>
{/* <TraceTxTable /> */}
</MainContainer>
<ActionBar stateActionBar={stateActionBar} />
</>
);
}
export default Warp;