components#CurrencyLogo TypeScript Examples

The following examples show how to use components#CurrencyLogo. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: DoubleCurrencyLogo.tsx    From interface-v2 with GNU General Public License v3.0 6 votes
DoubleCurrencyLogo: React.FC<DoubleCurrencyLogoProps> = ({
  currency0,
  currency1,
  size = 16,
  margin = false,
}: DoubleCurrencyLogoProps) => {
  const classes = useStyles({ size, margin });
  return (
    <Box className={classes.wrapper}>
      <CurrencyLogo currency={currency0} size={size.toString() + 'px'} />
      <CurrencyLogo currency={currency1} size={size.toString() + 'px'} />
    </Box>
  );
}
Example #2
Source File: SyrupAPR.tsx    From interface-v2 with GNU General Public License v3.0 6 votes
SyrupAPR: React.FC<{ syrup: SyrupInfo; dQUICKAPY: string }> = ({
  syrup,
  dQUICKAPY,
}) => {
  const { palette } = useTheme();
  const isDQUICKStakingToken = syrup.stakingToken.equals(
    returnTokenFromKey('DQUICK'),
  );

  return (
    <>
      <Typography variant='body2' style={{ color: palette.success.main }}>
        {getTokenAPRSyrup(syrup).toLocaleString()}%
      </Typography>
      {isDQUICKStakingToken && (
        <Box display='flex'>
          <Box
            borderRadius='4px'
            border={`1px solid ${palette.grey.A400}`}
            padding='4px'
            marginTop='4px'
            display='flex'
            alignItems='center'
          >
            <CurrencyLogo currency={returnTokenFromKey('QUICK')} size='12px' />
            <Typography variant='caption' style={{ marginLeft: 4 }}>
              {dQUICKAPY}% <span style={{ color: palette.text.hint }}>APY</span>
            </Typography>
          </Box>
        </Box>
      )}
    </>
  );
}
Example #3
Source File: CommonBases.tsx    From interface-v2 with GNU General Public License v3.0 5 votes
CommonBases: React.FC<CommonBasesProps> = ({
  chainId,
  onSelect,
  selectedCurrency,
}) => {
  const classes = useStyles();
  return (
    <Box mb={2}>
      <Box display='flex' className={classes.title} my={1.5}>
        <Typography variant='caption'>Common bases</Typography>
        <QuestionHelper text='These tokens are commonly paired with other tokens.' />
      </Box>
      <Box display='flex' flexWrap='wrap'>
        <Box
          className={classes.baseWrapper}
          onClick={() => {
            if (!selectedCurrency || !currencyEquals(selectedCurrency, ETHER)) {
              onSelect(ETHER);
            }
          }}
        >
          <CurrencyLogo currency={ETHER} size='24px' />
          <Typography variant='body2'>MATIC</Typography>
        </Box>
        {(chainId ? GlobalData.bases.SUGGESTED_BASES[chainId] : []).map(
          (token: Token) => {
            const selected =
              selectedCurrency instanceof Token &&
              selectedCurrency.address === token.address;
            return (
              <Box
                className={classes.baseWrapper}
                key={token.address}
                onClick={() => !selected && onSelect(token)}
              >
                <CurrencyLogo currency={token} size='24px' />
                <Typography variant='body2'>{token.symbol}</Typography>
              </Box>
            );
          },
        )}
      </Box>
    </Box>
  );
}
Example #4
Source File: DragonsLair.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
DragonsLair: React.FC = () => {
  const classes = useStyles();
  const { palette } = useTheme();
  const quickPrice = useUSDCPriceToken(returnTokenFromKey('QUICK'));
  const dQUICKPrice = useUSDCPriceToken(returnTokenFromKey('DQUICK'));
  const dQUICKtoQUICK = dQUICKPrice / quickPrice;
  const QUICKtodQUICK = quickPrice / dQUICKPrice;
  const [isQUICKRate, setIsQUICKRate] = useState(false);
  const [openStakeModal, setOpenStakeModal] = useState(false);
  const [openUnstakeModal, setOpenUnstakeModal] = useState(false);
  const lairInfo = useLairInfo();
  const APY = useLairDQUICKAPY(lairInfo);

  return (
    <Box position='relative' zIndex={3}>
      {openStakeModal && (
        <StakeQuickModal
          open={openStakeModal}
          onClose={() => setOpenStakeModal(false)}
        />
      )}
      {openUnstakeModal && (
        <UnstakeQuickModal
          open={openUnstakeModal}
          onClose={() => setOpenUnstakeModal(false)}
        />
      )}
      <Box display='flex'>
        <CurrencyLogo currency={returnTokenFromKey('QUICK')} size='32px' />
        <Box ml={1.5}>
          <Typography
            variant='body2'
            style={{ color: palette.text.primary, lineHeight: 1 }}
          >
            QUICK
          </Typography>
          <Typography variant='caption' style={{ color: palette.text.hint }}>
            Single Stake — Auto compounding
          </Typography>
        </Box>
      </Box>
      <Box display='flex' justifyContent='space-between' mt={1.5}>
        <Typography variant='body2'>Total QUICK</Typography>
        <Typography variant='body2'>
          {lairInfo
            ? lairInfo.totalQuickBalance.toFixed(2, {
                groupSeparator: ',',
              })
            : 0}
        </Typography>
      </Box>
      <Box display='flex' justifyContent='space-between' mt={1.5}>
        <Typography variant='body2'>TVL:</Typography>
        <Typography variant='body2'>
          $
          {(
            Number(lairInfo.totalQuickBalance.toExact()) * quickPrice
          ).toLocaleString()}
        </Typography>
      </Box>
      <Box display='flex' justifyContent='space-between' mt={1.5}>
        <Typography variant='body2'>APY</Typography>
        <Typography variant='body2' style={{ color: palette.success.main }}>
          {APY}%
        </Typography>
      </Box>
      <Box display='flex' justifyContent='space-between' mt={1.5}>
        <Typography variant='body2'>Your Deposits</Typography>
        <Typography variant='body2'>
          {formatTokenAmount(lairInfo.QUICKBalance)}
        </Typography>
      </Box>
      <Box
        mt={2.5}
        width={1}
        height='40px'
        display='flex'
        alignItems='center'
        justifyContent='center'
        borderRadius={10}
        border={`1px solid ${palette.secondary.light}`}
      >
        <CurrencyLogo currency={returnTokenFromKey('QUICK')} />
        <Typography variant='body2' style={{ margin: '0 8px' }}>
          {isQUICKRate ? 1 : dQUICKtoQUICK.toLocaleString()} QUICK =
        </Typography>
        <CurrencyLogo currency={returnTokenFromKey('QUICK')} />
        <Typography variant='body2' style={{ margin: '0 8px' }}>
          {isQUICKRate ? QUICKtodQUICK.toLocaleString() : 1} dQUICK
        </Typography>
        <PriceExchangeIcon
          style={{ cursor: 'pointer' }}
          onClick={() => setIsQUICKRate(!isQUICKRate)}
        />
      </Box>
      <Box
        className={classes.stakeButton}
        bgcolor={palette.primary.main}
        onClick={() => setOpenStakeModal(true)}
      >
        <Typography variant='body2'>Stake</Typography>
      </Box>
      <Box
        className={classes.stakeButton}
        bgcolor='transparent'
        onClick={() => setOpenUnstakeModal(true)}
      >
        <Typography variant='body2'>Unstake</Typography>
      </Box>
      <Box mt={3} textAlign='center'>
        <Typography
          variant='caption'
          style={{ color: palette.text.secondary, fontWeight: 500 }}
        >
          ⭐️ When you unstake, the contract will automatically claim QUICK on
          your behalf.
        </Typography>
      </Box>
    </Box>
  );
}
Example #5
Source File: CurrencyInput.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
CurrencyInput: React.FC<CurrencyInputProps> = ({
  handleCurrencySelect,
  currency,
  otherCurrency,
  amount,
  setAmount,
  onMax,
  onHalf,
  showMaxButton,
  showHalfButton,
  title,
  showPrice,
  bgColor,
  id,
}) => {
  const classes = useStyles();
  const { palette } = useTheme();
  const [modalOpen, setModalOpen] = useState(false);
  const { account } = useActiveWeb3React();
  const selectedCurrencyBalance = useCurrencyBalance(
    account ?? undefined,
    currency,
  );
  const usdPrice = Number(useUSDCPrice(currency)?.toSignificant() ?? 0);

  const handleOpenModal = useCallback(() => {
    setModalOpen(true);
  }, [setModalOpen]);

  return (
    <Box
      id={id}
      className={cx(classes.swapBox, showPrice && classes.priceShowBox)}
      bgcolor={bgColor ?? palette.secondary.dark}
    >
      <Box display='flex' justifyContent='space-between' mb={2}>
        <Typography>{title || 'You Pay:'}</Typography>
        <Box display='flex'>
          {account && currency && showHalfButton && (
            <Box className='maxWrapper' onClick={onHalf}>
              <Typography variant='body2'>50%</Typography>
            </Box>
          )}
          {account && currency && showMaxButton && (
            <Box className='maxWrapper' marginLeft='20px' onClick={onMax}>
              <Typography variant='body2'>MAX</Typography>
            </Box>
          )}
        </Box>
      </Box>
      <Box mb={2}>
        <Box
          className={cx(
            classes.currencyButton,
            currency ? classes.currencySelected : classes.noCurrency,
          )}
          onClick={handleOpenModal}
        >
          {currency ? (
            <>
              <CurrencyLogo currency={currency} size={'28px'} />
              <Typography className='token-symbol-container' variant='body1'>
                {currency?.symbol}
              </Typography>
            </>
          ) : (
            <Typography variant='body1'>Select a token</Typography>
          )}
        </Box>
        <Box className='inputWrapper'>
          <NumericalInput
            value={amount}
            align='right'
            color={palette.text.secondary}
            placeholder='0.00'
            onUserInput={(val) => {
              setAmount(val);
            }}
          />
        </Box>
      </Box>
      <Box
        display='flex'
        justifyContent='space-between'
        className={classes.balanceSection}
      >
        <Typography variant='body2'>
          Balance: {formatTokenAmount(selectedCurrencyBalance)}
        </Typography>
        <Typography variant='body2'>
          ${(usdPrice * Number(amount)).toLocaleString()}
        </Typography>
      </Box>
      {modalOpen && (
        <CurrencySearchModal
          isOpen={modalOpen}
          onDismiss={() => {
            setModalOpen(false);
          }}
          onCurrencySelect={handleCurrencySelect}
          selectedCurrency={currency}
          showCommonBases={true}
          otherSelectedCurrency={otherCurrency}
        />
      )}
    </Box>
  );
}
Example #6
Source File: AnalyticsTokenDetails.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
AnalyticsTokenDetails: React.FC = () => {
  const classes = useStyles();
  const { palette, breakpoints } = useTheme();
  const isMobile = useMediaQuery(breakpoints.down('xs'));
  const history = useHistory();
  const match = useRouteMatch<{ id: string }>();
  const tokenAddress = match.params.id;
  const [token, setToken] = useState<any>(null);
  const { chainId } = useActiveWeb3React();
  const currency = token
    ? new Token(ChainId.MATIC, getAddress(token.id), token.decimals)
    : undefined;
  const [tokenPairs, updateTokenPairs] = useState<any>(null);
  const {
    bookmarkTokens,
    addBookmarkToken,
    removeBookmarkToken,
  } = useBookmarkTokens();

  useEffect(() => {
    async function fetchTokenInfo() {
      const [newPrice, oneDayPrice] = await getEthPrice();
      const tokenInfo = await getTokenInfo(newPrice, oneDayPrice, tokenAddress);
      if (tokenInfo) {
        setToken(tokenInfo[0]);
      }
    }
    fetchTokenInfo();
  }, [tokenAddress]);

  useEffect(() => {
    async function fetchTokenPairs() {
      const [newPrice] = await getEthPrice();
      const tokenPairs = await getTokenPairs2(tokenAddress);
      const formattedPairs = tokenPairs
        ? tokenPairs.map((pair: any) => {
            return pair.id;
          })
        : [];
      const pairData = await getBulkPairData(formattedPairs, newPrice);
      if (pairData) {
        updateTokenPairs(pairData);
      }
    }
    fetchTokenPairs();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [updateTokenPairs, tokenAddress]);

  const tokenPercentColor = getPriceColor(
    token ? Number(token.priceChangeUSD) : 0,
    palette,
  );

  return (
    <>
      <AnalyticsHeader type='token' data={token} />
      {token ? (
        <>
          <Box
            width={1}
            display='flex'
            flexWrap='wrap'
            justifyContent='space-between'
          >
            <Box display='flex'>
              <CurrencyLogo currency={currency} size='32px' />
              <Box ml={1.5}>
                <Box display='flex' alignItems='center'>
                  <Box display='flex' alignItems='flex-end' mr={0.5}>
                    <Typography className={classes.heading1}>
                      {token.name}{' '}
                    </Typography>
                    <Typography className={classes.heading2}>
                      ({token.symbol})
                    </Typography>
                  </Box>
                  {bookmarkTokens.includes(token.id) ? (
                    <StarChecked
                      onClick={() => removeBookmarkToken(token.id)}
                    />
                  ) : (
                    <StarUnchecked onClick={() => addBookmarkToken(token.id)} />
                  )}
                </Box>
                <Box mt={1.25} display='flex' alignItems='center'>
                  <Typography
                    variant='h5'
                    style={{ color: palette.text.primary }}
                  >
                    ${formatNumber(token.priceUSD)}
                  </Typography>
                  <Box
                    className={classes.priceChangeWrapper}
                    ml={2}
                    bgcolor={tokenPercentColor.bgColor}
                    color={tokenPercentColor.textColor}
                  >
                    <Typography variant='body2'>
                      {getFormattedPrice(Number(token.priceChangeUSD))}%
                    </Typography>
                  </Box>
                </Box>
              </Box>
            </Box>
            <Box my={2} display='flex'>
              <Box
                className={classes.button}
                mr={1.5}
                border={`1px solid ${palette.primary.main}`}
                onClick={() => {
                  history.push(`/pools?currency0=${token.id}&currency1=ETH`);
                }}
              >
                <Typography variant='body2'>Add Liquidity</Typography>
              </Box>
              <Box
                className={cx(classes.button, classes.filledButton)}
                onClick={() => {
                  history.push(`/swap?currency0=${token.id}&currency1=ETH`);
                }}
              >
                <Typography variant='body2'>Swap</Typography>
              </Box>
            </Box>
          </Box>
          <Box width={1} className={classes.panel} mt={4}>
            <Grid container>
              <Grid item xs={12} sm={12} md={6}>
                <AnalyticsTokenChart token={token} />
              </Grid>
              <Grid item xs={12} sm={12} md={6}>
                <Box
                  my={2}
                  height={1}
                  display='flex'
                  flexDirection='column'
                  alignItems='center'
                  justifyContent='center'
                >
                  <Box
                    width={isMobile ? 1 : 0.8}
                    display='flex'
                    justifyContent='space-between'
                  >
                    <Box width={180}>
                      <Typography
                        variant='caption'
                        style={{ color: palette.text.disabled }}
                      >
                        TOTAL LIQUIDITY
                      </Typography>
                      <Typography variant={isMobile ? 'body1' : 'h5'}>
                        ${token.totalLiquidityUSD.toLocaleString()}
                      </Typography>
                    </Box>
                    <Box width={140}>
                      <Typography
                        variant='caption'
                        style={{ color: palette.text.disabled }}
                      >
                        7d Trading Vol
                      </Typography>
                      <Typography variant={isMobile ? 'body1' : 'h5'}>
                        ${token.oneWeekVolumeUSD.toLocaleString()}
                      </Typography>
                    </Box>
                  </Box>
                  <Box
                    width={isMobile ? 1 : 0.8}
                    mt={4}
                    display='flex'
                    justifyContent='space-between'
                  >
                    <Box width={180}>
                      <Typography
                        variant='caption'
                        style={{ color: palette.text.disabled }}
                      >
                        24h Trading Vol
                      </Typography>
                      <Typography variant={isMobile ? 'body1' : 'h5'}>
                        ${token.oneDayVolumeUSD.toLocaleString()}
                      </Typography>
                    </Box>
                    <Box width={140}>
                      <Typography
                        variant='caption'
                        style={{ color: palette.text.disabled }}
                      >
                        24h FEES
                      </Typography>
                      <Typography variant={isMobile ? 'body1' : 'h5'}>
                        $
                        {(
                          token.oneDayVolumeUSD * GlobalConst.utils.FEEPERCENT
                        ).toLocaleString()}
                      </Typography>
                    </Box>
                  </Box>
                  <Box
                    width={isMobile ? 1 : 0.8}
                    mt={4}
                    display='flex'
                    justifyContent='space-between'
                  >
                    <Box width={180}>
                      <Typography
                        variant='caption'
                        style={{ color: palette.text.disabled }}
                      >
                        Contract Address
                      </Typography>
                      <Typography
                        variant='h5'
                        style={{ color: palette.primary.main }}
                      >
                        {chainId ? (
                          <a
                            href={getEtherscanLink(
                              chainId,
                              token.id,
                              'address',
                            )}
                            target='_blank'
                            rel='noopener noreferrer'
                            style={{
                              color: palette.primary.main,
                              textDecoration: 'none',
                            }}
                          >
                            {shortenAddress(token.id)}
                          </a>
                        ) : (
                          shortenAddress(token.id)
                        )}
                      </Typography>
                    </Box>
                  </Box>
                </Box>
              </Grid>
            </Grid>
          </Box>
          <Box width={1} mt={5}>
            <Typography variant='body1'>{token.symbol} Pools</Typography>
          </Box>
          <Box width={1} className={classes.panel} mt={4}>
            {tokenPairs ? (
              <PairTable data={tokenPairs} />
            ) : (
              <Skeleton variant='rect' width='100%' height={150} />
            )}
          </Box>
        </>
      ) : (
        <Skeleton width='100%' height={100} />
      )}
    </>
  );
}
Example #7
Source File: AnalyticsPairDetails.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
AnalyticsPairDetails: React.FC = () => {
  const classes = useStyles();
  const { palette, breakpoints } = useTheme();
  const isMobile = useMediaQuery(breakpoints.down('xs'));
  const history = useHistory();
  const match = useRouteMatch<{ id: string }>();
  const pairAddress = match.params.id;
  const [pairData, setPairData] = useState<any>(null);
  const [pairTransactions, setPairTransactions] = useState<any>(null);
  const pairTransactionsList = useMemo(() => {
    if (pairTransactions) {
      const mints = pairTransactions.mints.map((item: any) => {
        return { ...item, type: TxnType.ADD };
      });
      const swaps = pairTransactions.swaps.map((item: any) => {
        const amount0 = item.amount0Out > 0 ? item.amount0Out : item.amount1Out;
        const amount1 = item.amount0In > 0 ? item.amount0In : item.amount1In;
        const token0 =
          item.amount0Out > 0 ? item.pair.token0 : item.pair.token1;
        const token1 =
          item.amount0Out > 0 ? item.pair.token1 : item.pair.token0;
        return {
          ...item,
          amount0,
          amount1,
          pair: { token0, token1 },
          type: TxnType.SWAP,
        };
      });
      const burns = pairTransactions.burns.map((item: any) => {
        return { ...item, type: TxnType.REMOVE };
      });
      return mints.concat(swaps).concat(burns);
    } else {
      return null;
    }
  }, [pairTransactions]);
  const { chainId } = useActiveWeb3React();
  const currency0 = pairData
    ? new Token(
        ChainId.MATIC,
        getAddress(pairData.token0.id),
        pairData.token0.decimals,
      )
    : undefined;
  const currency1 = pairData
    ? new Token(
        ChainId.MATIC,
        getAddress(pairData.token1.id),
        pairData.token1.decimals,
      )
    : undefined;

  const token0Rate =
    pairData && pairData.reserve0 && pairData.reserve1
      ? Number(pairData.reserve1) / Number(pairData.reserve0) >= 0.0001
        ? (Number(pairData.reserve1) / Number(pairData.reserve0)).toFixed(
            Number(pairData.reserve1) / Number(pairData.reserve0) > 1 ? 2 : 4,
          )
        : '< 0.0001'
      : '-';
  const token1Rate =
    pairData && pairData.reserve0 && pairData.reserve1
      ? Number(pairData.reserve0) / Number(pairData.reserve1) >= 0.0001
        ? (Number(pairData.reserve0) / Number(pairData.reserve1)).toFixed(
            Number(pairData.reserve0) / Number(pairData.reserve1) > 1 ? 2 : 4,
          )
        : '< 0.0001'
      : '-';
  const usingUtVolume =
    pairData &&
    pairData.oneDayVolumeUSD === 0 &&
    !!pairData.oneDayVolumeUntracked;
  const fees =
    pairData && (pairData.oneDayVolumeUSD || pairData.oneDayVolumeUSD === 0)
      ? usingUtVolume
        ? (
            Number(pairData.oneDayVolumeUntracked) *
            GlobalConst.utils.FEEPERCENT
          ).toLocaleString()
        : (
            Number(pairData.oneDayVolumeUSD) * GlobalConst.utils.FEEPERCENT
          ).toLocaleString()
      : '-';

  useEffect(() => {
    async function checkEthPrice() {
      const [newPrice] = await getEthPrice();
      const pairInfo = await getBulkPairData([pairAddress], newPrice);
      if (pairInfo && pairInfo.length > 0) {
        setPairData(pairInfo[0]);
      }
    }
    async function fetchTransctions() {
      const transactions = await getPairTransactions(pairAddress);
      if (transactions) {
        setPairTransactions(transactions);
      }
    }
    checkEthPrice();
    fetchTransctions();
  }, [pairAddress]);

  return (
    <>
      <AnalyticsHeader type='pair' data={pairData} />
      {pairData ? (
        <>
          <Box
            width={1}
            display='flex'
            flexWrap='wrap'
            justifyContent='space-between'
          >
            <Box>
              <Box display='flex' alignItems='center'>
                <DoubleCurrencyLogo
                  currency0={currency0}
                  currency1={currency1}
                  size={32}
                />
                <Box ml={1}>
                  <Typography className={classes.heading2}>
                    <Link to={`/analytics/token/${pairData.token0.id}`}>
                      {pairData.token0.symbol}
                    </Link>{' '}
                    /{' '}
                    <Link to={`/analytics/token/${pairData.token1.id}`}>
                      {pairData.token1.symbol}
                    </Link>
                  </Typography>
                </Box>
              </Box>
              <Box mt={2} display='flex'>
                <Box
                  paddingY={0.75}
                  paddingX={1.5}
                  borderRadius={20}
                  display='flex'
                  alignItems='center'
                  bgcolor={palette.grey.A700}
                >
                  <CurrencyLogo currency={currency0} size='16px' />
                  <Typography
                    variant='body2'
                    color='textPrimary'
                    style={{ marginLeft: 6 }}
                  >
                    1 {pairData.token0.symbol} = {token0Rate}{' '}
                    {pairData.token1.symbol}
                  </Typography>
                </Box>
                <Box
                  padding={0.75}
                  paddingX={1.5}
                  ml={2}
                  borderRadius={20}
                  display='flex'
                  alignItems='center'
                  bgcolor={palette.grey.A700}
                >
                  <CurrencyLogo currency={currency1} size='16px' />
                  <Typography
                    variant='body2'
                    color='textPrimary'
                    style={{ marginLeft: 6 }}
                  >
                    1 {pairData.token1.symbol} = {token1Rate}{' '}
                    {pairData.token0.symbol}
                  </Typography>
                </Box>
              </Box>
            </Box>
            <Box my={2} display='flex'>
              <Box
                className={classes.button}
                mr={1.5}
                border={`1px solid ${palette.primary.main}`}
                onClick={() => {
                  history.push(
                    `/pools?currency0=${pairData.token0.id}&currency1=${pairData.token1.id}`,
                  );
                }}
              >
                <Typography variant='body2'>Add Liquidity</Typography>
              </Box>
              <Box
                className={cx(classes.button, classes.filledButton)}
                onClick={() => {
                  history.push(
                    `/swap?currency0=${pairData.token0.id}&currency1=${pairData.token1.id}`,
                  );
                }}
              >
                <Typography variant='body2'>Swap</Typography>
              </Box>
            </Box>
          </Box>
          <Box width={1} className={classes.panel} mt={4}>
            <Grid container>
              <Grid item xs={12} sm={12} md={6}>
                <AnalyticsPairChart pairData={pairData} />
              </Grid>
              <Grid item xs={12} sm={12} md={6}>
                <Box
                  my={2}
                  height={1}
                  display='flex'
                  justifyContent='center'
                  alignItems='center'
                >
                  <Box
                    width={isMobile ? 1 : 0.8}
                    display='flex'
                    justifyContent='space-between'
                  >
                    <Box width={212}>
                      <Box>
                        <Typography
                          variant='caption'
                          style={{ color: palette.text.disabled }}
                        >
                          TOTAL TOKENS LOCKED
                        </Typography>
                        <Box
                          mt={1.5}
                          bgcolor={palette.grey.A400}
                          borderRadius={8}
                          padding={1.5}
                        >
                          <Box
                            display='flex'
                            alignItems='center'
                            justifyContent='space-between'
                          >
                            <Box display='flex' alignItems='center'>
                              <CurrencyLogo currency={currency0} size='16px' />
                              <Typography
                                variant='caption'
                                color='textPrimary'
                                style={{ marginLeft: 6 }}
                              >
                                {pairData.token0.symbol} :
                              </Typography>
                            </Box>
                            <Typography variant='caption' color='textPrimary'>
                              {Number(pairData.reserve0).toLocaleString()}
                            </Typography>
                          </Box>
                          <Box
                            mt={1}
                            display='flex'
                            alignItems='center'
                            justifyContent='space-between'
                          >
                            <Box display='flex' alignItems='center'>
                              <CurrencyLogo currency={currency1} size='16px' />
                              <Typography
                                variant='caption'
                                color='textPrimary'
                                style={{ marginLeft: 6 }}
                              >
                                {pairData.token1.symbol} :
                              </Typography>
                            </Box>
                            <Typography variant='caption' color='textPrimary'>
                              {Number(pairData.reserve1).toLocaleString()}
                            </Typography>
                          </Box>
                        </Box>
                      </Box>
                      <Box mt={4}>
                        <Typography
                          variant='caption'
                          style={{ color: palette.text.disabled }}
                        >
                          7d Trading Vol
                        </Typography>
                        <Typography variant={isMobile ? 'body1' : 'h5'}>
                          ${pairData.oneWeekVolumeUSD.toLocaleString()}
                        </Typography>
                      </Box>
                      <Box mt={4}>
                        <Typography
                          variant='caption'
                          style={{ color: palette.text.disabled }}
                        >
                          24h FEES
                        </Typography>
                        <Typography variant={isMobile ? 'body1' : 'h5'}>
                          ${fees}
                        </Typography>
                      </Box>
                    </Box>
                    <Box width={140}>
                      <Typography
                        variant='caption'
                        style={{ color: palette.text.disabled }}
                      >
                        TOTAL LIQUIDITY
                      </Typography>
                      <Typography variant={isMobile ? 'body1' : 'h5'}>
                        $
                        {Number(
                          pairData.reserveUSD
                            ? pairData.reserveUSD
                            : pairData.trackedReserveUSD,
                        ).toLocaleString()}
                      </Typography>
                      <Box mt={4}>
                        <Typography
                          variant='caption'
                          style={{ color: palette.text.disabled }}
                        >
                          24h Trading Vol
                        </Typography>
                        <Typography variant={isMobile ? 'body1' : 'h5'}>
                          ${pairData.oneDayVolumeUSD.toLocaleString()}
                        </Typography>
                      </Box>
                      <Box mt={4}>
                        <Typography
                          variant='caption'
                          style={{ color: palette.text.disabled }}
                        >
                          Contract Address
                        </Typography>
                        <Typography
                          variant='h5'
                          style={{ color: palette.primary.main }}
                        >
                          {chainId ? (
                            <a
                              href={getEtherscanLink(
                                chainId,
                                pairData.id,
                                'address',
                              )}
                              target='_blank'
                              rel='noopener noreferrer'
                              style={{
                                color: palette.primary.main,
                                textDecoration: 'none',
                              }}
                            >
                              {shortenAddress(pairData.id)}
                            </a>
                          ) : (
                            shortenAddress(pairData.id)
                          )}
                        </Typography>
                      </Box>
                    </Box>
                  </Box>
                </Box>
              </Grid>
            </Grid>
          </Box>
          <Box width={1} mt={5}>
            <Typography variant='body1'>Transactions</Typography>
          </Box>
          <Box width={1} className={classes.panel} mt={4}>
            {pairTransactionsList ? (
              <TransactionsTable data={pairTransactionsList} />
            ) : (
              <Skeleton variant='rect' width='100%' height={150} />
            )}
          </Box>
        </>
      ) : (
        <Skeleton width='100%' height={100} />
      )}
    </>
  );
}
Example #8
Source File: TopMovers.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
TopMovers: React.FC<TopMoversProps> = ({
  background,
  hideArrow = false,
}) => {
  const classes = useStyles();
  const { palette, breakpoints } = useTheme();
  const [topTokens, updateTopTokens] = useState<any[] | null>(null);
  const smallWindowSize = useMediaQuery(breakpoints.down('xs'));

  const topMoverTokens = useMemo(
    () => (topTokens && topTokens.length >= 5 ? topTokens.slice(0, 5) : null),
    [topTokens],
  );

  useEffect(() => {
    async function fetchTopTokens() {
      const [newPrice, oneDayPrice] = await getEthPrice();
      const topTokensData = await getTopTokens(newPrice, oneDayPrice, 5);
      if (topTokensData) {
        updateTopTokens(topTokensData);
      }
    }
    fetchTopTokens();
  }, [updateTopTokens]);

  return (
    <Box
      width='100%'
      display='flex'
      flexWrap='wrap'
      flexDirection='column'
      justifyContent='center'
      alignItems={smallWindowSize ? 'center' : 'flex-start'}
      bgcolor={background}
      border={`1px solid ${
        background === 'transparent' ? palette.background.paper : 'transparent'
      }`}
      borderRadius={10}
      px={2.5}
      pt={2.5}
      pb={0.5}
    >
      <Typography variant='h6' style={{ color: palette.text.secondary }}>
        24h TOP MOVERS
      </Typography>
      <Box width={1} pb={2} style={{ overflowX: 'auto' }}>
        {topMoverTokens ? (
          <Box className={classes.content}>
            {topMoverTokens.map((token: any, index: number) => {
              const currency = new Token(
                ChainId.MATIC,
                getAddress(token.id),
                token.decimals,
              );
              const priceColor = getPriceColor(
                Number(token.priceChangeUSD),
                palette,
              );
              const priceUp = Number(token.priceChangeUSD) > 0;
              const priceDown = Number(token.priceChangeUSD) < 0;
              const priceUpPercent = Number(token.priceChangeUSD).toFixed(2);
              return (
                <Box
                  mr={index < topMoverTokens.length ? 2 : 0}
                  width={smallWindowSize ? 180 : 'unset'}
                  mt={2}
                  key={token.id}
                  display='flex'
                  flexDirection='row'
                  justifyContent={smallWindowSize ? 'flex-start' : 'center'}
                  alignItems='center'
                >
                  <CurrencyLogo currency={currency} size='28px' />
                  <Box ml={1}>
                    <Typography variant='body2' style={{ fontWeight: 'bold' }}>
                      {token.symbol}
                    </Typography>
                    <Box
                      display='flex'
                      flexDirection='row'
                      justifyContent='center'
                      alignItems='center'
                    >
                      <Typography variant='body2'>
                        ${formatNumber(token.priceUSD)}
                      </Typography>
                      <Box
                        ml={hideArrow ? 1 : 0}
                        display='flex'
                        flexDirection='row'
                        justifyContent='center'
                        alignItems='center'
                        px={0.75}
                        py={0.25}
                        borderRadius={12}
                        bgcolor={
                          !hideArrow ? 'transparent' : priceColor.bgColor
                        }
                        color={priceColor.textColor}
                      >
                        {!hideArrow && priceUp && <ArrowDropUp />}
                        {!hideArrow && priceDown && <ArrowDropDown />}
                        <Typography variant='caption'>
                          {hideArrow && priceUp ? '+' : ''}
                          {priceUpPercent}%
                        </Typography>
                      </Box>
                    </Box>
                  </Box>
                </Box>
              );
            })}
          </Box>
        ) : (
          <Skeleton variant='rect' width='100%' height={100} />
        )}
      </Box>
    </Box>
  );
}
Example #9
Source File: TokensTable.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
TokensTable: React.FC<TokensTableProps> = ({ data }) => {
  const tokenHeadCells = headCells();
  const classes = useStyles();
  const { palette } = useTheme();
  const {
    bookmarkTokens,
    addBookmarkToken,
    removeBookmarkToken,
  } = useBookmarkTokens();
  const mobileHTML = (token: any, index: number) => {
    const tokenCurrency = new Token(
      ChainId.MATIC,
      getAddress(token.id),
      Number(token.decimals),
      token.symbol,
      token.name,
    );
    const priceColor = getPriceColor(Number(token.priceChangeUSD), palette);
    return (
      <Box mt={index === 0 ? 0 : 3}>
        <Box display='flex' alignItems='center' mb={1}>
          <Box
            display='flex'
            mr={1}
            onClick={() => {
              const tokenIndex = bookmarkTokens.indexOf(token.id);
              if (tokenIndex === -1) {
                addBookmarkToken(token.id);
              } else {
                removeBookmarkToken(token.id);
              }
            }}
          >
            {bookmarkTokens.indexOf(token.id) > -1 ? (
              <StarChecked />
            ) : (
              <StarUnchecked />
            )}
          </Box>
          <Link
            to={`/analytics/token/${tokenCurrency.address}`}
            style={{ textDecoration: 'none' }}
          >
            <Box display='flex' alignItems='center'>
              <CurrencyLogo currency={tokenCurrency} size='28px' />
              <Box ml={1}>
                <Typography
                  variant='body1'
                  style={{ color: palette.text.primary }}
                >
                  {token.name}{' '}
                  <span style={{ color: palette.text.hint }}>
                    ({token.symbol})
                  </span>
                </Typography>
              </Box>
            </Box>
          </Link>
        </Box>
        <Divider />
        <Box className={classes.mobileRow}>
          <Typography variant='body1'>Price</Typography>
          <Typography variant='body1'>
            ${formatNumber(token.priceUSD)}
          </Typography>
        </Box>
        <Box className={classes.mobileRow}>
          <Typography variant='body1'>24H %</Typography>
          <Box
            className={classes.priceChangeWrapper}
            bgcolor={priceColor.bgColor}
            color={priceColor.textColor}
          >
            <Typography variant='body2'>
              {getFormattedPrice(Number(token.priceChangeUSD))}%
            </Typography>
          </Box>
        </Box>
        <Box className={classes.mobileRow}>
          <Typography variant='body1'>24H Volume</Typography>
          <Typography variant='body1'>
            ${Number(token.oneDayVolumeUSD).toLocaleString()}
          </Typography>
        </Box>
        <Box className={classes.mobileRow}>
          <Typography variant='body1'>Liquidity</Typography>
          <Typography variant='body1'>
            ${Number(token.totalLiquidityUSD).toLocaleString()}
          </Typography>
        </Box>
      </Box>
    );
  };

  const desktopHTML = (token: any) => {
    const tokenCurrency = new Token(
      ChainId.MATIC,
      getAddress(token.id),
      Number(token.decimals),
      token.symbol,
      token.name,
    );
    const priceColor = getPriceColor(Number(token.priceChangeUSD), palette);

    return [
      {
        html: (
          <Box display='flex' alignItems='center'>
            <Box
              display='flex'
              mr={1}
              onClick={() => {
                const tokenIndex = bookmarkTokens.indexOf(token.id);
                if (tokenIndex === -1) {
                  addBookmarkToken(token.id);
                } else {
                  removeBookmarkToken(token.id);
                }
              }}
            >
              {bookmarkTokens.indexOf(token.id) > -1 ? (
                <StarChecked />
              ) : (
                <StarUnchecked />
              )}
            </Box>
            <Link
              to={`/analytics/token/${tokenCurrency.address}`}
              style={{ textDecoration: 'none' }}
            >
              <Box display='flex' alignItems='center'>
                <CurrencyLogo currency={tokenCurrency} size='28px' />
                <Box ml={1}>
                  <Typography
                    variant='body1'
                    style={{ color: palette.text.primary }}
                  >
                    {token.name}{' '}
                    <span style={{ color: palette.text.hint }}>
                      ({token.symbol})
                    </span>
                  </Typography>
                </Box>
              </Box>
            </Link>
          </Box>
        ),
      },
      {
        html: (
          <Box>
            <Typography>${Number(token.priceUSD).toLocaleString()}</Typography>
          </Box>
        ),
      },
      {
        html: (
          <Box
            className={classes.priceChangeWrapper}
            mr={2}
            bgcolor={priceColor.bgColor}
            color={priceColor.textColor}
          >
            <Typography variant='body2'>
              {getFormattedPrice(Number(token.priceChangeUSD))}%
            </Typography>
          </Box>
        ),
      },
      {
        html: (
          <Box>
            <Typography>
              ${Number(token.oneDayVolumeUSD).toLocaleString()}
            </Typography>
          </Box>
        ),
      },
      {
        html: (
          <Box>
            <Typography>
              ${Number(token.totalLiquidityUSD).toLocaleString()}
            </Typography>
          </Box>
        ),
      },
    ];
  };

  return (
    <CustomTable
      defaultOrderBy={tokenHeadCells[liquidityHeadCellIndex]}
      defaultOrder='desc'
      showPagination={data.length > GlobalConst.utils.ROWSPERPAGE}
      headCells={tokenHeadCells}
      rowsPerPage={GlobalConst.utils.ROWSPERPAGE}
      data={data}
      mobileHTML={mobileHTML}
      desktopHTML={desktopHTML}
    />
  );
}
Example #10
Source File: SyrupCardDetails.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
SyrupCardDetails: React.FC<{ syrup: SyrupInfo; dQUICKAPY: string }> = ({
  syrup,
  dQUICKAPY,
}) => {
  const syrupCurrency = unwrappedToken(syrup.token);
  const { palette, breakpoints } = useTheme();
  const { t } = useTranslation();
  const isMobile = useMediaQuery(breakpoints.down('xs'));
  const [attemptingClaim, setAttemptingClaim] = useState(false);
  const [attemptingUnstake, setAttemptingUnstake] = useState(false);
  const [openStakeModal, setOpenStakeModal] = useState(false);
  const classes = useStyles();

  const stakingTokenPrice = useUSDCPriceToken(syrup.stakingToken);
  const stakingContract = useStakingContract(syrup?.stakingRewardAddress);
  const addTransaction = useTransactionAdder();
  const finalizedTransaction = useTransactionFinalizer();

  const { account } = useActiveWeb3React();
  const currency = syrup ? unwrappedToken(syrup.token) : undefined;

  const userLiquidityUnstaked = useTokenBalance(
    account ?? undefined,
    syrup.stakedAmount?.token,
  );

  const exactEnd = syrup ? syrup.periodFinish : 0;

  const depositAmount =
    syrup && syrup.valueOfTotalStakedAmountInUSDC
      ? `$${Number(syrup.valueOfTotalStakedAmountInUSDC).toLocaleString()}`
      : `${formatTokenAmount(syrup?.totalStakedAmount)} ${
          syrup?.stakingToken.symbol
        }`;

  const onClaimReward = async () => {
    if (syrup && stakingContract && syrup.stakedAmount) {
      setAttemptingClaim(true);
      await stakingContract
        .getReward({ gasLimit: 350000 })
        .then(async (response: TransactionResponse) => {
          addTransaction(response, {
            summary: `Claim accumulated` + syrup.token.symbol + `rewards`,
          });
          try {
            const receipt = await response.wait();
            finalizedTransaction(receipt, {
              summary: `Claim accumulated` + syrup.token.symbol + `rewards`,
            });
            setAttemptingClaim(false);
          } catch (e) {
            setAttemptingClaim(false);
          }
        })
        .catch((error: any) => {
          setAttemptingClaim(false);
          console.log(error);
        });
    }
  };

  const onWithdraw = async () => {
    if (syrup && stakingContract && syrup.stakedAmount) {
      setAttemptingUnstake(true);
      await stakingContract
        .exit({ gasLimit: 300000 })
        .then(async (response: TransactionResponse) => {
          addTransaction(response, {
            summary: `Withdraw deposited liquidity`,
          });
          try {
            const receipt = await response.wait();
            finalizedTransaction(receipt, {
              summary: `Withdraw deposited dQUICK`,
            });
            setAttemptingUnstake(false);
          } catch (e) {
            setAttemptingUnstake(false);
          }
        })
        .catch((error: any) => {
          setAttemptingUnstake(false);
          console.log(error);
        });
    }
  };

  const StakeButton = () => (
    <Box
      className={classes.syrupButton}
      onClick={() => setOpenStakeModal(true)}
    >
      <Typography variant='body2'>Stake</Typography>
    </Box>
  );

  const UnstakeButton = () => (
    <Box
      className={classes.syrupButton}
      style={{ opacity: attemptingUnstake ? 0.6 : 1 }}
      onClick={() => {
        if (!attemptingUnstake) {
          onWithdraw();
        }
      }}
    >
      <Typography variant='body2'>
        {attemptingUnstake ? 'Unstaking...' : 'Unstake'}
      </Typography>
    </Box>
  );

  const ClaimButton = () => (
    <Box
      className={classes.syrupButton}
      style={{ opacity: attemptingClaim ? 0.6 : 1 }}
      onClick={() => {
        if (!attemptingClaim) {
          onClaimReward();
        }
      }}
    >
      <Typography variant='body2'>
        {attemptingClaim ? 'Claiming...' : 'Claim'}
      </Typography>
    </Box>
  );

  return (
    <>
      {openStakeModal && syrup && (
        <StakeSyrupModal
          open={openStakeModal}
          onClose={() => setOpenStakeModal(false)}
          syrup={syrup}
        />
      )}
      {syrup && (
        <>
          <Divider />
          <Box padding={3}>
            {isMobile && (
              <Box mb={3}>
                <Box display='flex' justifyContent='space-between' mb={2}>
                  <Typography
                    variant='body2'
                    style={{ color: palette.text.secondary }}
                  >
                    {syrup.stakingToken.symbol} {t('deposits')}:
                  </Typography>
                  <Typography variant='body2'>{depositAmount}</Typography>
                </Box>
                <Box display='flex' justifyContent='space-between' mb={2}>
                  <Typography
                    variant='body2'
                    style={{ color: palette.text.secondary }}
                  >
                    {t('dailyRewards')}:
                  </Typography>
                  <Typography variant='body2'>
                    {syrup.rate >= 1000000
                      ? formatCompact(syrup.rate)
                      : syrup.rate.toLocaleString()}{' '}
                    {syrup.token.symbol}
                    <span style={{ color: palette.text.secondary }}>
                      {' '}
                      / {t('day')}
                    </span>
                  </Typography>
                </Box>
                <Box mb={2}>
                  <SyrupTimerLabel exactEnd={exactEnd} isEnded={syrup?.ended} />
                </Box>
                <Box display='flex' justifyContent='space-between' mb={3}>
                  <Box display='flex' alignItems='center'>
                    <Typography
                      variant='body2'
                      style={{ color: palette.text.secondary }}
                    >
                      APR:
                    </Typography>
                    <Box ml={0.5} height={16}>
                      <img src={CircleInfoIcon} alt={'arrow up'} />
                    </Box>
                  </Box>
                  <Box textAlign='right'>
                    <SyrupAPR syrup={syrup} dQUICKAPY={dQUICKAPY} />
                  </Box>
                </Box>
                <Divider />
              </Box>
            )}
            <Box
              display='flex'
              alignItems='center'
              justifyContent='space-between'
              mb={1}
            >
              <Typography
                variant='body2'
                style={{ color: palette.text.secondary }}
              >
                {t('inwallet')}
              </Typography>
              <Typography variant='body2'>
                <span style={{ color: palette.text.primary }}>
                  {userLiquidityUnstaked
                    ? formatTokenAmount(userLiquidityUnstaked)
                    : 0}{' '}
                  {syrup.stakingToken.symbol}
                </span>
                <span style={{ color: palette.text.secondary, marginLeft: 4 }}>
                  $
                  {userLiquidityUnstaked
                    ? (
                        stakingTokenPrice *
                        Number(userLiquidityUnstaked.toExact())
                      ).toLocaleString()
                    : 0}
                </span>
              </Typography>
            </Box>
            <Box
              display='flex'
              alignItems='center'
              justifyContent='space-between'
              mb={1}
            >
              <Typography
                variant='body2'
                style={{ color: palette.text.secondary }}
              >
                {t('staked')}
              </Typography>
              <Typography variant='body2'>
                <span style={{ color: palette.text.primary }}>
                  {formatTokenAmount(syrup.stakedAmount)}{' '}
                  {syrup.stakingToken.symbol}
                </span>
                <span style={{ color: palette.text.secondary, marginLeft: 4 }}>
                  {syrup.stakedAmount
                    ? `$${(
                        stakingTokenPrice * Number(syrup.stakedAmount.toExact())
                      ).toLocaleString()}`
                    : '-'}
                </span>
              </Typography>
            </Box>
            <Box
              display='flex'
              alignItems='center'
              justifyContent='space-between'
              mb={2}
            >
              <Typography
                variant='body2'
                style={{ color: palette.text.secondary }}
              >
                {t('earned')} {currency?.symbol}
              </Typography>
              <Box display='flex' alignItems='center'>
                <CurrencyLogo currency={currency} size='16px' />
                <Typography variant='body2' style={{ marginLeft: 4 }}>
                  <span style={{ color: palette.text.primary }}>
                    {formatTokenAmount(syrup.earnedAmount)}
                  </span>
                  <span
                    style={{ color: palette.text.secondary, marginLeft: 4 }}
                  >
                    {getEarnedUSDSyrup(syrup)}
                  </span>
                </Typography>
              </Box>
            </Box>
            <Box
              display='flex'
              flexWrap='wrap'
              alignItems='center'
              justifyContent='space-between'
            >
              {!isMobile && (
                <SyrupTimerLabel exactEnd={exactEnd} isEnded={syrup?.ended} />
              )}
              {isMobile ? (
                <>
                  {syrup.earnedAmount && syrup.earnedAmount.greaterThan('0') && (
                    <Box
                      width={1}
                      mb={1.5}
                      display='flex'
                      justifyContent='flex-end'
                    >
                      <ClaimButton />
                    </Box>
                  )}
                  <Box
                    width={1}
                    mb={1.5}
                    display='flex'
                    justifyContent={
                      syrup.stakedAmount && syrup.stakedAmount.greaterThan('0')
                        ? 'space-between'
                        : 'flex-end'
                    }
                  >
                    {!syrup.ended && <StakeButton />}
                    {syrup.stakedAmount &&
                      syrup.stakedAmount.greaterThan('0') && <UnstakeButton />}
                  </Box>
                </>
              ) : (
                <Box display='flex' flexWrap='wrap' my={1}>
                  {!syrup.ended && <StakeButton />}
                  {syrup.stakedAmount && syrup.stakedAmount.greaterThan('0') && (
                    <Box ml={1}>
                      <UnstakeButton />
                    </Box>
                  )}
                  {syrup.earnedAmount && syrup.earnedAmount.greaterThan('0') && (
                    <Box ml={1}>
                      <ClaimButton />
                    </Box>
                  )}
                </Box>
              )}
            </Box>
            {syrup.rewardRate?.greaterThan('0') && (
              <Box className={classes.dailyRateWrapper}>
                <Box
                  display='flex'
                  alignItems='center'
                  justifyContent={isMobile ? 'space-between' : 'flex-start'}
                  width={isMobile ? 1 : 'auto'}
                  flexWrap='wrap'
                >
                  <Box display='flex' mr={1}>
                    <Typography variant='body2' color='textSecondary'>
                      {t('yourRate')}:
                    </Typography>
                  </Box>
                  <Typography variant='body2' color='textPrimary'>
                    {formatMulDivTokenAmount(
                      syrup.rewardRate,
                      GlobalConst.utils.ONEDAYSECONDS,
                    )}{' '}
                    {syrupCurrency.symbol} / {t('day')}
                  </Typography>
                </Box>
              </Box>
            )}
          </Box>
        </>
      )}
    </>
  );
}
Example #11
Source File: SyrupCard.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
SyrupCard: React.FC<{ syrup: SyrupInfo; dQUICKAPY: string }> = ({
  syrup,
  dQUICKAPY,
}) => {
  const { palette, breakpoints } = useTheme();
  const isMobile = useMediaQuery(breakpoints.down('xs'));
  const [expanded, setExpanded] = useState(false);
  const classes = useStyles();

  const currency = unwrappedToken(syrup.token);

  const depositAmount = syrup.valueOfTotalStakedAmountInUSDC
    ? `$${syrup.valueOfTotalStakedAmountInUSDC.toLocaleString()}`
    : `${formatTokenAmount(syrup.totalStakedAmount)} ${
        syrup.stakingToken.symbol
      }`;

  return (
    <Box className={classes.syrupCard}>
      <Box
        display='flex'
        flexWrap='wrap'
        alignItems='center'
        width={1}
        padding={2}
        style={{ cursor: 'pointer' }}
        onClick={() => setExpanded(!expanded)}
      >
        {isMobile ? (
          <>
            <Box
              display='flex'
              alignItems='center'
              width={expanded ? 0.95 : 0.5}
            >
              <CurrencyLogo currency={currency} size='32px' />
              <Box ml={1.5}>
                <Typography variant='body2'>{currency.symbol}</Typography>
              </Box>
            </Box>
            {!expanded && (
              <Box width={0.45}>
                <SyrupAPR syrup={syrup} dQUICKAPY={dQUICKAPY} />
              </Box>
            )}
            <Box
              width={0.05}
              display='flex'
              justifyContent='flex-end'
              color={palette.primary.main}
            >
              {expanded ? <KeyboardArrowUp /> : <KeyboardArrowDown />}
            </Box>
          </>
        ) : (
          <>
            <Box width={0.3} display='flex' alignItems='center'>
              <CurrencyLogo currency={currency} size='32px' />
              <Box ml={1.5}>
                <Typography variant='body2'>{currency.symbol}</Typography>
                <Box display='flex' mt={0.25}>
                  <Typography variant='caption'>
                    {syrup.rate >= 1000000
                      ? formatCompact(syrup.rate)
                      : syrup.rate.toLocaleString()}
                    <span style={{ color: palette.text.secondary }}>
                      {' '}
                      / day
                    </span>
                  </Typography>
                </Box>
                <Box display='flex' mt={0.25}>
                  <Typography variant='caption'>
                    $
                    {syrup.rewardTokenPriceinUSD
                      ? (
                          syrup.rate * syrup.rewardTokenPriceinUSD
                        ).toLocaleString()
                      : '-'}{' '}
                    <span style={{ color: palette.text.secondary }}>/ day</span>
                  </Typography>
                </Box>
              </Box>
            </Box>
            <Box width={0.3}>
              <Typography variant='body2'>{depositAmount}</Typography>
            </Box>
            <Box width={0.2} textAlign='left'>
              <SyrupAPR syrup={syrup} dQUICKAPY={dQUICKAPY} />
            </Box>
            <Box width={0.2} textAlign='right'>
              <Box
                display='flex'
                alignItems='center'
                justifyContent='flex-end'
                mb={0.25}
              >
                <CurrencyLogo currency={currency} size='16px' />
                <Typography variant='body2' style={{ marginLeft: 5 }}>
                  {formatTokenAmount(syrup.earnedAmount)}
                </Typography>
              </Box>
              <Typography
                variant='body2'
                style={{ color: palette.text.secondary }}
              >
                {getEarnedUSDSyrup(syrup)}
              </Typography>
            </Box>
          </>
        )}
      </Box>
      {expanded && syrup && (
        <SyrupCardDetails syrup={syrup} dQUICKAPY={dQUICKAPY} />
      )}
    </Box>
  );
}
Example #12
Source File: SwapTokenDetails.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
SwapTokenDetails: React.FC<{
  token: Token;
}> = ({ token }) => {
  const classes = useStyles();
  const currency = unwrappedToken(token);
  const tokenAddress = token.address;
  const { palette } = useTheme();
  const latestBlock = useBlockNumber();
  const { tokenDetails, updateTokenDetails } = useTokenDetails();
  const [tokenData, setTokenData] = useState<any>(null);
  const [priceData, setPriceData] = useState<any>(null);
  const priceUp = Number(tokenData?.priceChangeUSD) > 0;
  const priceUpPercent = Number(tokenData?.priceChangeUSD).toFixed(2);
  const [isCopied, setCopied] = useCopyClipboard();
  const prices = priceData ? priceData.map((price: any) => price.close) : [];

  useEffect(() => {
    async function fetchTokenData() {
      const tokenDetail = tokenDetails.find(
        (item) => item.address === tokenAddress,
      );
      setTokenData(tokenDetail?.tokenData);
      setPriceData(tokenDetail?.priceData);
      const currentTime = dayjs.utc();
      const startTime = currentTime
        .subtract(1, 'day')
        .startOf('hour')
        .unix();
      const tokenPriceData = await getIntervalTokenData(
        tokenAddress,
        startTime,
        3600,
        latestBlock,
      );
      setPriceData(tokenPriceData);

      const [newPrice, oneDayPrice] = await getEthPrice();
      const tokenInfo = await getTokenInfo(newPrice, oneDayPrice, tokenAddress);
      if (tokenInfo) {
        const token0 = tokenInfo[0];
        setTokenData(token0);
        const tokenDetailToUpdate = {
          address: tokenAddress,
          tokenData: token0,
          priceData: tokenPriceData,
        };
        updateTokenDetails(tokenDetailToUpdate);
      }
    }
    fetchTokenData();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [tokenAddress]);

  return (
    <Box>
      <Box
        display='flex'
        alignItems='center'
        justifyContent='space-between'
        px={2}
        py={1.5}
      >
        <Box display='flex' alignItems='center'>
          <CurrencyLogo currency={currency} size='28px' />
          <Box ml={1}>
            <Typography variant='body2'>{currency.symbol}</Typography>
            {tokenData ? (
              <Box display='flex' alignItems='center'>
                <Typography variant='body2'>
                  ${formatNumber(tokenData.priceUSD)}
                </Typography>
                <Box
                  ml={0.5}
                  display='flex'
                  alignItems='center'
                  className={priceUp ? classes.success : classes.danger}
                >
                  {priceUp ? <ArrowDropUp /> : <ArrowDropDown />}
                  <Typography variant='body2'>{priceUpPercent}%</Typography>
                </Box>
              </Box>
            ) : (
              <Skeleton variant='rect' width={100} height={20} />
            )}
          </Box>
        </Box>
        {tokenData && priceData ? (
          <Box width={88} height={47} position='relative'>
            <Box position='absolute' top={-30} width={1}>
              {prices.length > 0 && (
                <LineChart
                  data={prices}
                  width='100%'
                  height={120}
                  color={priceUp ? palette.success.main : palette.error.main}
                />
              )}
            </Box>
          </Box>
        ) : (
          <Skeleton variant='rect' width={88} height={47} />
        )}
      </Box>
      <Box
        borderTop={`1px solid ${palette.secondary.light}`}
        borderBottom={`1px solid ${palette.secondary.light}`}
        px={2}
      >
        <Grid container>
          <Grid item xs={6}>
            <Box borderRight={`1px solid ${palette.secondary.light}`} py={1}>
              {tokenData ? (
                <Typography
                  variant='body2'
                  style={{ color: palette.text.secondary }}
                >
                  TVL: {formatCompact(tokenData?.totalLiquidityUSD)}
                </Typography>
              ) : (
                <Skeleton variant='rect' width={100} height={16} />
              )}
            </Box>
          </Grid>
          <Grid item xs={6}>
            <Box py={1} pl={2}>
              {tokenData ? (
                <Typography
                  variant='body2'
                  style={{ color: palette.text.secondary }}
                >
                  24h VOL: {formatCompact(tokenData?.oneDayVolumeUSD)}
                </Typography>
              ) : (
                <Skeleton variant='rect' width={100} height={16} />
              )}
            </Box>
          </Grid>
        </Grid>
      </Box>
      <Box
        display='flex'
        justifyContent='space-between'
        alignItems='center'
        py={1}
        px={2}
      >
        <a
          href={`https://polygonscan.com/token/${tokenAddress}`}
          target='_blank'
          rel='noopener noreferrer'
          style={{ textDecoration: 'none' }}
        >
          <Typography variant='body2' style={{ color: palette.primary.main }}>
            {shortenAddress(tokenAddress)}
          </Typography>
        </a>
        <Box
          display='flex'
          style={{ cursor: 'pointer', opacity: isCopied ? 0.5 : 1 }}
          onClick={() => {
            setCopied(tokenAddress);
          }}
        >
          <CopyIcon />
        </Box>
      </Box>
    </Box>
  );
}
Example #13
Source File: AdvancedSwapDetails.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
TradeSummary: React.FC<TradeSummaryProps> = ({
  trade,
  allowedSlippage,
}) => {
  const [openSettingsModal, setOpenSettingsModal] = useState(false);
  const { palette } = useTheme();
  const { t } = useTranslation();

  const { priceImpactWithoutFee, realizedLPFee } = computeTradePriceBreakdown(
    trade,
  );
  const isExactIn = trade.tradeType === TradeType.EXACT_INPUT;
  const slippageAdjustedAmounts = computeSlippageAdjustedAmounts(
    trade,
    allowedSlippage,
  );
  const classes = useStyles();
  const tradeAmount = isExactIn ? trade.outputAmount : trade.inputAmount;

  return (
    <Box mt={1.5}>
      {openSettingsModal && (
        <SettingsModal
          open={openSettingsModal}
          onClose={() => setOpenSettingsModal(false)}
        />
      )}
      <Box className={classes.summaryRow}>
        <Box display='flex' alignItems='center'>
          <Typography variant='body2'>Slippage:</Typography>
          <QuestionHelper text={t('slippageHelper')} />
        </Box>
        <Box
          display='flex'
          alignItems='center'
          onClick={() => setOpenSettingsModal(true)}
          style={{ cursor: 'pointer' }}
        >
          <Typography variant='body2' style={{ color: palette.primary.main }}>
            {allowedSlippage / 100}%
          </Typography>
          <EditIcon style={{ marginLeft: 8 }} />
        </Box>
      </Box>
      <Box className={classes.summaryRow}>
        <Box display='flex' alignItems='center'>
          <Typography variant='body2'>
            {isExactIn ? t('minReceived') : t('maxSold')}:
          </Typography>
          <QuestionHelper text={t('txLimitHelper')} />
        </Box>
        <Box display='flex' alignItems='center'>
          <Typography variant='body2'>
            {formatTokenAmount(
              slippageAdjustedAmounts[isExactIn ? Field.OUTPUT : Field.INPUT],
            )}{' '}
            {tradeAmount.currency.symbol}
          </Typography>
          <Box
            width={16}
            height={16}
            ml={0.5}
            borderRadius={8}
            overflow='hidden'
          >
            <CurrencyLogo currency={tradeAmount.currency} size='16px' />
          </Box>
        </Box>
      </Box>
      <Box className={classes.summaryRow}>
        <Box display='flex' alignItems='center'>
          <Typography variant='body2'>Price Impact:</Typography>
          <QuestionHelper text={t('priceImpactHelper')} />
        </Box>
        <FormattedPriceImpact priceImpact={priceImpactWithoutFee} />
      </Box>
      <Box className={classes.summaryRow}>
        <Box display='flex' alignItems='center'>
          <Typography variant='body2'>Liquidity Provider Fee:</Typography>
          <QuestionHelper text={t('liquidityProviderFeeHelper')} />
        </Box>
        <Typography variant='body2'>
          {formatTokenAmount(realizedLPFee)} {trade.inputAmount.currency.symbol}
        </Typography>
      </Box>
      <Box className={classes.summaryRow}>
        <Box display='flex' alignItems='center'>
          <Typography variant='body2' style={{ marginRight: 4 }}>
            Route
          </Typography>
          <QuestionHelper text={t('swapRouteHelper')} />
        </Box>
        <Box>
          {trade.route.path.map((token, i, path) => {
            const isLastItem: boolean = i === path.length - 1;
            return (
              <Box key={i} display='flex' alignItems='center'>
                <Typography variant='body2'>
                  {token.symbol}{' '}
                  {// this is not to show the arrow at the end of the trade path
                  isLastItem ? '' : ' > '}
                </Typography>
              </Box>
            );
          })}
        </Box>
      </Box>
    </Box>
  );
}
Example #14
Source File: index.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
Search: React.FC = () => {
  const classes = useStyles();
  const history = useHistory();
  const [searchVal, setSearchVal] = useState('');
  const [menuOpen, setMenuOpen] = useState(false);
  const menuRef = useRef<any>(null);
  const wrapperRef = useRef<any>(null);
  const [searchedTokens, setSearchedTokens] = useState<any[]>([]);
  const [searchedPairs, setSearchedPairs] = useState<any[]>([]);
  const [tokensShown, setTokensShown] = useState(3);
  const [pairsShown, setPairsShown] = useState(3);
  const [allTokens, setAllTokens] = useState<any[]>([]);
  const [allPairs, setAllPairs] = useState<any[]>([]);

  const escapeRegExp = (str: string) => {
    return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  };

  const filteredTokens = useMemo(() => {
    const tokens = allTokens.concat(
      searchedTokens.filter((searchedToken) => {
        let included = false;
        allTokens.map((token) => {
          if (token.id === searchedToken.id) {
            included = true;
          }
          return true;
        });
        return !included;
      }),
    );
    const filtered = tokens
      ? tokens.filter((token) => {
          if (GlobalConst.blacklists.TOKEN_BLACKLIST.includes(token.id)) {
            return false;
          }
          const regexMatches = Object.keys(token).map((tokenEntryKey) => {
            const isAddress = searchVal.slice(0, 2) === '0x';
            if (tokenEntryKey === 'id' && isAddress) {
              return token[tokenEntryKey].match(
                new RegExp(escapeRegExp(searchVal), 'i'),
              );
            }
            if (tokenEntryKey === 'symbol' && !isAddress) {
              return token[tokenEntryKey].match(
                new RegExp(escapeRegExp(searchVal), 'i'),
              );
            }
            if (tokenEntryKey === 'name' && !isAddress) {
              return token[tokenEntryKey].match(
                new RegExp(escapeRegExp(searchVal), 'i'),
              );
            }
            return false;
          });
          return regexMatches.some((m) => m);
        })
      : [];
    return filtered;
  }, [allTokens, searchedTokens, searchVal]);

  const filteredPairs = useMemo(() => {
    const pairs = allPairs.concat(
      searchedPairs.filter((searchedPair) => {
        let included = false;
        allPairs.map((pair) => {
          if (pair.id === searchedPair.id) {
            included = true;
          }
          return true;
        });
        return !included;
      }),
    );
    const filtered = pairs
      ? pairs.filter((pair) => {
          if (GlobalConst.blacklists.PAIR_BLACKLIST.includes(pair.id)) {
            return false;
          }
          if (searchVal && searchVal.includes(' ')) {
            const pairA = searchVal.split(' ')[0]?.toUpperCase();
            const pairB = searchVal.split(' ')[1]?.toUpperCase();
            return (
              (pair.token0.symbol.includes(pairA) ||
                pair.token0.symbol.includes(pairB)) &&
              (pair.token1.symbol.includes(pairA) ||
                pair.token1.symbol.includes(pairB))
            );
          }
          if (searchVal && searchVal.includes('-')) {
            const pairA = searchVal.split('-')[0]?.toUpperCase();
            const pairB = searchVal.split('-')[1]?.toUpperCase();
            return (
              (pair.token0.symbol.includes(pairA) ||
                pair.token0.symbol.includes(pairB)) &&
              (pair.token1.symbol.includes(pairA) ||
                pair.token1.symbol.includes(pairB))
            );
          }
          const regexMatches = Object.keys(pair).map((field) => {
            const isAddress = searchVal.slice(0, 2) === '0x';
            if (field === 'id' && isAddress) {
              return pair[field].match(
                new RegExp(escapeRegExp(searchVal), 'i'),
              );
            }
            if (field === 'token0') {
              return (
                pair[field].symbol.match(
                  new RegExp(escapeRegExp(searchVal), 'i'),
                ) ||
                pair[field].name.match(new RegExp(escapeRegExp(searchVal), 'i'))
              );
            }
            if (field === 'token1') {
              return (
                pair[field].symbol.match(
                  new RegExp(escapeRegExp(searchVal), 'i'),
                ) ||
                pair[field].name.match(new RegExp(escapeRegExp(searchVal), 'i'))
              );
            }
            return false;
          });
          return regexMatches.some((m) => m);
        })
      : [];
    return filtered;
  }, [allPairs, searchedPairs, searchVal]);

  useEffect(() => {
    async function fetchAllData() {
      const tokens = await getAllTokensOnUniswap();
      const pairs = await getAllPairsOnUniswap();
      if (tokens) {
        setAllTokens(tokens);
      }
      if (pairs) {
        setAllPairs(pairs);
      }
    }
    fetchAllData();
  }, []);

  useEffect(() => {
    async function fetchData() {
      try {
        if (searchVal.length > 0) {
          const tokens = await client.query({
            query: TOKEN_SEARCH,
            variables: {
              value: searchVal ? searchVal.toUpperCase() : '',
              id: searchVal,
            },
          });

          const pairs = await client.query({
            query: PAIR_SEARCH,
            variables: {
              tokens: tokens.data.asSymbol?.map((t: any) => t.id),
              id: searchVal,
            },
          });

          setSearchedPairs(
            pairs.data.as0.concat(pairs.data.as1).concat(pairs.data.asAddress),
          );
          const foundTokens = tokens.data.asSymbol
            .concat(tokens.data.asAddress)
            .concat(tokens.data.asName);
          setSearchedTokens(foundTokens);
        }
      } catch (e) {
        console.log(e);
      }
    }
    fetchData();
  }, [searchVal]);

  const handleClick = (e: any) => {
    if (
      !(menuRef.current && menuRef.current.contains(e.target)) &&
      !(wrapperRef.current && wrapperRef.current.contains(e.target))
    ) {
      setPairsShown(3);
      setTokensShown(3);
      setMenuOpen(false);
    }
  };

  useEffect(() => {
    document.addEventListener('click', handleClick);
    return () => {
      document.removeEventListener('click', handleClick);
    };
  });

  return (
    <Box position='relative'>
      <Box className={classes.searchInput}>
        <input
          placeholder='Search for tokens, pairs, etc…'
          value={searchVal}
          ref={menuRef}
          onFocus={() => setMenuOpen(true)}
          onChange={(evt) => setSearchVal(evt.target.value)}
        />
        <Box display='flex'>
          <SearchIcon />
        </Box>
      </Box>
      {menuOpen && (
        <div ref={wrapperRef} className={classes.searchContent}>
          <Typography variant='body1'>Pairs</Typography>
          {filteredPairs.slice(0, pairsShown).map((val, ind) => {
            const currency0 = new Token(
              ChainId.MATIC,
              getAddress(val.token0.id),
              val.token0.decimals,
            );
            const currency1 = new Token(
              ChainId.MATIC,
              getAddress(val.token1.id),
              val.token1.decimals,
            );
            return (
              <Box
                mt={1}
                key={ind}
                display='flex'
                alignItems='center'
                style={{ cursor: 'pointer' }}
                onClick={() => history.push(`/analytics/pair/${val.id}`)}
              >
                <DoubleCurrencyLogo
                  currency0={currency0}
                  currency1={currency1}
                  size={28}
                />
                <Typography variant='body2' style={{ marginLeft: 8 }}>
                  {val.token0.symbol} - {val.token1.symbol} Pair
                </Typography>
              </Box>
            );
          })}
          <Typography
            variant='body2'
            style={{ cursor: 'pointer', margin: '8px 0' }}
            onClick={() => setPairsShown(pairsShown + 5)}
          >
            Show More
          </Typography>
          <Typography variant='body1'>Tokens</Typography>
          {filteredTokens.slice(0, tokensShown).map((val, ind) => {
            const currency = new Token(
              ChainId.MATIC,
              getAddress(val.id),
              val.decimals,
            );
            return (
              <Box
                mt={1}
                key={ind}
                display='flex'
                alignItems='center'
                style={{ cursor: 'pointer' }}
                onClick={() => history.push(`/analytics/token/${val.id}`)}
              >
                <CurrencyLogo currency={currency} size='28px' />
                <Typography variant='body2' style={{ marginLeft: 8 }}>
                  {val.name} {val.symbol}
                </Typography>
              </Box>
            );
          })}
          <Typography
            variant='body2'
            style={{ cursor: 'pointer', marginTop: 8 }}
            onClick={() => setTokensShown(tokensShown + 5)}
          >
            Show More
          </Typography>
        </div>
      )}
    </Box>
  );
}
Example #15
Source File: RemoveLiquidityModal.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
RemoveLiquidityModal: React.FC<RemoveLiquidityModalProps> = ({
  currency0,
  currency1,
  open,
  onClose,
}) => {
  const classes = useStyles();
  const { palette } = useTheme();
  const [showConfirm, setShowConfirm] = useState(false);
  const [txPending, setTxPending] = useState(false);
  const [approving, setApproving] = useState(false);
  const [attemptingTxn, setAttemptingTxn] = useState(false);
  const [removeErrorMessage, setRemoveErrorMessage] = useState('');
  const [errorMsg, setErrorMsg] = useState('');
  const [txHash, setTxHash] = useState('');
  const addTransaction = useTransactionAdder();
  const finalizedTransaction = useTransactionFinalizer();
  const { chainId, account, library } = useActiveWeb3React();
  const [tokenA, tokenB] = useMemo(
    () => [
      wrappedCurrency(currency0, chainId),
      wrappedCurrency(currency1, chainId),
    ],
    [currency0, currency1, chainId],
  );

  const { independentField, typedValue } = useBurnState();
  const { pair, parsedAmounts, error } = useDerivedBurnInfo(
    currency0,
    currency1,
  );
  const deadline = useTransactionDeadline();
  const { onUserInput: _onUserInput } = useBurnActionHandlers();
  const [allowedSlippage] = useUserSlippageTolerance();

  const onUserInput = useCallback(
    (field: Field, typedValue: string) => {
      return _onUserInput(field, typedValue);
    },
    [_onUserInput],
  );

  const onLiquidityInput = useCallback(
    (typedValue: string): void => onUserInput(Field.LIQUIDITY, typedValue),
    [onUserInput],
  );

  const liquidityPercentChangeCallback = useCallback(
    (value: number) => {
      onUserInput(Field.LIQUIDITY_PERCENT, value.toString());
    },
    [onUserInput],
  );

  const [
    innerLiquidityPercentage,
    setInnerLiquidityPercentage,
  ] = useDebouncedChangeHandler(
    Number.parseInt(parsedAmounts[Field.LIQUIDITY_PERCENT].toFixed(0)),
    liquidityPercentChangeCallback,
  );
  const userPoolBalance = useTokenBalance(
    account ?? undefined,
    pair?.liquidityToken,
  );
  const totalPoolTokens = useTotalSupply(pair?.liquidityToken);
  const poolTokenPercentage =
    !!userPoolBalance &&
    !!totalPoolTokens &&
    JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
      ? new Percent(userPoolBalance.raw, totalPoolTokens.raw)
      : undefined;

  const formattedAmounts = {
    [Field.LIQUIDITY_PERCENT]: parsedAmounts[Field.LIQUIDITY_PERCENT].equalTo(
      '0',
    )
      ? '0'
      : parsedAmounts[Field.LIQUIDITY_PERCENT].lessThan(new Percent('1', '100'))
      ? '<1'
      : parsedAmounts[Field.LIQUIDITY_PERCENT].toFixed(0),
    [Field.LIQUIDITY]:
      independentField === Field.LIQUIDITY
        ? typedValue
        : parsedAmounts[Field.LIQUIDITY]?.toExact() ?? '',
    [Field.CURRENCY_A]:
      independentField === Field.CURRENCY_A
        ? typedValue
        : parsedAmounts[Field.CURRENCY_A]?.toExact() ?? '',
    [Field.CURRENCY_B]:
      independentField === Field.CURRENCY_B
        ? typedValue
        : parsedAmounts[Field.CURRENCY_B]?.toExact() ?? '',
  };

  const [token0Deposited, token1Deposited] =
    !!pair &&
    !!totalPoolTokens &&
    !!userPoolBalance &&
    JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
      ? [
          pair.getLiquidityValue(
            pair.token0,
            totalPoolTokens,
            userPoolBalance,
            false,
          ),
          pair.getLiquidityValue(
            pair.token1,
            totalPoolTokens,
            userPoolBalance,
            false,
          ),
        ]
      : [undefined, undefined];

  const pairContract: Contract | null = usePairContract(
    pair?.liquidityToken?.address,
  );
  const [approval, approveCallback] = useApproveCallback(
    parsedAmounts[Field.LIQUIDITY],
    chainId ? GlobalConst.addresses.ROUTER_ADDRESS[chainId] : undefined,
  );
  const onAttemptToApprove = async () => {
    if (!pairContract || !pair || !library || !deadline) {
      setErrorMsg('missing dependencies');
      return;
    }
    const liquidityAmount = parsedAmounts[Field.LIQUIDITY];
    if (!liquidityAmount) {
      setErrorMsg('missing liquidity amount');
      return;
    }
    setApproving(true);
    try {
      await approveCallback();
      setApproving(false);
    } catch (e) {
      setApproving(false);
    }
  };

  const handleDismissConfirmation = useCallback(() => {
    setShowConfirm(false);
    setTxHash('');
  }, []);

  const router = useRouterContract();

  const onRemove = async () => {
    if (!chainId || !library || !account || !deadline || !router)
      throw new Error('missing dependencies');
    const {
      [Field.CURRENCY_A]: currencyAmountA,
      [Field.CURRENCY_B]: currencyAmountB,
    } = parsedAmounts;
    if (!currencyAmountA || !currencyAmountB) {
      throw new Error('missing currency amounts');
    }

    const amountsMin = {
      [Field.CURRENCY_A]: calculateSlippageAmount(
        currencyAmountA,
        allowedSlippage,
      )[0],
      [Field.CURRENCY_B]: calculateSlippageAmount(
        currencyAmountB,
        allowedSlippage,
      )[0],
    };

    const liquidityAmount = parsedAmounts[Field.LIQUIDITY];
    if (!liquidityAmount) throw new Error('missing liquidity amount');

    const currencyBIsETH = currency1 === ETHER;
    const oneCurrencyIsETH = currency0 === ETHER || currencyBIsETH;

    if (!tokenA || !tokenB) throw new Error('could not wrap');

    let methodNames: string[],
      args: Array<string | string[] | number | boolean>;
    // we have approval, use normal remove liquidity
    if (approval === ApprovalState.APPROVED) {
      // removeLiquidityETH
      if (oneCurrencyIsETH) {
        methodNames = [
          'removeLiquidityETH',
          'removeLiquidityETHSupportingFeeOnTransferTokens',
        ];
        args = [
          currencyBIsETH ? tokenA.address : tokenB.address,
          liquidityAmount.raw.toString(),
          amountsMin[
            currencyBIsETH ? Field.CURRENCY_A : Field.CURRENCY_B
          ].toString(),
          amountsMin[
            currencyBIsETH ? Field.CURRENCY_B : Field.CURRENCY_A
          ].toString(),
          account,
          deadline.toHexString(),
        ];
      }
      // removeLiquidity
      else {
        methodNames = ['removeLiquidity'];
        args = [
          tokenA.address,
          tokenB.address,
          liquidityAmount.raw.toString(),
          amountsMin[Field.CURRENCY_A].toString(),
          amountsMin[Field.CURRENCY_B].toString(),
          account,
          deadline.toHexString(),
        ];
      }
    } else {
      throw new Error(
        'Attempting to confirm without approval. Please contact support.',
      );
    }

    const safeGasEstimates: (BigNumber | undefined)[] = await Promise.all(
      methodNames.map((methodName) =>
        router.estimateGas[methodName](...args)
          .then(calculateGasMargin)
          .catch((error) => {
            console.error(`estimateGas failed`, methodName, args, error);
            return undefined;
          }),
      ),
    );

    const indexOfSuccessfulEstimation = safeGasEstimates.findIndex(
      (safeGasEstimate) => BigNumber.isBigNumber(safeGasEstimate),
    );

    // all estimations failed...
    if (indexOfSuccessfulEstimation === -1) {
      console.error('This transaction would fail. Please contact support.');
    } else {
      const methodName = methodNames[indexOfSuccessfulEstimation];
      const safeGasEstimate = safeGasEstimates[indexOfSuccessfulEstimation];

      setAttemptingTxn(true);
      await router[methodName](...args, {
        gasLimit: safeGasEstimate,
      })
        .then(async (response: TransactionResponse) => {
          setAttemptingTxn(false);
          setTxPending(true);
          const summary =
            'Remove ' +
            parsedAmounts[Field.CURRENCY_A]?.toSignificant(3) +
            ' ' +
            currency0.symbol +
            ' and ' +
            parsedAmounts[Field.CURRENCY_B]?.toSignificant(3) +
            ' ' +
            currency1.symbol;

          addTransaction(response, {
            summary,
          });

          setTxHash(response.hash);

          try {
            const receipt = await response.wait();
            finalizedTransaction(receipt, {
              summary,
            });
            setTxPending(false);
          } catch (error) {
            setTxPending(false);
            setRemoveErrorMessage('There is an error in transaction.');
          }

          ReactGA.event({
            category: 'Liquidity',
            action: 'Remove',
            label: [currency0.symbol, currency1.symbol].join('/'),
          });
        })
        .catch((error: Error) => {
          setAttemptingTxn(false);
          // we only care if the error is something _other_ than the user rejected the tx
          console.error(error);
        });
    }
  };

  const modalHeader = () => {
    return (
      <Box>
        <Box mt={10} mb={3} display='flex' justifyContent='center'>
          <DoubleCurrencyLogo
            currency0={currency0}
            currency1={currency1}
            size={48}
          />
        </Box>
        <Box mb={6} color={palette.text.primary} textAlign='center'>
          <Typography variant='h6'>
            Removing {formattedAmounts[Field.LIQUIDITY]} {currency0.symbol} /{' '}
            {currency1.symbol} LP Tokens
            <br />
            You will receive {parsedAmounts[Field.CURRENCY_A]?.toSignificant(
              2,
            )}{' '}
            {currency0.symbol} and{' '}
            {parsedAmounts[Field.CURRENCY_B]?.toSignificant(2)}{' '}
            {currency1.symbol}
          </Typography>
        </Box>
        <Box mb={3} color={palette.text.secondary} textAlign='center'>
          <Typography variant='body2'>
            {`Output is estimated. If the price changes by more than ${allowedSlippage /
              100}% your transaction will revert.`}
          </Typography>
        </Box>
        <Box mt={2}>
          <Button
            style={{ width: '100%' }}
            className={classes.removeButton}
            onClick={onRemove}
          >
            Confirm
          </Button>
        </Box>
      </Box>
    );
  };

  return (
    <CustomModal open={open} onClose={onClose}>
      <Box paddingX={3} paddingY={4}>
        {showConfirm && (
          <TransactionConfirmationModal
            isOpen={showConfirm}
            onDismiss={handleDismissConfirmation}
            attemptingTxn={attemptingTxn}
            txPending={txPending}
            hash={txHash}
            content={() =>
              removeErrorMessage ? (
                <TransactionErrorContent
                  onDismiss={handleDismissConfirmation}
                  message={removeErrorMessage}
                />
              ) : (
                <ConfirmationModalContent
                  title='Removing Liquidity'
                  onDismiss={handleDismissConfirmation}
                  content={modalHeader}
                />
              )
            }
            pendingText=''
            modalContent={
              txPending
                ? 'Submitted transaction to remove liquidity'
                : 'Successfully removed liquidity'
            }
          />
        )}
        <Box display='flex' alignItems='center' justifyContent='space-between'>
          <ArrowLeft
            color={palette.text.secondary}
            style={{ cursor: 'pointer' }}
            onClick={onClose}
          />
          <Typography
            variant='subtitle2'
            style={{ color: palette.text.primary }}
          >
            Remove Liquidity
          </Typography>
          <CloseIcon style={{ cursor: 'pointer' }} onClick={onClose} />
        </Box>
        <Box
          mt={3}
          bgcolor={palette.background.default}
          border='1px solid rgba(105, 108, 128, 0.12)'
          borderRadius='10px'
          padding='16px'
        >
          <Box
            display='flex'
            alignItems='center'
            justifyContent='space-between'
          >
            <Typography variant='body2'>
              {currency0.symbol} / {currency1.symbol} LP
            </Typography>
            <Typography variant='body2'>
              Balance: {formatTokenAmount(userPoolBalance)}
            </Typography>
          </Box>
          <Box mt={2}>
            <NumericalInput
              placeholder='0'
              value={formattedAmounts[Field.LIQUIDITY]}
              fontSize={28}
              onUserInput={(value) => {
                onLiquidityInput(value);
              }}
            />
          </Box>
          <Box display='flex' alignItems='center'>
            <Box flex={1} mr={2} mt={0.5}>
              <ColoredSlider
                min={1}
                max={100}
                step={1}
                value={innerLiquidityPercentage}
                onChange={(evt: any, value) =>
                  setInnerLiquidityPercentage(value as number)
                }
              />
            </Box>
            <Typography variant='body2'>
              {formattedAmounts[Field.LIQUIDITY_PERCENT]}%
            </Typography>
          </Box>
        </Box>
        <Box display='flex' my={3} justifyContent='center'>
          <ArrowDown color={palette.text.secondary} />
        </Box>
        <Box
          padding='16px'
          bgcolor={palette.secondary.light}
          borderRadius='10px'
        >
          <Box
            display='flex'
            justifyContent='space-between'
            alignItems='center'
          >
            <Typography variant='body1'>Pooled {currency0.symbol}</Typography>
            <Box display='flex' alignItems='center'>
              <Typography variant='body1' style={{ marginRight: 6 }}>
                {formatTokenAmount(token0Deposited)}
              </Typography>
              <CurrencyLogo currency={currency0} />
            </Box>
          </Box>
          <Box
            mt={1}
            display='flex'
            justifyContent='space-between'
            alignItems='center'
          >
            <Typography
              variant='body1'
              style={{ color: 'rgba(68, 138, 255, 0.5)' }}
            >
              - Withdraw {currency0.symbol}
            </Typography>
            <Typography
              variant='body1'
              style={{ color: 'rgba(68, 138, 255, 0.5)' }}
            >
              {formattedAmounts[Field.CURRENCY_A]}
            </Typography>
          </Box>
          <Box
            mt={1}
            display='flex'
            justifyContent='space-between'
            alignItems='center'
          >
            <Typography variant='body1'>Pooled {currency1.symbol}</Typography>
            <Box display='flex' alignItems='center'>
              <Typography variant='body1' style={{ marginRight: 6 }}>
                {formatTokenAmount(token1Deposited)}
              </Typography>
              <CurrencyLogo currency={currency1} />
            </Box>
          </Box>
          <Box
            mt={1}
            display='flex'
            justifyContent='space-between'
            alignItems='center'
          >
            <Typography
              variant='body1'
              style={{ color: 'rgba(68, 138, 255, 0.5)' }}
            >
              - Withdraw {currency1.symbol}
            </Typography>
            <Typography
              variant='body1'
              style={{ color: 'rgba(68, 138, 255, 0.5)' }}
            >
              {formattedAmounts[Field.CURRENCY_B]}
            </Typography>
          </Box>
          <Box
            mt={1}
            display='flex'
            justifyContent='space-between'
            alignItems='center'
          >
            <Typography variant='body1'>Your Pool Share</Typography>
            <Typography variant='body1'>
              {poolTokenPercentage
                ? poolTokenPercentage.toSignificant() + '%'
                : '-'}
            </Typography>
          </Box>
        </Box>
        {pair && (
          <Box
            display='flex'
            mt={2}
            px={2}
            alignItems='center'
            justifyContent='space-between'
          >
            <Typography variant='body2'>
              1 {currency0.symbol} ={' '}
              {tokenA ? pair.priceOf(tokenA).toSignificant(6) : '-'}{' '}
              {currency1.symbol}
            </Typography>
            <Typography variant='body2'>
              1 {currency1.symbol} ={' '}
              {tokenB ? pair.priceOf(tokenB).toSignificant(6) : '-'}{' '}
              {currency0.symbol}
            </Typography>
          </Box>
        )}
        <Box
          mt={2}
          display='flex'
          alignItems='center'
          justifyContent='space-between'
        >
          <Button
            className={classes.removeButton}
            onClick={onAttemptToApprove}
            disabled={approving || approval !== ApprovalState.NOT_APPROVED}
          >
            {approving
              ? 'Approving...'
              : approval === ApprovalState.APPROVED
              ? 'Approved'
              : 'Approve'}
          </Button>
          <Button
            className={classes.removeButton}
            onClick={() => {
              setShowConfirm(true);
            }}
            disabled={Boolean(error) || approval !== ApprovalState.APPROVED}
          >
            {error || 'Remove'}
          </Button>
        </Box>
        <Box mt={2}>
          <Typography variant='body1' style={{ color: palette.error.main }}>
            {errorMsg}
          </Typography>
        </Box>
      </Box>
    </CustomModal>
  );
}
Example #16
Source File: PositionCard.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
FullPositionCard: React.FC<PositionCardProps> = ({ pair }) => {
  const { account } = useActiveWeb3React();

  const currency0 = unwrappedToken(pair.token0);
  const currency1 = unwrappedToken(pair.token1);

  const [showMore, setShowMore] = useState(false);

  const userPoolBalance = useTokenBalance(
    account ?? undefined,
    pair.liquidityToken,
  );
  const totalPoolTokens = useTotalSupply(pair.liquidityToken);

  const poolTokenPercentage =
    !!userPoolBalance &&
    !!totalPoolTokens &&
    JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
      ? new Percent(userPoolBalance.raw, totalPoolTokens.raw)
      : undefined;

  const [token0Deposited, token1Deposited] =
    !!pair &&
    !!totalPoolTokens &&
    !!userPoolBalance &&
    // this condition is a short-circuit in the case where useTokenBalance updates sooner than useTotalSupply
    JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
      ? [
          pair.getLiquidityValue(
            pair.token0,
            totalPoolTokens,
            userPoolBalance,
            false,
          ),
          pair.getLiquidityValue(
            pair.token1,
            totalPoolTokens,
            userPoolBalance,
            false,
          ),
        ]
      : [undefined, undefined];

  return (
    <Box>
      <Box>
        <Box>
          <DoubleCurrencyLogo
            currency0={currency0}
            currency1={currency1}
            margin={true}
            size={20}
          />
          <Typography>
            {!currency0 || !currency1 ? (
              <Typography>Loading</Typography>
            ) : (
              `${currency0.symbol}/${currency1.symbol}`
            )}
          </Typography>
        </Box>

        <Button onClick={() => setShowMore(!showMore)}>
          {showMore ? (
            <>
              {' '}
              Manage
              <ChevronUp size='20' style={{ marginLeft: '10px' }} />
            </>
          ) : (
            <>
              Manage
              <ChevronDown size='20' style={{ marginLeft: '10px' }} />
            </>
          )}
        </Button>
      </Box>

      {showMore && (
        <Box>
          <Box>
            <Typography>Your pool tokens:</Typography>
            <Typography>{formatTokenAmount(userPoolBalance)}</Typography>
          </Box>
          <Box>
            <Typography>Pooled {currency0.symbol}:</Typography>
            <Box>
              <Typography>{formatTokenAmount(token0Deposited)}</Typography>
              <CurrencyLogo
                size='20px'
                style={{ marginLeft: '8px' }}
                currency={currency0}
              />
            </Box>
          </Box>

          <Box>
            <Typography>Pooled {currency1.symbol}:</Typography>
            <Box>
              <Typography>{formatTokenAmount(token1Deposited)}</Typography>
              <CurrencyLogo
                size='20px'
                style={{ marginLeft: '8px' }}
                currency={currency1}
              />
            </Box>
          </Box>

          <Box>
            <Typography>Your pool share:</Typography>
            <Typography>
              {poolTokenPercentage ? poolTokenPercentage.toFixed(2) + '%' : '-'}
            </Typography>
          </Box>

          <Button>
            <a
              style={{ width: '100%', textAlign: 'center' }}
              href={`https://info.quickswap.exchange/account/${account}`}
              target='_blank'
              rel='noopener noreferrer'
            >
              View accrued fees and analytics
              <span style={{ fontSize: '11px' }}>↗</span>
            </a>
          </Button>
          <Box display='flex'>
            <Box width='48%'>
              <Link
                to={`/add/${currencyId(currency0)}/${currencyId(currency1)}`}
              >
                Add
              </Link>
            </Box>
            <Box width='48%'>
              <Link
                to={`/remove/${currencyId(currency0)}/${currencyId(currency1)}`}
              >
                Remove
              </Link>
            </Box>
          </Box>
        </Box>
      )}
    </Box>
  );
}
Example #17
Source File: PoolPositionCardDetails.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
PoolPositionCardDetails: React.FC<{ pair: Pair }> = ({ pair }) => {
  const classes = useStyles();
  const history = useHistory();
  const { breakpoints } = useTheme();
  const isMobile = useMediaQuery(breakpoints.down('xs'));

  const { account } = useActiveWeb3React();
  const [openRemoveModal, setOpenRemoveModal] = useState(false);

  const currency0 = unwrappedToken(pair.token0);
  const currency1 = unwrappedToken(pair.token1);

  const userPoolBalance = useTokenBalance(
    account ?? undefined,
    pair.liquidityToken,
  );
  const totalPoolTokens = useTotalSupply(pair.liquidityToken);

  const poolTokenPercentage =
    !!userPoolBalance &&
    !!totalPoolTokens &&
    JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
      ? new Percent(userPoolBalance.raw, totalPoolTokens.raw)
      : undefined;

  const [token0Deposited, token1Deposited] =
    !!pair &&
    !!totalPoolTokens &&
    !!userPoolBalance &&
    // this condition is a short-circuit in the case where useTokenBalance updates sooner than useTotalSupply
    JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
      ? [
          pair.getLiquidityValue(
            pair.token0,
            totalPoolTokens,
            userPoolBalance,
            false,
          ),
          pair.getLiquidityValue(
            pair.token1,
            totalPoolTokens,
            userPoolBalance,
            false,
          ),
        ]
      : [undefined, undefined];

  return (
    <>
      <Box px={isMobile ? 1.5 : 3} mb={3}>
        <Box className={classes.cardRow}>
          <Typography variant='body2'>Your pool tokens:</Typography>
          <Typography variant='body2'>
            {formatTokenAmount(userPoolBalance)}
          </Typography>
        </Box>
        <Box className={classes.cardRow}>
          <Typography variant='body2'>Pooled {currency0.symbol}:</Typography>
          <Box display='flex' alignItems='center'>
            <Typography variant='body2' style={{ marginRight: 10 }}>
              {formatTokenAmount(token0Deposited)}
            </Typography>
            <CurrencyLogo size='20px' currency={currency0} />
          </Box>
        </Box>

        <Box className={classes.cardRow}>
          <Typography variant='body2'>Pooled {currency1.symbol}:</Typography>
          <Box display='flex' alignItems='center'>
            <Typography variant='body2' style={{ marginRight: 10 }}>
              {formatTokenAmount(token1Deposited)}
            </Typography>
            <CurrencyLogo size='20px' currency={currency1} />
          </Box>
        </Box>

        <Box className={classes.cardRow}>
          <Typography variant='body2'>Your pool share:</Typography>
          <Typography variant='body2'>
            {poolTokenPercentage
              ? poolTokenPercentage.toSignificant() + '%'
              : '-'}
          </Typography>
        </Box>

        <Box className={classes.poolButtonRow}>
          <Button
            variant='outlined'
            onClick={() =>
              history.push(`/analytics/pair/${pair.liquidityToken.address}`)
            }
          >
            <Typography variant='body2'>View Analytics</Typography>
          </Button>
          <Button
            variant='contained'
            onClick={() => {
              history.push(
                `/pools?currency0=${currencyId(
                  currency0,
                )}&currency1=${currencyId(currency1)}`,
              );
            }}
          >
            <Typography variant='body2'>Add</Typography>
          </Button>
          <Button
            variant='contained'
            onClick={() => {
              setOpenRemoveModal(true);
            }}
          >
            <Typography variant='body2'>Remove</Typography>
          </Button>
        </Box>
      </Box>
      {openRemoveModal && (
        <RemoveLiquidityModal
          currency0={currency0}
          currency1={currency1}
          open={openRemoveModal}
          onClose={() => setOpenRemoveModal(false)}
        />
      )}
    </>
  );
}
Example #18
Source File: PoolFinderModal.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
PoolFinderModal: React.FC<PoolFinderModalProps> = ({ open, onClose }) => {
  const classes = useStyles();
  const { palette } = useTheme();

  const { account } = useActiveWeb3React();

  const [showSearch, setShowSearch] = useState<boolean>(false);
  const [activeField, setActiveField] = useState<number>(Fields.TOKEN1);

  const [currency0, setCurrency0] = useState<Currency | null>(ETHER);
  const [currency1, setCurrency1] = useState<Currency | null>(null);

  const [pairState, pair] = usePair(
    currency0 ?? undefined,
    currency1 ?? undefined,
  );
  const addPair = usePairAdder();
  useEffect(() => {
    if (pair) {
      addPair(pair);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [pair?.liquidityToken.address, addPair]);

  const validPairNoLiquidity: boolean =
    pairState === PairState.NOT_EXISTS ||
    Boolean(
      pairState === PairState.EXISTS &&
        pair &&
        JSBI.equal(pair.reserve0.raw, JSBI.BigInt(0)) &&
        JSBI.equal(pair.reserve1.raw, JSBI.BigInt(0)),
    );

  const position: TokenAmount | undefined = useTokenBalance(
    account ?? undefined,
    pair?.liquidityToken,
  );
  const hasPosition = Boolean(
    position && JSBI.greaterThan(position.raw, JSBI.BigInt(0)),
  );

  const handleCurrencySelect = useCallback(
    (currency: Currency) => {
      if (activeField === Fields.TOKEN0) {
        setCurrency0(currency);
      } else {
        setCurrency1(currency);
      }
    },
    [activeField],
  );

  const handleSearchDismiss = useCallback(() => {
    setShowSearch(false);
  }, [setShowSearch]);

  return (
    <CustomModal open={open} onClose={onClose}>
      <Box paddingX={3} paddingY={4}>
        <Box display='flex' alignItems='center' justifyContent='space-between'>
          <ArrowLeft
            color={palette.text.secondary}
            style={{ cursor: 'pointer' }}
            onClick={onClose}
          />
          <Typography
            variant='subtitle2'
            style={{ color: palette.text.primary }}
          >
            Import Pool
          </Typography>
          <CloseIcon style={{ cursor: 'pointer' }} onClick={onClose} />
        </Box>
        <Box
          mt={2}
          className={classes.borderedCard}
          onClick={() => {
            setShowSearch(true);
            setActiveField(Fields.TOKEN0);
          }}
        >
          {currency0 ? (
            <Box display='flex' alignItems='center'>
              <CurrencyLogo currency={currency0} size='20px' />
              <Typography variant='h6' style={{ marginLeft: 6 }}>
                {currency0.symbol}
              </Typography>
            </Box>
          ) : (
            <Typography variant='h6'>Select a Token</Typography>
          )}
        </Box>
        <Box my={1} display='flex' justifyContent='center'>
          <Plus size='20' color={palette.text.secondary} />
        </Box>
        <Box
          className={classes.borderedCard}
          onClick={() => {
            setShowSearch(true);
            setActiveField(Fields.TOKEN1);
          }}
        >
          {currency1 ? (
            <Box display='flex'>
              <CurrencyLogo currency={currency1} />
              <Typography variant='h6' style={{ marginLeft: 6 }}>
                {currency1.symbol}
              </Typography>
            </Box>
          ) : (
            <Typography variant='h6'>Select a Token</Typography>
          )}
        </Box>
        {hasPosition && (
          <Box textAlign='center' mt={2}>
            <Typography variant='body1'>Pool Found!</Typography>
            <Typography
              variant='body1'
              style={{ cursor: 'pointer', color: palette.primary.main }}
              onClick={onClose}
            >
              Manage this pool.
            </Typography>
          </Box>
        )}
        <Box
          mt={2}
          p={1}
          borderRadius={10}
          display='flex'
          justifyContent='center'
          border={`1px solid ${palette.divider}`}
        >
          {currency0 && currency1 ? (
            pairState === PairState.EXISTS ? (
              hasPosition && pair ? (
                <MinimalPositionCard pair={pair} border='none' />
              ) : (
                <Box textAlign='center'>
                  <Typography>
                    You don’t have liquidity in this pool yet.
                  </Typography>
                  <Link
                    to={`/pools?currency0=${currencyId(
                      currency0,
                    )}&currency1=${currencyId(currency1)}`}
                    style={{
                      color: palette.primary.main,
                      textDecoration: 'none',
                    }}
                    onClick={onClose}
                  >
                    <Typography>Add liquidity.</Typography>
                  </Link>
                </Box>
              )
            ) : validPairNoLiquidity ? (
              <Box textAlign='center'>
                <Typography>No pool found.</Typography>
                <Link
                  to={`/pools?currency0=${currencyId(
                    currency0,
                  )}&currency1=${currencyId(currency1)}`}
                  style={{
                    color: palette.primary.main,
                    textDecoration: 'none',
                  }}
                  onClick={onClose}
                >
                  Create pool.
                </Link>
              </Box>
            ) : pairState === PairState.INVALID ? (
              <Typography>Invalid pair.</Typography>
            ) : pairState === PairState.LOADING ? (
              <Typography>Loading...</Typography>
            ) : null
          ) : (
            <Typography>
              {!account
                ? 'Connect to a wallet to find pools'
                : 'Select a token to find your liquidity.'}
            </Typography>
          )}
        </Box>
      </Box>
      {showSearch && (
        <CurrencySearchModal
          isOpen={showSearch}
          onCurrencySelect={handleCurrencySelect}
          onDismiss={handleSearchDismiss}
          showCommonBases
          selectedCurrency={
            (activeField === Fields.TOKEN0 ? currency1 : currency0) ?? undefined
          }
        />
      )}
    </CustomModal>
  );
}
Example #19
Source File: FarmCardDetails.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
FarmCardDetails: React.FC<{
  stakingInfo: StakingInfo | DualStakingInfo;
  stakingAPY: number;
  isLPFarm?: boolean;
}> = ({ stakingInfo, stakingAPY, isLPFarm }) => {
  const classes = useStyles();
  const { t } = useTranslation();
  const { palette, breakpoints } = useTheme();
  const isMobile = useMediaQuery(breakpoints.down('xs'));
  const [stakeAmount, setStakeAmount] = useState('');
  const [attemptStaking, setAttemptStaking] = useState(false);
  const [attemptUnstaking, setAttemptUnstaking] = useState(false);
  const [attemptClaiming, setAttemptClaiming] = useState(false);
  const [approving, setApproving] = useState(false);
  const [unstakeAmount, setUnStakeAmount] = useState('');

  const lpStakingInfo = stakingInfo as StakingInfo;
  const dualStakingInfo = stakingInfo as DualStakingInfo;

  const token0 = stakingInfo ? stakingInfo.tokens[0] : undefined;
  const token1 = stakingInfo ? stakingInfo.tokens[1] : undefined;

  const { account, library } = useActiveWeb3React();
  const addTransaction = useTransactionAdder();

  const currency0 = token0 ? unwrappedToken(token0) : undefined;
  const currency1 = token1 ? unwrappedToken(token1) : undefined;

  const userLiquidityUnstaked = useTokenBalance(
    account ?? undefined,
    stakingInfo.stakedAmount?.token,
  );

  const stakedAmounts = getStakedAmountStakingInfo(
    stakingInfo,
    userLiquidityUnstaked,
  );

  let apyWithFee: number | string = 0;

  if (
    stakingInfo &&
    stakingInfo.perMonthReturnInRewards &&
    stakingAPY &&
    stakingAPY > 0
  ) {
    apyWithFee = formatAPY(
      getAPYWithFee(stakingInfo.perMonthReturnInRewards, stakingAPY),
    );
  }

  const stakingContract = useStakingContract(stakingInfo?.stakingRewardAddress);

  const { parsedAmount: unstakeParsedAmount } = useDerivedStakeInfo(
    unstakeAmount,
    stakingInfo.stakedAmount?.token,
    stakingInfo.stakedAmount,
  );

  const onWithdraw = async () => {
    if (stakingInfo && stakingContract && unstakeParsedAmount) {
      setAttemptUnstaking(true);
      await stakingContract
        .withdraw(`0x${unstakeParsedAmount.raw.toString(16)}`, {
          gasLimit: 300000,
        })
        .then(async (response: TransactionResponse) => {
          addTransaction(response, {
            summary: t('withdrawliquidity'),
          });
          try {
            await response.wait();
            setAttemptUnstaking(false);
          } catch (error) {
            setAttemptUnstaking(false);
          }
        })
        .catch((error: any) => {
          setAttemptUnstaking(false);
          console.log(error);
        });
    }
  };

  const onClaimReward = async () => {
    if (stakingContract && stakingInfo && stakingInfo.stakedAmount) {
      setAttemptClaiming(true);
      await stakingContract
        .getReward({ gasLimit: 350000 })
        .then(async (response: TransactionResponse) => {
          addTransaction(response, {
            summary: t('claimrewards'),
          });
          try {
            await response.wait();
            setAttemptClaiming(false);
          } catch (error) {
            setAttemptClaiming(false);
          }
        })
        .catch((error: any) => {
          setAttemptClaiming(false);
          console.log(error);
        });
    }
  };

  const { parsedAmount } = useDerivedStakeInfo(
    stakeAmount,
    stakingInfo.stakedAmount?.token,
    userLiquidityUnstaked,
  );
  const deadline = useTransactionDeadline();
  const [approval, approveCallback] = useApproveCallback(
    parsedAmount,
    stakingInfo?.stakingRewardAddress,
  );

  const dummyPair = stakingInfo
    ? new Pair(
        new TokenAmount(stakingInfo.tokens[0], '0'),
        new TokenAmount(stakingInfo.tokens[1], '0'),
      )
    : null;
  const pairContract = usePairContract(
    stakingInfo && stakingInfo.lp && stakingInfo.lp !== ''
      ? stakingInfo.lp
      : dummyPair?.liquidityToken.address,
  );

  const onStake = async () => {
    if (stakingContract && parsedAmount && deadline) {
      setAttemptStaking(true);
      stakingContract
        .stake(`0x${parsedAmount.raw.toString(16)}`, {
          gasLimit: 350000,
        })
        .then(async (response: TransactionResponse) => {
          addTransaction(response, {
            summary: t('depositliquidity'),
          });
          try {
            await response.wait();
            setAttemptStaking(false);
          } catch (error) {
            setAttemptStaking(false);
          }
        })
        .catch((error: any) => {
          setAttemptStaking(false);
          console.log(error);
        });
    } else {
      throw new Error(t('stakewithoutapproval'));
    }
  };

  const onAttemptToApprove = async () => {
    if (!pairContract || !library || !deadline)
      throw new Error(t('missingdependencies'));
    const liquidityAmount = parsedAmount;
    if (!liquidityAmount) throw new Error(t('missingliquidity'));
    setApproving(true);
    try {
      await approveCallback();
      setApproving(false);
    } catch (e) {
      setApproving(false);
    }
  };

  const earnedUSDStr = isLPFarm
    ? getEarnedUSDLPFarm(lpStakingInfo)
    : getEarnedUSDDualFarm(dualStakingInfo);

  const tvl = getTVLStaking(
    stakedAmounts?.totalStakedUSD,
    stakedAmounts?.totalStakedBase,
  );

  const lpRewards = lpStakingInfo.rate * lpStakingInfo.rewardTokenPrice;

  const lpPoolRate = getRewardRate(
    lpStakingInfo.totalRewardRate,
    lpStakingInfo.rewardToken,
  );

  const dualRewards =
    dualStakingInfo.rateA * dualStakingInfo.rewardTokenAPrice +
    dualStakingInfo.rateB * Number(dualStakingInfo.rewardTokenBPrice);

  const dualPoolRateA = getRewardRate(
    dualStakingInfo.totalRewardRateA,
    dualStakingInfo.rewardTokenA,
  );
  const dualPoolRateB = getRewardRate(
    dualStakingInfo.totalRewardRateB,
    dualStakingInfo.rewardTokenB,
  );

  const mainRewardRate = isLPFarm
    ? lpStakingInfo.rewardRate
    : dualStakingInfo.rewardRateA;

  const stakeEnabled =
    !approving &&
    !attemptStaking &&
    Number(stakeAmount) > 0 &&
    Number(stakeAmount) <= getExactTokenAmount(userLiquidityUnstaked);

  const unstakeEnabled =
    !attemptUnstaking &&
    Number(unstakeAmount) > 0 &&
    Number(unstakeAmount) <= getExactTokenAmount(stakingInfo.stakedAmount);

  const claimEnabled =
    !attemptClaiming &&
    (isLPFarm
      ? lpStakingInfo.earnedAmount &&
        lpStakingInfo.earnedAmount.greaterThan('0')
      : dualStakingInfo.earnedAmountA &&
        dualStakingInfo.earnedAmountA.greaterThan('0'));

  return (
    <>
      <Box
        width='100%'
        p={2}
        display='flex'
        flexDirection='row'
        flexWrap='wrap'
        borderTop='1px solid #444444'
        alignItems='center'
        justifyContent={stakingInfo?.ended ? 'flex-end' : 'space-between'}
      >
        {stakingInfo && (
          <>
            {isMobile && (
              <>
                <Box
                  mt={2}
                  width={1}
                  display='flex'
                  justifyContent='space-between'
                >
                  <Typography variant='body2' color='textSecondary'>
                    {t('tvl')}
                  </Typography>
                  <Typography variant='body2'>{tvl}</Typography>
                </Box>
                <Box
                  mt={2}
                  width={1}
                  display='flex'
                  justifyContent='space-between'
                >
                  <Typography variant='body2' color='textSecondary'>
                    {t('rewards')}
                  </Typography>
                  <Box textAlign='right'>
                    <Typography variant='body2'>
                      ${(isLPFarm ? lpRewards : dualRewards).toLocaleString()} /
                      {t('day')}
                    </Typography>
                    {isLPFarm ? (
                      <Typography variant='body2'>{lpPoolRate}</Typography>
                    ) : (
                      <>
                        <Typography variant='body2'>{dualPoolRateA}</Typography>
                        <Typography variant='body2'>{dualPoolRateB}</Typography>
                      </>
                    )}
                  </Box>
                </Box>
                <Box
                  mt={2}
                  width={1}
                  display='flex'
                  justifyContent='space-between'
                >
                  <Box display='flex' alignItems='center'>
                    <Typography variant='body2' color='textSecondary'>
                      {t('apy')}
                    </Typography>
                    <Box ml={0.5} height={16}>
                      <img src={CircleInfoIcon} alt={'arrow up'} />
                    </Box>
                  </Box>
                  <Box color={palette.success.main}>
                    <Typography variant='body2'>{apyWithFee}%</Typography>
                  </Box>
                </Box>
              </>
            )}
            {!stakingInfo.ended && (
              <Box className={classes.buttonWrapper} mt={isMobile ? 2 : 0}>
                <Box display='flex' justifyContent='space-between'>
                  <Typography variant='body2'>{t('inwallet')}:</Typography>
                  <Box
                    display='flex'
                    flexDirection='column'
                    alignItems='flex-end'
                    justifyContent='flex-start'
                  >
                    <Typography variant='body2'>
                      {formatTokenAmount(userLiquidityUnstaked)} {t('lp')}{' '}
                      <span>({getUSDString(stakedAmounts?.unStakedUSD)})</span>
                    </Typography>
                    <Link
                      to={`/pools?currency0=${getTokenAddress(
                        token0,
                      )}&currency1=${getTokenAddress(token1)}`}
                      style={{ color: palette.primary.main }}
                    >
                      {t('get')} {currency0?.symbol} / {currency1?.symbol}{' '}
                      {t('lp')}
                    </Link>
                  </Box>
                </Box>
                <Box className={classes.inputVal} mb={2} mt={2} p={2}>
                  <NumericalInput
                    placeholder='0.00'
                    value={stakeAmount}
                    fontSize={16}
                    onUserInput={(value) => {
                      setStakeAmount(value);
                    }}
                  />
                  <Typography
                    variant='body2'
                    style={{
                      color:
                        userLiquidityUnstaked &&
                        userLiquidityUnstaked.greaterThan('0')
                          ? palette.primary.main
                          : palette.text.hint,
                    }}
                    onClick={() => {
                      if (
                        userLiquidityUnstaked &&
                        userLiquidityUnstaked.greaterThan('0')
                      ) {
                        setStakeAmount(userLiquidityUnstaked.toExact());
                      } else {
                        setStakeAmount('');
                      }
                    }}
                  >
                    {t('max')}
                  </Typography>
                </Box>
                <Box
                  className={
                    stakeEnabled ? classes.buttonClaim : classes.buttonToken
                  }
                  mt={2}
                  p={2}
                  onClick={async () => {
                    if (stakeEnabled) {
                      if (approval === ApprovalState.APPROVED) {
                        onStake();
                      } else {
                        onAttemptToApprove();
                      }
                    }
                  }}
                >
                  <Typography variant='body1'>
                    {attemptStaking
                      ? t('stakingLPTokens')
                      : approval === ApprovalState.APPROVED
                      ? t('stakeLPTokens')
                      : approving
                      ? t('approving')
                      : t('approve')}
                  </Typography>
                </Box>
              </Box>
            )}
            <Box className={classes.buttonWrapper} mx={isMobile ? 0 : 2} my={2}>
              <Box display='flex' justifyContent='space-between'>
                <Typography variant='body2'>{t('mydeposits')}:</Typography>
                <Typography variant='body2'>
                  {formatTokenAmount(stakingInfo.stakedAmount)} {t('lp')}{' '}
                  <span>({getUSDString(stakedAmounts?.myStakedUSD)})</span>
                </Typography>
              </Box>
              <Box className={classes.inputVal} mb={2} mt={4.5} p={2}>
                <NumericalInput
                  placeholder='0.00'
                  value={unstakeAmount}
                  fontSize={16}
                  onUserInput={(value) => {
                    setUnStakeAmount(value);
                  }}
                />
                <Typography
                  variant='body2'
                  style={{
                    color:
                      stakingInfo.stakedAmount &&
                      stakingInfo.stakedAmount.greaterThan('0')
                        ? palette.primary.main
                        : palette.text.hint,
                  }}
                  onClick={() => {
                    if (
                      stakingInfo.stakedAmount &&
                      stakingInfo.stakedAmount.greaterThan('0')
                    ) {
                      setUnStakeAmount(stakingInfo.stakedAmount.toExact());
                    } else {
                      setUnStakeAmount('');
                    }
                  }}
                >
                  {t('max')}
                </Typography>
              </Box>
              <Box
                className={
                  unstakeEnabled ? classes.buttonClaim : classes.buttonToken
                }
                mt={2}
                p={2}
                onClick={() => {
                  if (unstakeEnabled) {
                    onWithdraw();
                  }
                }}
              >
                <Typography variant='body1'>
                  {attemptUnstaking
                    ? t('unstakingLPTokens')
                    : t('unstakeLPTokens')}
                </Typography>
              </Box>
            </Box>
            <Box className={classes.buttonWrapper}>
              <Box
                display='flex'
                flexDirection='column'
                alignItems='center'
                justifyContent='space-between'
              >
                <Box mb={1}>
                  <Typography variant='body2'>
                    {t('unclaimedRewards')}:
                  </Typography>
                </Box>
                {isLPFarm ? (
                  <>
                    <Box mb={1}>
                      <CurrencyLogo currency={lpStakingInfo.rewardToken} />
                    </Box>
                    <Box mb={1} textAlign='center'>
                      <Typography variant='body1' color='textSecondary'>
                        {formatTokenAmount(lpStakingInfo.earnedAmount)}
                        <span>&nbsp;{lpStakingInfo.rewardToken.symbol}</span>
                      </Typography>
                      <Typography variant='body2'>{earnedUSDStr}</Typography>
                    </Box>
                  </>
                ) : (
                  <>
                    <Box mb={1} display='flex'>
                      <CurrencyLogo
                        currency={unwrappedToken(dualStakingInfo.rewardTokenA)}
                      />
                      <CurrencyLogo
                        currency={unwrappedToken(dualStakingInfo.rewardTokenB)}
                      />
                    </Box>
                    <Box mb={1} textAlign='center'>
                      <Typography variant='body1'>{earnedUSDStr}</Typography>
                      <Typography variant='body1' color='textSecondary'>
                        {formatTokenAmount(dualStakingInfo.earnedAmountA)}
                        <span>&nbsp;{dualStakingInfo.rewardTokenA.symbol}</span>
                      </Typography>
                      <Typography variant='body1' color='textSecondary'>
                        {formatTokenAmount(dualStakingInfo.earnedAmountB)}
                        <span>&nbsp;{dualStakingInfo.rewardTokenB.symbol}</span>
                      </Typography>
                    </Box>
                  </>
                )}
              </Box>
              <Box
                className={
                  claimEnabled ? classes.buttonClaim : classes.buttonToken
                }
                p={2}
                onClick={() => {
                  if (claimEnabled) {
                    onClaimReward();
                  }
                }}
              >
                <Typography variant='body1'>
                  {attemptClaiming ? t('claiming') : t('claim')}
                </Typography>
              </Box>
            </Box>
          </>
        )}
      </Box>
      {mainRewardRate?.greaterThan('0') && (
        <Box className={classes.dailyRateWrapper}>
          <Box
            display='flex'
            alignItems='center'
            justifyContent={isMobile ? 'space-between' : 'flex-start'}
            mr={isMobile ? 0 : 1.5}
            width={isMobile ? 1 : 'auto'}
            mb={isMobile ? 1 : 0}
            flexWrap='wrap'
          >
            <Box display='flex' mr={1}>
              <Typography variant='body2' color='textSecondary'>
                {t('yourRate', {
                  symbol: isLPFarm ? '' : dualStakingInfo.rewardTokenA.symbol,
                })}
                :
              </Typography>
            </Box>
            <Typography variant='body2' color='textPrimary'>
              {formatMulDivTokenAmount(
                mainRewardRate,
                GlobalConst.utils.ONEDAYSECONDS,
              )}{' '}
              {isLPFarm
                ? lpStakingInfo.rewardToken.symbol
                : dualStakingInfo.rewardTokenA.symbol}{' '}
              / {t('day')}
            </Typography>
          </Box>
          {!isLPFarm && (
            <Box
              display='flex'
              alignItems='center'
              justifyContent={isMobile ? 'space-between' : 'flex-start'}
              mr={isMobile ? 0 : 1.5}
              width={isMobile ? 1 : 'auto'}
              mb={isMobile ? 1 : 0}
              flexWrap='wrap'
            >
              <Box display='flex' mr={1}>
                <Typography variant='body2' color='textSecondary'>
                  {t('yourRate', {
                    symbol: dualStakingInfo.rewardTokenB.symbol,
                  })}
                  :
                </Typography>
              </Box>
              <Typography variant='body2' color='textPrimary'>
                {formatMulDivTokenAmount(
                  dualStakingInfo.rewardRateB,
                  GlobalConst.utils.ONEDAYSECONDS,
                )}{' '}
                {dualStakingInfo.rewardTokenB.symbol} / {t('day')}
              </Typography>
            </Box>
          )}
          <Box
            display='flex'
            justifyContent={isMobile ? 'space-between' : 'flex-start'}
            alignItems='center'
            width={isMobile ? 1 : 'auto'}
            flexWrap='wrap'
          >
            <Box display='flex' mr={1}>
              <Typography variant='body2' color='textSecondary'>
                {t('yourFees')}:
              </Typography>
            </Box>
            <Typography variant='body2' color='textPrimary'>
              ${formatNumber(stakingInfo.accountFee)} / {t('day')}
            </Typography>
          </Box>
        </Box>
      )}
    </>
  );
}
Example #20
Source File: FarmCard.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
FarmCard: React.FC<{
  stakingInfo: StakingInfo | DualStakingInfo;
  stakingAPY: number;
  isLPFarm?: boolean;
}> = ({ stakingInfo, stakingAPY, isLPFarm }) => {
  const classes = useStyles();
  const { palette, breakpoints } = useTheme();
  const isMobile = useMediaQuery(breakpoints.down('xs'));
  const [isExpandCard, setExpandCard] = useState(false);

  const lpStakingInfo = stakingInfo as StakingInfo;
  const dualStakingInfo = stakingInfo as DualStakingInfo;

  const token0 = stakingInfo.tokens[0];
  const token1 = stakingInfo.tokens[1];

  const currency0 = unwrappedToken(token0);
  const currency1 = unwrappedToken(token1);

  const stakedAmounts = getStakedAmountStakingInfo(stakingInfo);

  let apyWithFee: number | string = 0;

  if (stakingAPY && stakingAPY > 0 && stakingInfo.perMonthReturnInRewards) {
    apyWithFee = formatAPY(
      getAPYWithFee(stakingInfo.perMonthReturnInRewards, stakingAPY),
    );
  }

  const tvl = getTVLStaking(
    stakedAmounts?.totalStakedUSD,
    stakedAmounts?.totalStakedBase,
  );

  const lpPoolRate = getRewardRate(
    lpStakingInfo.totalRewardRate,
    lpStakingInfo.rewardToken,
  );

  const dualPoolRateA = getRewardRate(
    dualStakingInfo.totalRewardRateA,
    dualStakingInfo.rewardTokenA,
  );
  const dualPoolRateB = getRewardRate(
    dualStakingInfo.totalRewardRateB,
    dualStakingInfo.rewardTokenB,
  );

  const earnedUSDStr = isLPFarm
    ? getEarnedUSDLPFarm(lpStakingInfo)
    : getEarnedUSDDualFarm(dualStakingInfo);

  const lpRewards = lpStakingInfo.rewardTokenPrice * lpStakingInfo.rate;
  const dualRewards =
    dualStakingInfo.rateA * dualStakingInfo.rewardTokenAPrice +
    dualStakingInfo.rateB * dualStakingInfo.rewardTokenBPrice;

  const renderPool = (width: number) => (
    <Box display='flex' alignItems='center' width={width}>
      <DoubleCurrencyLogo
        currency0={currency0}
        currency1={currency1}
        size={28}
      />
      <Box ml={1.5}>
        <Typography variant='body2'>
          {currency0.symbol} / {currency1.symbol} LP
        </Typography>
      </Box>
    </Box>
  );

  return (
    <Box
      className={cx(
        classes.farmLPCard,
        isExpandCard && classes.highlightedCard,
      )}
    >
      <Box
        className={classes.farmLPCardUp}
        onClick={() => setExpandCard(!isExpandCard)}
      >
        {isMobile ? (
          <>
            {renderPool(isExpandCard ? 0.95 : 0.7)}
            {!isExpandCard && (
              <Box width={0.25}>
                <Box display='flex' alignItems='center'>
                  <Typography variant='caption' color='textSecondary'>
                    APY
                  </Typography>
                  <Box ml={0.5} height={16}>
                    <img src={CircleInfoIcon} alt={'arrow up'} />
                  </Box>
                </Box>
                <Box mt={0.5} color={palette.success.main}>
                  <Typography variant='body2'>{apyWithFee}%</Typography>
                </Box>
              </Box>
            )}
            <Box
              width={0.05}
              display='flex'
              justifyContent='flex-end'
              color={palette.primary.main}
            >
              {isExpandCard ? <KeyboardArrowUp /> : <KeyboardArrowDown />}
            </Box>
          </>
        ) : (
          <>
            {renderPool(0.3)}
            <Box width={0.2} textAlign='center'>
              <Typography variant='body2'>{tvl}</Typography>
            </Box>
            <Box width={0.25} textAlign='center'>
              <Typography variant='body2'>
                ${(isLPFarm ? lpRewards : dualRewards).toLocaleString()} / day
              </Typography>
              {isLPFarm ? (
                <Typography variant='body2'>{lpPoolRate}</Typography>
              ) : (
                <>
                  <Typography variant='body2'>{dualPoolRateA}</Typography>
                  <Typography variant='body2'>{dualPoolRateB}</Typography>
                </>
              )}
            </Box>
            <Box
              width={0.15}
              display='flex'
              justifyContent='center'
              alignItems='center'
              color={palette.success.main}
            >
              <Typography variant='body2'>{apyWithFee}%</Typography>
              <Box ml={0.5} height={16}>
                <img src={CircleInfoIcon} alt={'arrow up'} />
              </Box>
            </Box>
            <Box width={0.2} textAlign='right'>
              <Typography variant='body2'>{earnedUSDStr}</Typography>
              {isLPFarm ? (
                <Box
                  display='flex'
                  alignItems='center'
                  justifyContent='flex-end'
                >
                  <CurrencyLogo
                    currency={lpStakingInfo.rewardToken}
                    size='16px'
                  />
                  <Typography variant='body2' style={{ marginLeft: 5 }}>
                    {formatTokenAmount(lpStakingInfo.earnedAmount)}
                    <span>&nbsp;{lpStakingInfo.rewardToken.symbol}</span>
                  </Typography>
                </Box>
              ) : (
                <>
                  <Box
                    display='flex'
                    alignItems='center'
                    justifyContent='flex-end'
                  >
                    <CurrencyLogo
                      currency={unwrappedToken(dualStakingInfo.rewardTokenA)}
                      size='16px'
                    />
                    <Typography variant='body2' style={{ marginLeft: 5 }}>
                      {formatTokenAmount(dualStakingInfo.earnedAmountA)}
                      <span>&nbsp;{dualStakingInfo.rewardTokenA.symbol}</span>
                    </Typography>
                  </Box>
                  <Box
                    display='flex'
                    alignItems='center'
                    justifyContent='flex-end'
                  >
                    <CurrencyLogo
                      currency={unwrappedToken(dualStakingInfo.rewardTokenB)}
                      size='16px'
                    />
                    <Typography variant='body2' style={{ marginLeft: 5 }}>
                      {formatTokenAmount(dualStakingInfo.earnedAmountB)}
                      <span>&nbsp;{dualStakingInfo.rewardTokenB.symbol}</span>
                    </Typography>
                  </Box>
                </>
              )}
            </Box>
          </>
        )}
      </Box>

      {isExpandCard && (
        <FarmCardDetails
          stakingInfo={stakingInfo}
          stakingAPY={stakingAPY}
          isLPFarm={isLPFarm}
        />
      )}
    </Box>
  );
}
Example #21
Source File: CurrencyRow.tsx    From interface-v2 with GNU General Public License v3.0 4 votes
CurrencyRow: React.FC<CurrenyRowProps> = ({
  currency,
  onSelect,
  isSelected,
  otherSelected,
  style,
  isOnSelectedList,
}) => {
  const { ethereum } = window as any;
  const classes = useStyles();
  const { palette } = useTheme();
  const { account, chainId } = useActiveWeb3React();
  const key = currencyKey(currency);

  const usdPrice = useUSDCPrice(currency);
  const balance = useCurrencyBalance(account ?? undefined, currency);
  const customAdded = useIsUserAddedToken(currency);

  const removeToken = useRemoveUserAddedToken();
  const addToken = useAddUserToken();
  const isMetamask =
    ethereum &&
    ethereum.isMetaMask &&
    Number(ethereum.chainId) === 137 &&
    isOnSelectedList;

  const addTokenToMetamask = (
    tokenAddress: any,
    tokenSymbol: any,
    tokenDecimals: any,
    tokenImage: any,
  ) => {
    if (ethereum) {
      ethereum
        .request({
          method: 'wallet_watchAsset',
          params: {
            type: 'ERC20', // Initially only supports ERC20, but eventually more!
            options: {
              address: tokenAddress, // The address that the token is at.
              symbol: tokenSymbol, // A ticker symbol or shorthand, up to 5 chars.
              decimals: tokenDecimals, // The number of decimals in the token
              image: tokenImage, // A string url of the token logo
            },
          },
        })
        .catch((error: any) => {
          if (error.code === 4001) {
            // EIP-1193 userRejectedRequest error
            console.log('We can encrypt anything without the key.');
          } else {
            console.error(error);
          }
        });
    }
  };

  // only show add or remove buttons if not on selected list
  return (
    <ListItem
      button
      style={style}
      key={key}
      selected={otherSelected || isSelected}
      onClick={() => {
        if (!isSelected && !otherSelected) onSelect();
      }}
    >
      <Box className={classes.currencyRow}>
        {(otherSelected || isSelected) && <TokenSelectedIcon />}
        <CurrencyLogo currency={currency} size={'32px'} />
        <Box ml={1} height={32}>
          <Box display='flex' alignItems='center'>
            <Typography variant='body2' className={classes.currencySymbol}>
              {currency.symbol}
            </Typography>
            {isMetamask && currency !== ETHER && (
              <Box
                style={{ cursor: 'pointer', marginLeft: 2 }}
                onClick={(event: any) => {
                  addTokenToMetamask(
                    currency.address,
                    currency.symbol,
                    currency.decimals,
                    getTokenLogoURL(currency.address),
                  );
                  event.stopPropagation();
                }}
              >
                <PlusHelper text='Add to metamask.' />
              </Box>
            )}
          </Box>
          {isOnSelectedList ? (
            <Typography variant='caption' className={classes.currencyName}>
              {currency.name}
            </Typography>
          ) : (
            <Box display='flex' alignItems='center'>
              <Typography variant='caption'>
                {customAdded ? 'Added by user' : 'Found by address'}
              </Typography>
              <Box
                ml={0.5}
                color={palette.primary.main}
                onClick={(event) => {
                  event.stopPropagation();
                  if (customAdded) {
                    if (chainId && currency instanceof Token)
                      removeToken(chainId, currency.address);
                  } else {
                    if (currency instanceof Token) addToken(currency);
                  }
                }}
              >
                <Typography variant='caption'>
                  {customAdded ? '(Remove)' : '(Add)'}
                </Typography>
              </Box>
            </Box>
          )}
        </Box>

        <Box flex={1}></Box>
        <TokenTags currency={currency} />
        <Box textAlign='right'>
          {balance ? (
            <>
              <Balance balance={balance} />
              <Typography
                variant='caption'
                style={{ color: palette.text.secondary }}
              >
                $
                {(
                  Number(balance.toExact()) *
                  (usdPrice ? Number(usdPrice.toSignificant()) : 0)
                ).toLocaleString()}
              </Typography>
            </>
          ) : account ? (
            <CircularProgress size={24} color='secondary' />
          ) : null}
        </Box>
      </Box>
    </ListItem>
  );
}