diff --git a/src/assets/images/gas-refund/knc-dropping.png b/src/assets/images/gas-refund/knc-dropping.png new file mode 100644 index 0000000000..901fb34e74 Binary files /dev/null and b/src/assets/images/gas-refund/knc-dropping.png differ diff --git a/src/assets/images/gas-refund/kncs.png b/src/assets/images/gas-refund/kncs.png new file mode 100644 index 0000000000..ee21ecd56e Binary files /dev/null and b/src/assets/images/gas-refund/kncs.png differ diff --git a/src/assets/images/gas-refund/ringwave.gif b/src/assets/images/gas-refund/ringwave.gif new file mode 100644 index 0000000000..2d7169d7dd Binary files /dev/null and b/src/assets/images/gas-refund/ringwave.gif differ diff --git a/src/assets/images/gas-refund/ringwave.png b/src/assets/images/gas-refund/ringwave.png new file mode 100644 index 0000000000..b436932293 Binary files /dev/null and b/src/assets/images/gas-refund/ringwave.png differ diff --git a/src/assets/svg/crystals.svg b/src/assets/svg/crystals.svg new file mode 100644 index 0000000000..3614ba2aa2 --- /dev/null +++ b/src/assets/svg/crystals.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/svg/knc_black.svg b/src/assets/svg/knc_black.svg index b65e010aaa..a0a93e6c13 100644 --- a/src/assets/svg/knc_black.svg +++ b/src/assets/svg/knc_black.svg @@ -1,4 +1,4 @@ - + @@ -11,4 +11,4 @@ - + \ No newline at end of file diff --git a/src/assets/svg/refund1.svg b/src/assets/svg/refund1.svg new file mode 100644 index 0000000000..6a39a97216 --- /dev/null +++ b/src/assets/svg/refund1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/svg/refund2.svg b/src/assets/svg/refund2.svg new file mode 100644 index 0000000000..e54beb0434 --- /dev/null +++ b/src/assets/svg/refund2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/svg/refund3.svg b/src/assets/svg/refund3.svg new file mode 100644 index 0000000000..e9b45a34b5 --- /dev/null +++ b/src/assets/svg/refund3.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/wallets/browser-wallet-dark.svg b/src/assets/wallets/browser-wallet-dark.svg index af517ca597..97efc9f867 100644 --- a/src/assets/wallets/browser-wallet-dark.svg +++ b/src/assets/wallets/browser-wallet-dark.svg @@ -1,4 +1,7 @@ - - - - + + + + + \ No newline at end of file diff --git a/src/assets/wallets/browser-wallet-light.svg b/src/assets/wallets/browser-wallet-light.svg index d557a1f4e0..d445239d55 100644 --- a/src/assets/wallets/browser-wallet-light.svg +++ b/src/assets/wallets/browser-wallet-light.svg @@ -1,5 +1,7 @@ - - - - - + + + + + \ No newline at end of file diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 351afa8010..b15f94def7 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -4,7 +4,7 @@ import React, { ReactNode, useRef } from 'react' import { ChevronDown, Info } from 'react-feather' import { Flex, Text } from 'rebass' import { ButtonProps, Button as RebassButton } from 'rebass/styled-components' -import styled from 'styled-components' +import styled, { css } from 'styled-components' import Loader from 'components/Loader' import { MouseoverTooltip } from 'components/Tooltip' @@ -12,7 +12,14 @@ import { ApprovalState } from 'hooks/useApproveCallback' import { RowBetween } from '../Row' +const disabledBase = css` + cursor: auto; +` +const disabledHoverBase = css` + filter: none; +` const Base = styled(RebassButton)<{ + color?: string padding?: string margin?: string width?: string @@ -20,6 +27,7 @@ const Base = styled(RebassButton)<{ borderRadius?: string altDisabledStyle?: boolean gap?: string + $disabled?: boolean // use this for disabled button with MouseoverTooltip }>` padding: ${({ padding }) => (padding ? padding : '12px')}; width: ${({ width }) => (width ? width : '100%')}; @@ -32,7 +40,7 @@ const Base = styled(RebassButton)<{ border-radius: ${({ borderRadius }) => (borderRadius ? borderRadius : '999px')}; outline: none; border: 1px solid transparent; - color: white; + color: ${({ color }) => color || 'white'}; text-decoration: none; display: flex; justify-content: center; @@ -45,16 +53,33 @@ const Base = styled(RebassButton)<{ filter: brightness(0.8); } &:disabled { - cursor: auto; + ${disabledBase} } - &:hover:disabled { - filter: none; + ${({ $disabled }) => $disabled && disabledBase} + + &:hover { + &:disabled { + ${disabledHoverBase} + } + ${({ $disabled }) => $disabled && disabledHoverBase} } + & > * { user-select: none; } ` +const disabledPrimary = css<{ + altDisabledStyle?: boolean +}>` + background-color: ${({ theme, altDisabledStyle }) => + altDisabledStyle ? theme.primary : theme.buttonGray} !important; + color: ${({ theme, altDisabledStyle }) => (altDisabledStyle ? 'white' : theme.border)}; + box-shadow: none !important; + border: 1px solid transparent; + outline: none; + opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.7' : '1')}; +` export const ButtonPrimary = styled(Base)` background-color: ${({ theme }) => theme.primary}; color: ${({ theme }) => theme.textReverse}; @@ -63,16 +88,16 @@ export const ButtonPrimary = styled(Base)` background-color: ${({ theme }) => darken(0.1, theme.primary)}; } &:disabled { - background-color: ${({ theme, altDisabledStyle }) => (altDisabledStyle ? theme.primary : theme.buttonGray)}; - color: ${({ theme, altDisabledStyle }) => (altDisabledStyle ? 'white' : theme.border)}; - cursor: auto; - box-shadow: none; - border: 1px solid transparent; - outline: none; - opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.7' : '1')}; + ${disabledPrimary} } + ${({ $disabled }) => $disabled && disabledPrimary} ` +const disabledWarning = css` + background-color: ${({ theme }) => rgba(theme.warning, 0.2)}; + cursor: auto; + color: ${({ theme }) => theme.warning}; +` export const ButtonWarning = styled(Base)` background-color: ${({ theme }) => theme.warning}; color: ${({ theme }) => theme.textReverse}; @@ -85,17 +110,27 @@ export const ButtonWarning = styled(Base)` background-color: ${({ theme }) => darken(0.1, theme.warning)}; } &:disabled { + ${disabledWarning} background-color: ${({ theme }) => rgba(theme.warning, 0.2)}; cursor: auto; color: ${({ theme }) => theme.warning}; } + ${({ $disabled }) => $disabled && disabledWarning} ` -export const ButtonLight = styled(Base)<{ color?: string }>` +const disabledLight = css` + cursor: not-allowed; + background-color: ${({ theme }) => `${theme.buttonGray}`}; + color: ${({ theme }) => theme.border}; + box-shadow: none; + border: 1px solid transparent; + outline: none; +` +export const ButtonLight = styled(Base)<{ color?: string; fontSize?: number }>` background-color: ${({ theme, color }) => `${color || theme.primary}4d`}; min-width: unset; color: ${({ color, theme }) => color || theme.primary}; - font-size: 14px; + font-size: ${({ fontSize }) => fontSize || 14}px; font-weight: 500; &:hover { background-color: ${({ theme, disabled, color }) => !disabled && darken(0.03, `${color || theme.primary}4d`)}; @@ -105,13 +140,9 @@ export const ButtonLight = styled(Base)<{ color?: string }>` background-color: ${({ theme, disabled, color }) => !disabled && darken(0.05, `${color || theme.primary}4d`)}; } :disabled { - cursor: not-allowed; - background-color: ${({ theme }) => `${theme.buttonGray}`}; - color: ${({ theme }) => theme.border}; - box-shadow: none; - border: 1px solid transparent; - outline: none; + ${disabledLight} } + ${({ $disabled }) => $disabled && disabledLight} ` export const ButtonGray = styled(Base)` @@ -128,6 +159,10 @@ export const ButtonGray = styled(Base)` } ` +const disabledSecondary = css` + opacity: 50%; + cursor: auto; +` export const ButtonSecondary = styled(Base)` border: 1px solid ${({ theme }) => theme.primary}; color: ${({ theme }) => theme.primary}; @@ -145,14 +180,23 @@ export const ButtonSecondary = styled(Base)` border: 1px solid ${({ theme }) => theme.primary}; } &:disabled { - opacity: 50%; - cursor: auto; + ${disabledSecondary} } + ${({ $disabled }) => $disabled && disabledSecondary} + a:hover { text-decoration: none; } ` +const disabledOutlined = css<{ + altDisabledStyle?: boolean +}>` + color: ${({ theme, altDisabledStyle }) => (altDisabledStyle ? 'white' : theme.border)}; + cursor: auto; + box-shadow: none; + border: 1px solid ${({ theme, altDisabledStyle }) => (altDisabledStyle ? 'white' : theme.border)}; +` export const ButtonOutlined = styled(Base)<{ color?: string }>` border: 1px solid ${({ theme, color }) => color || theme.subText}; background-color: transparent; @@ -169,13 +213,15 @@ export const ButtonOutlined = styled(Base)<{ color?: string }>` box-shadow: 0 0 0 1px ${({ theme, color }) => color || theme.subText}; } &:disabled { - color: ${({ theme, altDisabledStyle }) => (altDisabledStyle ? 'white' : theme.border)}; - cursor: auto; - box-shadow: none; - border: 1px solid ${({ theme, altDisabledStyle }) => (altDisabledStyle ? 'white' : theme.border)}; + ${disabledOutlined} } + ${({ $disabled }) => $disabled && disabledOutlined} ` +const disabledEmpty = css` + opacity: 50%; + cursor: not-allowed; +` export const ButtonEmpty = styled(Base)` background-color: transparent; color: ${({ theme }) => theme.primary}; @@ -184,20 +230,31 @@ export const ButtonEmpty = styled(Base)` align-items: center; &:disabled { - opacity: 50%; - cursor: not-allowed; + ${disabledEmpty} } + ${({ $disabled }) => $disabled && disabledEmpty} ` +const disabledConfirmed = css` + cursor: auto; +` const ButtonConfirmedStyle = styled(Base)` background-color: ${({ theme }) => rgba(theme.apr, 0.2)}; color: ${({ theme }) => theme.green}; &:disabled { - cursor: auto; + ${disabledConfirmed} } + ${({ $disabled }) => $disabled && disabledConfirmed} ` +const disabledError = css` + opacity: 50%; + cursor: auto; + box-shadow: none; + background-color: ${({ theme }) => theme.red}; + border: 1px solid ${({ theme }) => theme.red}; +` export const ButtonErrorStyle = styled(Base)` background-color: ${({ theme }) => theme.red}; border: 1px solid ${({ theme }) => theme.red}; @@ -211,12 +268,9 @@ export const ButtonErrorStyle = styled(Base)` background-color: ${({ theme }) => darken(0.1, theme.red)}; } &:disabled { - opacity: 50%; - cursor: auto; - box-shadow: none; - background-color: ${({ theme }) => theme.red}; - border: 1px solid ${({ theme }) => theme.red}; + ${disabledError} } + ${({ $disabled }) => $disabled && disabledError} ` export function ButtonConfirmed({ diff --git a/src/components/Copy/index.tsx b/src/components/Copy/index.tsx index 82b98bdcf8..d0441a66fb 100644 --- a/src/components/Copy/index.tsx +++ b/src/components/Copy/index.tsx @@ -8,7 +8,7 @@ import useCopyClipboard from 'hooks/useCopyClipboard' const Wrapper = styled.div<{ margin?: string; size?: string }>` flex-shrink: 0; - margin-left: 4px; + margin-left: ${({ margin }) => margin || '4px'}; text-decoration: none; cursor: pointer; position: relative; diff --git a/src/components/Header/groups/KyberDaoGroup.tsx b/src/components/Header/groups/KyberDaoGroup.tsx index 1b93af968e..38165fe4db 100644 --- a/src/components/Header/groups/KyberDaoGroup.tsx +++ b/src/components/Header/groups/KyberDaoGroup.tsx @@ -2,6 +2,7 @@ import { Trans } from '@lingui/macro' import { useLocation } from 'react-router-dom' import styled from 'styled-components' +import { ReactComponent as KyberLogo } from 'assets/svg/knc_black.svg' import Column from 'components/Column' import LightBulb from 'components/Icons/LightBulb' import StakeIcon from 'components/Icons/Stake' @@ -36,17 +37,21 @@ const KyberDAONavGroup = () => { } dropdownContent={ - + Stake KNC - + Vote + + + KNC Utility + { diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index c105986bf1..4d1383051a 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -13,7 +13,7 @@ import { PieChart, Share2, } from 'react-feather' -import { useLocation, useNavigate } from 'react-router-dom' +import { NavLink, useLocation, useNavigate } from 'react-router-dom' import { useMedia } from 'react-use' import { Text } from 'rebass' import styled, { css } from 'styled-components' @@ -35,11 +35,12 @@ import Row, { AutoRow } from 'components/Row' import Toggle from 'components/Toggle' import ThemeToggle from 'components/Toggle/ThemeToggle' import { TutorialIds } from 'components/Tutorial/TutorialSwap/constant' -import { TAG } from 'constants/env' +import { ENV_LEVEL, TAG } from 'constants/env' import { AGGREGATOR_ANALYTICS_URL, APP_PATHS, DMM_ANALYTICS_URL, TERM_FILES_PATH } from 'constants/index' import { getLocaleLabel } from 'constants/locales' import { FAUCET_NETWORKS } from 'constants/networks' import { EVMNetworkInfo } from 'constants/networks/type' +import { ENV_TYPE } from 'constants/type' import { useActiveWeb3React } from 'hooks' import useClaimReward from 'hooks/useClaimReward' import useMixpanel, { MIXPANEL_TYPE } from 'hooks/useMixpanel' @@ -414,6 +415,7 @@ export default function Menu() { options={[ { link: '/kyberdao/stake-knc', label: t`Stake KNC` }, { link: '/kyberdao/vote', label: t`Vote` }, + { link: APP_PATHS.KYBERDAO_KNC_UTILITY, label: t`KNC Utility` }, { link: 'https://kyberswap.canny.io/feature-request', label: t`Feature Request`, external: true }, ]} /> @@ -517,7 +519,14 @@ export default function Menu() { Help - + {ENV_LEVEL === ENV_TYPE.LOCAL && ( + + + + Icons + + + )} diff --git a/src/components/SwapForm/SwapModal/SwapDetails/index.tsx b/src/components/SwapForm/SwapModal/SwapDetails/index.tsx index ae2b109937..0ddb21e76c 100644 --- a/src/components/SwapForm/SwapModal/SwapDetails/index.tsx +++ b/src/components/SwapForm/SwapModal/SwapDetails/index.tsx @@ -7,6 +7,7 @@ import { Flex, Text } from 'rebass' import { BuildRouteData } from 'services/route/types/buildRoute' import { TruncatedText } from 'components' +import { ButtonLight } from 'components/Button' import { AutoColumn } from 'components/Column' import CopyHelper from 'components/Copy' import Divider from 'components/Divider' @@ -19,6 +20,7 @@ import { MouseoverTooltip, TextDashed } from 'components/Tooltip' import { StyledBalanceMaxMini } from 'components/swapv2/styleds' import { CHAINS_SUPPORT_FEE_CONFIGS } from 'constants/index' import { useActiveWeb3React } from 'hooks' +import { isSupportKyberDao, useGasRefundTier } from 'hooks/kyberdao' import useTheme from 'hooks/useTheme' import { useIsDarkMode } from 'state/user/hooks' import { ExternalLink, TYPE } from 'theme' @@ -71,11 +73,12 @@ export default function SwapDetails({ priceImpact, buildData, }: Props) { - const { isEVM, chainId, networkInfo } = useActiveWeb3React() + const { isEVM, chainId, networkInfo, account } = useActiveWeb3React() const [showInverted, setShowInverted] = useState<boolean>(false) const theme = useTheme() const isDarkMode = useIsDarkMode() const { slippage, routeSummary } = useSwapFormContext() + const { gasRefundPerCentage } = useGasRefundTier() const currencyIn = routeSummary?.parsedAmountIn?.currency const currencyOut = routeSummary?.parsedAmountOut?.currency @@ -325,6 +328,41 @@ export default function SwapDetails({ </TYPE.black> </RowBetween> + {isSupportKyberDao(chainId) && account && Number(routeSummary?.amountInUsd || 0) > 200 && ( + <RowBetween height="20px" style={{ gap: '16px' }}> + <RowFixed> + <TextDashed fontSize={12} fontWeight={400} color={theme.subText}> + <MouseoverTooltip + text={ + <Text> + <Trans> + Stake KNC in KyberDAO to get gas refund. Read more{' '} + <ExternalLink href="https://docs.kyberswap.com/governance/knc-token/gas-refund-program"> + here ↗ + </ExternalLink> + </Trans> + </Text> + } + placement="right" + > + <Trans>Gas Refund</Trans> + </MouseoverTooltip> + </TextDashed> + </RowFixed> + + <ButtonLight + padding="0px 8px" + width="fit-content" + fontSize={10} + fontWeight={500} + lineHeight="16px" + style={{ pointerEvents: 'none' }} + > + <Trans>{gasRefundPerCentage * 100}% Refund</Trans> + </ButtonLight> + </RowBetween> + )} + <Divider /> <RowBetween> <TextDashed fontSize={12} color={theme.subText}> diff --git a/src/components/SwapForm/TradeSummary.tsx b/src/components/SwapForm/TradeSummary.tsx index 7d99558319..034410a078 100644 --- a/src/components/SwapForm/TradeSummary.tsx +++ b/src/components/SwapForm/TradeSummary.tsx @@ -1,16 +1,19 @@ import { Trans } from '@lingui/macro' import React, { useEffect, useState } from 'react' +import { NavLink } from 'react-router-dom' import { Text } from 'rebass' import styled from 'styled-components' import { ReactComponent as DropdownSVG } from 'assets/svg/down.svg' +import { ButtonLight } from 'components/Button' import { AutoColumn } from 'components/Column' import Divider from 'components/Divider' import { RowBetween, RowFixed } from 'components/Row' import { useSwapFormContext } from 'components/SwapForm/SwapFormContext' import { MouseoverTooltip, TextDashed } from 'components/Tooltip' -import { BIPS_BASE, CHAINS_SUPPORT_FEE_CONFIGS } from 'constants/index' +import { APP_PATHS, BIPS_BASE, CHAINS_SUPPORT_FEE_CONFIGS } from 'constants/index' import { useActiveWeb3React } from 'hooks' +import { isSupportKyberDao, useGasRefundTier } from 'hooks/kyberdao' import useMixpanel, { MIXPANEL_TYPE } from 'hooks/useMixpanel' import useTheme from 'hooks/useTheme' import { ExternalLink, TYPE } from 'theme' @@ -143,7 +146,9 @@ type Props = { slippage: number } const TradeSummary: React.FC<Props> = ({ routeSummary, slippage }) => { + const { account, chainId } = useActiveWeb3React() const theme = useTheme() + const { gasRefundPerCentage } = useGasRefundTier() const [expanded, setExpanded] = useState(true) const [alreadyVisible, setAlreadyVisible] = useState(false) const { parsedAmountOut, priceImpact, gasUsd } = routeSummary || {} @@ -261,6 +266,32 @@ const TradeSummary: React.FC<Props> = ({ routeSummary, slippage }) => { </TYPE.black> </RowBetween> + {isSupportKyberDao(chainId) && ( + <RowBetween> + <RowFixed> + <TextDashed fontSize={12} fontWeight={400} color={theme.subText}> + <MouseoverTooltip + text={ + <Trans> + Stake KNC in KyberDAO to get gas refund. Read more{' '} + <ExternalLink href="https://docs.kyberswap.com/governance/knc-token/gas-refund-program"> + here ↗ + </ExternalLink> + </Trans> + } + placement="right" + > + <Trans>Gas Refund</Trans> + </MouseoverTooltip> + </TextDashed> + </RowFixed> + <NavLink to={APP_PATHS.KYBERDAO_KNC_UTILITY}> + <ButtonLight padding="0px 8px" width="fit-content" fontSize={10} fontWeight={500} lineHeight="16px"> + <Trans>{account ? gasRefundPerCentage * 100 : '--'}% Refund</Trans> + </ButtonLight> + </NavLink> + </RowBetween> + )} <SwapFee /> </ContentWrapper> </AutoColumn> diff --git a/src/components/Tooltip/index.tsx b/src/components/Tooltip/index.tsx index c418961d03..704af63ba2 100644 --- a/src/components/Tooltip/index.tsx +++ b/src/components/Tooltip/index.tsx @@ -55,7 +55,6 @@ export default function Tooltip({ text, width, maxWidth, size, onMouseEnter, onM export function MouseoverTooltip({ children, disableTooltip, delay, ...rest }: Omit<TooltipProps, 'show'>) { const [show, setShow] = useState(false) const [closeTimeout, setCloseTimeout] = useState<NodeJS.Timeout | null>(null) - const ref = useRef(null) const hovering = useRef(false) const open = useCallback(() => { if (!!rest.text) { @@ -83,7 +82,7 @@ export function MouseoverTooltip({ children, disableTooltip, delay, ...rest }: O if (disableTooltip) return <>{children}</> return ( <Tooltip {...rest} show={show} onMouseEnter={open} onMouseLeave={close}> - <Flex ref={ref} onMouseOver={open} onMouseLeave={close} alignItems="center"> + <Flex onMouseOver={open} onMouseLeave={close} alignItems="center"> {children} </Flex> </Tooltip> diff --git a/src/constants/index.ts b/src/constants/index.ts index af2de18100..191046c877 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -221,12 +221,13 @@ export const APP_PATHS = { KYBERDAO: '/kyberdao', KYBERDAO_STAKE: '/kyberdao/stake-knc', KYBERDAO_VOTE: '/kyberdao/vote', + KYBERDAO_KNC_UTILITY: '/kyberdao/knc-utility', LIMIT: '/limit', GRANT_PROGRAMS: '/inter-project-trading-campaigns', PROFILE_MANAGE: '/manage', ELASTIC_LEGACY: '/elastic-legacy', VERIFY_AUTH: '/auth', -} +} as const export const TERM_FILES_PATH = { KYBERSWAP_TERMS: '/files/Kyber - Terms of Service - 14 June 2023.pdf', diff --git a/src/constants/tokens.ts b/src/constants/tokens.ts index 67c2ee63b3..b109ca388f 100644 --- a/src/constants/tokens.ts +++ b/src/constants/tokens.ts @@ -678,7 +678,7 @@ export const KNC: { [chainId in ChainId]: Token } = { 18, 'KNC', 'Kyber Network Crystal', - ), // todo namgold: not exists yet + ), // todo: not exists yet [ChainId.SOLANA_DEVNET]: new Token( ChainId.SOLANA_DEVNET, 'KNCkfGAnBUvoG5EJipAzSBjjaF8iNL4ivYsBS14DKdg', diff --git a/src/hooks/kyberdao/index.tsx b/src/hooks/kyberdao/index.tsx index 818bda530b..bf6613808a 100644 --- a/src/hooks/kyberdao/index.tsx +++ b/src/hooks/kyberdao/index.tsx @@ -11,25 +11,37 @@ import MigrateABI from 'constants/abis/kyberdao/migrate.json' import RewardDistributorABI from 'constants/abis/kyberdao/reward_distributor.json' import StakingABI from 'constants/abis/kyberdao/staking.json' import { CONTRACT_NOT_FOUND_MSG } from 'constants/messages' -import { NETWORKS_INFO, NETWORKS_INFO_CONFIG, isEVM } from 'constants/networks' +import { NETWORKS_INFO_CONFIG, isEVM } from 'constants/networks' +import ethereumInfo from 'constants/networks/ethereum' import { EVMNetworkInfo } from 'constants/networks/type' import { useActiveWeb3React } from 'hooks' import { useContract, useContractForReading, useTokenContractForReading } from 'hooks/useContract' import useTokenBalance from 'hooks/useTokenBalance' +import { KNCUtilityTabs } from 'pages/KyberDAO/KNCUtility/type' import { useSingleCallResult } from 'state/multicall/hooks' import { useTransactionAdder } from 'state/transactions/hooks' import { TRANSACTION_TYPE } from 'state/transactions/type' import { calculateGasMargin } from 'utils' -import { ProposalDetail, ProposalStatus, RewardStats, StakerAction, StakerInfo, VoteInfo } from './types' +import { + EligibleTxsInfo, + GasRefundTierInfo, + ProposalDetail, + ProposalStatus, + RewardInfo, + RewardStats, + StakerAction, + StakerInfo, + VoteInfo, +} from './types' export function isSupportKyberDao(chainId: ChainId) { - return isEVM(chainId) && (NETWORKS_INFO_CONFIG[chainId] as EVMNetworkInfo).kyberDAO + return isEVM(chainId) && NETWORKS_INFO_CONFIG[chainId].kyberDAO } export function useKyberDAOInfo() { - const { chainId } = useActiveWeb3React() - const kyberDaoInfo = NETWORKS_INFO[chainId !== ChainId.GÖRLI ? ChainId.MAINNET : ChainId.GÖRLI].kyberDAO + const { chainId, networkInfo } = useActiveWeb3React() + const kyberDaoInfo = (isSupportKyberDao(chainId) ? (networkInfo as EVMNetworkInfo) : ethereumInfo).kyberDAO return kyberDaoInfo } @@ -336,7 +348,7 @@ export function useStakingInfo() { kncContract ?.totalSupply() .then((res: any) => setTotalSupply(res)) - .catch((err: any) => console.log(err)) + .catch((error: any) => console.error('Get KNC totalSupply error:', { error })) }, [kncContract]) return { @@ -518,6 +530,93 @@ export function useVotingInfo() { } } +const aggregateValue = <T extends string>( + values: ({ [key in T]: string | number } | undefined)[], + field: T, +): number => { + return values.reduce((acc, cur) => { + const value = cur?.[field] ?? 0 + return (typeof value === 'number' ? value : parseFloat(value)) + acc + }, 0) +} + +export function useGasRefundTier(): GasRefundTierInfo { + const { account, chainId } = useActiveWeb3React() + const kyberDaoInfo = useKyberDAOInfo() + + const { data } = useSWR<GasRefundTierInfo>( + account && isSupportKyberDao(chainId) && kyberDaoInfo?.daoStatsApi + '/api/v1/stakers/' + account + '/refund-info', + url => fetcher(url).then(res => res.refundInfo), + ) + + return data || { userTier: 0, gasRefundPerCentage: 0 } +} + +export function useGasRefundInfo({ rewardStatus = KNCUtilityTabs.Available }: { rewardStatus?: KNCUtilityTabs }) { + const { account, chainId } = useActiveWeb3React() + const kyberDaoInfo = useKyberDAOInfo() + + const { data: claimableReward } = useSWR<RewardInfo>( + account && + isSupportKyberDao(chainId) && + kyberDaoInfo?.daoStatsApi + '/api/v1/stakers/' + account + '/refunds/total?rewardStatus=claimable', + url => + fetcher(url) + .then(res => res.total) + .then(({ knc, usd }) => ({ knc: parseFloat(knc), usd: parseFloat(usd) })), + ) + const { data: pendingReward } = useSWR<RewardInfo>( + account && + isSupportKyberDao(chainId) && + kyberDaoInfo?.daoStatsApi + '/api/v1/stakers/' + account + '/refunds/total?rewardStatus=pending', + url => + fetcher(url) + .then(res => res.total) + .then(({ knc, usd }) => ({ knc: parseFloat(knc), usd: parseFloat(usd) })), + ) + const { data: claimedReward } = useSWR<RewardInfo>( + account && + isSupportKyberDao(chainId) && + kyberDaoInfo?.daoStatsApi + '/api/v1/stakers/' + account + '/refunds/total?rewardStatus=claimed', + url => + fetcher(url) + .then(res => res.total) + .then(({ knc, usd }) => ({ knc: parseFloat(knc), usd: parseFloat(usd) })), + ) + return { + reward: + rewardStatus === KNCUtilityTabs.Available + ? claimableReward + : rewardStatus === KNCUtilityTabs.Pending + ? pendingReward + : rewardStatus === KNCUtilityTabs.Claimed + ? claimedReward + : undefined, + claimableReward, + totalReward: { + usd: aggregateValue([claimableReward, pendingReward, claimedReward], 'usd'), + knc: aggregateValue([claimableReward, pendingReward, claimedReward], 'knc'), + }, + } +} + +export const useEligibleTransactions = (page = 1, pageSize = 100): EligibleTxsInfo | undefined => { + const { account, chainId } = useActiveWeb3React() + const kyberDaoInfo = useKyberDAOInfo() + + const { data: eligibleTransactions } = useSWR<EligibleTxsInfo>( + account && + isSupportKyberDao(chainId) && + kyberDaoInfo?.daoStatsApi + + '/api/v1/stakers/' + + account + + `/refunds/eligible-transactions?pageSize=${pageSize}&page=${page}`, + fetcher, + ) + + return eligibleTransactions +} + export function useProposalInfoById(id?: number): { proposalInfo?: ProposalDetail } { const kyberDaoInfo = useKyberDAOInfo() const { data } = useSWRImmutable( diff --git a/src/hooks/kyberdao/types.ts b/src/hooks/kyberdao/types.ts index 243019f7f9..50073ae43e 100644 --- a/src/hooks/kyberdao/types.ts +++ b/src/hooks/kyberdao/types.ts @@ -66,6 +66,39 @@ export interface StakerInfo { stake_amount: number } +export interface GasRefundTierInfo { + userTier: number + gasRefundPerCentage: number +} + +export interface RewardInfo { + knc: number + usd: number +} + +interface TransactionInfo { + tx: string + timestamp: number + gasRefundInKNC: string + gasRefundInUSD: string + gasFeeInUSD: string + gasFeeInNativeToken: string + epoch: number + userTier: number + gasRefundPerCentage: string + userWallet: string +} + +export interface EligibleTxsInfo { + transactions: TransactionInfo[] + pagination: { + totalOfPages: number + currentPage: number + pageSize: number + hasMore: boolean + } +} + export interface StakerAction { timestamp: number epoch: number diff --git a/src/pages/App.tsx b/src/pages/App.tsx index 737dd84aa2..cf5711d63b 100644 --- a/src/pages/App.tsx +++ b/src/pages/App.tsx @@ -68,6 +68,7 @@ const RemoveLiquidity = lazy(() => import('pages/RemoveLiquidity')) const KyberDAOStakeKNC = lazy(() => import('pages/KyberDAO/StakeKNC')) const KyberDAOVote = lazy(() => import('pages/KyberDAO/Vote')) +const KNCUtility = lazy(() => import('pages/KyberDAO/KNCUtility')) const AboutKyberSwap = lazy(() => import('pages//About/AboutKyberSwap')) const AboutKNC = lazy(() => import('pages/About/AboutKNC')) const BuyCrypto = lazy(() => import('pages/BuyCrypto')) @@ -355,6 +356,7 @@ export default function App() { <Route path={`${APP_PATHS.KYBERDAO_STAKE}`} element={<KyberDAOStakeKNC />} /> <Route path={`${APP_PATHS.KYBERDAO_VOTE}`} element={<KyberDAOVote />} /> + <Route path={`${APP_PATHS.KYBERDAO_KNC_UTILITY}`} element={<KNCUtility />} /> <Route path={`${APP_PATHS.ABOUT}/kyberswap`} element={<AboutKyberSwap />} /> <Route path={`${APP_PATHS.ABOUT}/knc`} element={<AboutKNC />} /> <Route path={`${APP_PATHS.KYBERAI}`} element={<Navigate to={APP_PATHS.KYBERAI_ABOUT} replace />} /> diff --git a/src/pages/KyberDAO/KNCUtility/EligibleTxModal.tsx b/src/pages/KyberDAO/KNCUtility/EligibleTxModal.tsx new file mode 100644 index 0000000000..2ab6171d07 --- /dev/null +++ b/src/pages/KyberDAO/KNCUtility/EligibleTxModal.tsx @@ -0,0 +1,163 @@ +import { Trans } from '@lingui/macro' +import { useState } from 'react' +import { X } from 'react-feather' +import { useMedia } from 'react-use' +import { Flex, Text } from 'rebass' +import styled from 'styled-components' + +import CopyHelper from 'components/Copy' +import Modal from 'components/Modal' +import Pagination from 'components/Pagination' +import { RowBetween } from 'components/Row' +import { NativeCurrencies } from 'constants/tokens' +import { useActiveWeb3React } from 'hooks' +import { useEligibleTransactions } from 'hooks/kyberdao' +import useTheme from 'hooks/useTheme' +import { ExternalLinkIcon, MEDIA_WIDTHS } from 'theme' +import { formattedNum, getEtherscanLink } from 'utils' + +import { HeaderCell, Table, TableHeader, TableRow } from './TxTable' + +const Wrapper = styled.div` + width: 100%; + border-radius: 20px; + padding: 24px 24px 30px; + background-color: ${({ theme }) => theme.tableHeader}; + min-width: 550px; + ${({ theme }) => theme.mediaWidth.upToExtraSmall` + min-width: unset; + `} +` + +const IconWrapper = styled.div` + display: flex; + align-items: center; + justify-content: center; + background-color: transparent; + + & > img, + span { + height: 16px; + width: 16px; + } + ${({ theme }) => theme.mediaWidth.upToMedium` + align-items: flex-end; + `}; +` + +const CloseButton = styled.div` + svg { + display: block; + } + :hover { + filter: brightness(0.8); + } +` + +export default function EligibleTxModal({ isOpen, closeModal }: { isOpen: boolean; closeModal: () => void }) { + const { chainId, networkInfo } = useActiveWeb3React() + const [currentPage, setCurrentPage] = useState(1) + const eligibleTxs = useEligibleTransactions(currentPage, 10) + const theme = useTheme() + const upToExtraSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToExtraSmall}px)`) + + return ( + <Modal isOpen={isOpen} onDismiss={closeModal} maxWidth="800px" width="70vw"> + <Wrapper> + <Flex sx={{ gap: '16px' }} flexDirection="column"> + <Flex sx={{ gap: '26px' }} flexDirection="column"> + <RowBetween> + <Text fontSize={20} fontWeight={500} lineHeight="24px"> + <Trans>Your transactions</Trans> + </Text> + <CloseButton onClick={closeModal}> + <X style={{ cursor: 'pointer' }} /> + </CloseButton> + </RowBetween> + <Table> + <TableHeader> + <HeaderCell> + <Trans>TXN HASH</Trans> + </HeaderCell> + {upToExtraSmall ? null : ( + <> + <HeaderCell> + <Trans>LOCAL TIME</Trans> + </HeaderCell> + <HeaderCell> + <Trans>GAS FEE</Trans> + </HeaderCell> + </> + )} + <HeaderCell textAlign="right"> + <Trans>GAS REFUND REWARDS</Trans> + </HeaderCell> + </TableHeader> + {eligibleTxs?.transactions?.map(tx => { + const time = new Date(tx.timestamp * 1000) + return ( + <TableRow key={tx.tx}> + <HeaderCell> + <Flex sx={{ gap: '4px' }}> + <IconWrapper> + <img src={networkInfo.icon} /> + </IconWrapper> + <Text fontSize={14} fontWeight={400} lineHeight="normal" alignSelf="center"> + {tx.tx.slice(0, 6)}...{tx.tx.slice(-4)} + </Text> + <CopyHelper toCopy={tx.tx} margin="unset" color={theme.subText} /> + <ExternalLinkIcon + href={getEtherscanLink(chainId, tx.tx, 'transaction')} + color={theme.subText} + /> + </Flex> + </HeaderCell> + {upToExtraSmall ? null : ( + <> + <HeaderCell> + <Flex flexDirection="column" sx={{ gap: '4px' }}> + <Text>{time.toLocaleDateString()}</Text> + <Text fontWeight={400} color={theme.subText}> + {time.toLocaleTimeString()} + </Text> + </Flex> + </HeaderCell> + <HeaderCell> + <Flex flexDirection="column" sx={{ gap: '4px' }}> + <Text> + {formattedNum(tx.gasFeeInNativeToken)} {NativeCurrencies[chainId].symbol} + </Text> + <Text fontWeight={400} color={theme.subText}> + {formattedNum(tx.gasFeeInUSD, true)} + </Text> + </Flex> + </HeaderCell> + </> + )} + + <HeaderCell textAlign="right"> + <Flex flexDirection="column" sx={{ gap: '4px' }}> + <Text>{formattedNum(tx.gasRefundInKNC)} KNC</Text> + <Text fontWeight={400} color={theme.subText}> + <Trans>Tier {tx.userTier}</Trans> - {Number(tx.gasRefundPerCentage) * 100}% + </Text> + </Flex> + </HeaderCell> + </TableRow> + ) + })} + </Table> + </Flex> + <Pagination + onPageChange={setCurrentPage} + totalCount={(eligibleTxs?.pagination.pageSize ?? 0) * (eligibleTxs?.pagination.totalOfPages ?? 0)} + currentPage={eligibleTxs?.pagination.currentPage ?? 0} + pageSize={eligibleTxs?.pagination.pageSize ?? 0} + haveBg={false} + style={{ padding: '0' }} + /> + </Flex> + </Wrapper> + </Modal> + ) +} diff --git a/src/pages/KyberDAO/KNCUtility/FAQ.tsx b/src/pages/KyberDAO/KNCUtility/FAQ.tsx new file mode 100644 index 0000000000..c724f332f2 --- /dev/null +++ b/src/pages/KyberDAO/KNCUtility/FAQ.tsx @@ -0,0 +1,251 @@ +import { Trans, t } from '@lingui/macro' +import { rgba } from 'polished' +import { ReactNode, useState } from 'react' +import { ChevronDown } from 'react-feather' +import { NavLink } from 'react-router-dom' +import { Flex, Text } from 'rebass' +import styled from 'styled-components' + +import { APP_PATHS, TERM_FILES_PATH } from 'constants/index' +import useTheme from 'hooks/useTheme' +import { ExternalLink } from 'theme' + +const Title = styled.span` + font-weight: 500; + font-size: 16px; + line-height: 20px; + color: ${({ theme }) => theme.text}; + max-width: calc(100% - 20px - 8px); +` + +enum Panel { + Q_Join, + Q_Chain, + Q_When, + Q_Deadline, + Q_Vote, + Q_LimitOrder, + Q_Limit, + Q_Term, + Q_Other, +} + +type PanelProps = { + isExpanded: boolean + toggleExpand: () => void + title: string + content: ReactNode +} + +const DetailPanel: React.FC<PanelProps> = ({ isExpanded, title, content, toggleExpand }) => { + const theme = useTheme() + + return ( + <> + <Flex + role="button" + onClick={() => { + toggleExpand() + }} + sx={{ + height: '56px', + alignItems: 'center', + justifyContent: 'space-between', + cursor: 'pointer', + }} + > + <Title>{title} + + + + + + + {isExpanded && ( + + {content} + + )} + + ) +} + +const DetailsContainer = styled.div` + padding: 16px 24px; + height: fit-content; + display: flex; + flex-direction: column; + background: ${({ theme }) => rgba(theme.background, 0.8)}; + border-radius: 20px; +` + +const Separator = styled.div` + width: 100%; + height: 0; + border-bottom: 1px solid ${({ theme }) => theme.border}; + margin: 8px 0; +` + +const FAQ: React.FC = () => { + const [expandedPanel, setExpandedPanel] = useState() + + const handleToggleExpand = (panel?: Panel) => { + if (expandedPanel === panel) { + setExpandedPanel(undefined) + } else { + setExpandedPanel(panel) + } + } + + return ( + + handleToggleExpand(Panel.Q_Join)} + isExpanded={expandedPanel === Panel.Q_Join} + title={t`Can I participate in the Gas Refund Program if I am not staking in KyberDAO?`} + content={ + + No. You have to stake a minimum of 500 KNC in KyberDAO (on Ethereum) here, and meet the eligibility criteria + by completing swap(s) on KyberSwap, with a minimum trading volume of ≥$200 per swap. + + } + /> + + handleToggleExpand(Panel.Q_Chain)} + isExpanded={expandedPanel === Panel.Q_Chain} + title={t`Are swaps on all chains eligible for gas refunds?`} + content={ + + During this beta phase, only swaps on Ethereum are eligible for gas refunds. We may expand the gas refund + program to other chains in the future. + + } + /> + + handleToggleExpand(Panel.Q_When)} + isExpanded={expandedPanel === Panel.Q_When} + title={t`When will rewards be available to claim?`} + content={ + + On the “Pending” tab, there is a countdown timer showing when pending refunds become available for claiming. + Refunds become available for claiming at the start of n+2 epoch. Each epoch lasts approximately 2 weeks. You + can claim your rewards in the KNC Utility page or in the Wallet widget. + + } + /> + + handleToggleExpand(Panel.Q_Deadline)} + isExpanded={expandedPanel === Panel.Q_Deadline} + title={t`Is there a deadline to claim your gas refunds?`} + content={ + + There is no deadline to claim your gas refunds. You can wait for more KNC to be accumulated before claiming + them in order to save on gas fees. + + } + /> + + handleToggleExpand(Panel.Q_LimitOrder)} + isExpanded={expandedPanel === Panel.Q_LimitOrder} + title={t`Are limit orders and cross-chain swaps eligible for gas refunds?`} + content={ + + No. Limit orders and cross-chain swaps are not eligible for gas refunds. Only standard swaps on KyberSwap + are eligible. + + } + /> + + handleToggleExpand(Panel.Q_Vote)} + isExpanded={expandedPanel === Panel.Q_Vote} + title={t`How can I vote on KIPs with my staked KNC to earn voting rewards?`} + content={ + + Once you have staked KNC, you can vote on active KyberDAO KIPs (Kyber Improvement Proposals) on the{' '} + Vote page to earn voting rewards. Users who stake KNC can + enjoy gas refunds + vote on KIPs to and earn even more rewards. For more information on how to vote, please + visit{' '} + + https://docs.kyberswap.com/governance/kyberdao + + . + + } + /> + + handleToggleExpand(Panel.Q_Limit)} + isExpanded={expandedPanel === Panel.Q_Limit} + title={t`What is the maximum gas refund limit for a user?`} + content={ + Each user wallet address is eligible for gas refund of up to $200 within two epoch cycles. + } + /> + + handleToggleExpand(Panel.Q_Term)} + isExpanded={expandedPanel === Panel.Q_Term} + title={t`Terms and Conditions`} + content={ + <> +
  • + + These Terms and Conditions (Terms) + should be read in conjunction with the KyberSwap Terms of Use, which lay out the terms and conditions + that apply to all KyberSwap activities. + +
  • +
    +
  • + + Currently, only trades on Ethereum are eligible for gas refunds. Gas refunds amount is based on the + users’ KNC staking tier and the value of each trade. + +
  • +
    +
  • + + For a trade to be eligible for gas refunds (after staking KNC), the trade value has to be ≥$200; + calculated by KyberSwap at the point of trade. + +
  • +
    +
  • + Each address has a maximum limit of $200 in gas refunds per month. +
  • +
    +
  • + + KyberSwap retains the right to amend the gas refund program's end date with reasonable notice. + +
  • +
    +
  • + + KyberSwap maintains the right, at its sole discretion, to remove rewards for any user who violates, + cheats, or exploits the program. + +
  • + + } + /> +
    + ) +} + +export default FAQ diff --git a/src/pages/KyberDAO/KNCUtility/GasRefundBox.tsx b/src/pages/KyberDAO/KNCUtility/GasRefundBox.tsx new file mode 100644 index 0000000000..a7aa37f75d --- /dev/null +++ b/src/pages/KyberDAO/KNCUtility/GasRefundBox.tsx @@ -0,0 +1,322 @@ +import { Trans, t } from '@lingui/macro' +import axios from 'axios' +import { BigNumber } from 'ethers' +import { darken } from 'polished' +import { useCallback, useState } from 'react' +import { useMedia } from 'react-use' +import { Flex, Text } from 'rebass' +import styled, { css } from 'styled-components' + +import { NotificationType } from 'components/Announcement/type' +import { ButtonLight, ButtonPrimary } from 'components/Button' +import Dots from 'components/Dots' +import { RowBetween } from 'components/Row' +import { MouseoverTooltip, TextDashed } from 'components/Tooltip' +import { REWARD_SERVICE_API } from 'constants/env' +import { KNC } from 'constants/tokens' +import { useActiveWeb3React, useWeb3React } from 'hooks' +import { + isSupportKyberDao, + useEligibleTransactions, + useGasRefundInfo, + useGasRefundTier, + useVotingInfo, +} from 'hooks/kyberdao' +import useTheme from 'hooks/useTheme' +import { useNotify, useOpenNetworkModal, useWalletModalToggle } from 'state/application/hooks' +import { useTransactionAdder } from 'state/transactions/hooks' +import { TRANSACTION_TYPE } from 'state/transactions/type' +import { LinkStyledButton, MEDIA_WIDTHS } from 'theme' +import { formattedNum } from 'utils' +import { sendEVMTransaction } from 'utils/sendTransaction' + +import TimerCountdown from '../TimerCountdown' +import EligibleTxModal from './EligibleTxModal' +import { KNCUtilityTabs } from './type' + +const Hr = styled.hr` + width: 100%; + border: none; + height: 1px; + background-color: ${({ theme }) => theme.border}; + margin: 0; +` + +const Wrapper = styled(Flex)` + width: 100%; + border-radius: 20px; + padding: 20px; + background-color: ${({ theme }) => theme.tableHeader}; + gap: 28px; + flex-direction: column; +` + +const Tab = styled(Text)<{ active?: boolean }>` + ${({ theme }) => theme.flexRowNoWrap} + align-items: left; + border-radius: 3rem; + outline: none; + cursor: pointer; + text-decoration: none; + color: ${({ theme }) => theme.subText}; + font-size: 14px; + line-height: 20px; + font-weight: 500; + + ${({ active, theme }) => + active && + css` + border-radius: 12px; + font-weight: 600; + color: ${theme.primary}; + `} + + :hover { + color: ${({ theme }) => darken(0.1, theme.primary)}; + } +` + +export default function GasRefundBox() { + const { account, chainId } = useActiveWeb3React() + const { library } = useWeb3React() + const [selectedTab, setSelectedTab] = useState(KNCUtilityTabs.Available) + const theme = useTheme() + const { totalReward, reward, claimableReward } = useGasRefundInfo({ rewardStatus: selectedTab }) + const toggleWalletModal = useWalletModalToggle() + const [isShowEligibleTx, setShowEligibleTx] = useState(false) + const openNetworkModal = useOpenNetworkModal() + const notify = useNotify() + const [claiming, setClaiming] = useState(false) + const addTransactionWithType = useTransactionAdder() + const eligibleTxs = useEligibleTransactions(1, 1) + const upToXXSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToXXSmall}px)`) + const upToExtraSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToExtraSmall}px)`) + const { userTier, gasRefundPerCentage } = useGasRefundTier() + const { daoInfo: { first_epoch_start_timestamp = 0, current_epoch = 0, epoch_period_in_seconds = 0 } = {} } = + useVotingInfo() + + const claimRewards = useCallback(async () => { + if (!account || !library || !claimableReward || claimableReward.knc <= 0) return + + setClaiming(true) + + const url = REWARD_SERVICE_API + '/rewards/claim' + const data = { + wallet: account, + chainId: chainId.toString(), + clientCode: 'gas-refund', + ref: '', + } + let response: any + try { + response = await axios({ method: 'POST', url, data }) + if (response?.data?.code !== 200000) throw new Error(response?.data?.message) + } catch (error) { + console.error('Claim error:', { error }) + notify({ + title: t`Claim Error`, + summary: error?.response?.data?.message || error?.message || 'Unknown error', + type: NotificationType.ERROR, + }) + setClaiming(false) + return + } + + const rewardContractAddress = response.data.data.ContractAddress + const encodedData = response.data.data.EncodedData + try { + const tx = await sendEVMTransaction( + account, + library, + rewardContractAddress, + encodedData, + BigNumber.from(0), + async transactionResponse => { + const transactionReceipt = await transactionResponse.wait() + if (transactionReceipt.status === 1) { + setClaiming(false) + } + }, + ) + if (!tx) throw new Error() + addTransactionWithType({ + hash: tx.hash, + type: TRANSACTION_TYPE.CLAIM_REWARD, + extraInfo: { + tokenAddress: KNC[chainId].address, + tokenAmount: claimableReward.knc.toString(), + tokenSymbol: 'KNC', + }, + }) + } catch (error) { + console.error('Claim error:', { error }) + notify({ + title: t`Claim Error`, + summary: error.message || 'Unknown error', + type: NotificationType.ERROR, + }) + } finally { + setClaiming(false) + } + }, [account, addTransactionWithType, chainId, claimableReward, library, notify]) + + return ( + + + + + + Rewards available to claim} placement="top"> + setSelectedTab(KNCUtilityTabs.Available)} + > + Available + + + +  |  + + Rewards to claim after the end of the countdown period} + placement="top" + > + setSelectedTab(KNCUtilityTabs.Pending)} + > + Pending + + + +  |  + + Rewards successfully claimed} placement="top"> + setSelectedTab(KNCUtilityTabs.Claimed)} + > + Claimed + + + + + {!!userTier && !!gasRefundPerCentage && ( + + + Tier {userTier} - {gasRefundPerCentage * 100}% Gas Refund + + + )} + + + + + {account ? formattedNum(reward?.knc.toString() || '0') : '--'} KNC + + + {account ? (reward?.usd ? '~' : '') + formattedNum(reward?.usd.toString() || '0', true) : '$ --'} + + + + {selectedTab === KNCUtilityTabs.Available ? ( + account ? ( + isSupportKyberDao(chainId) ? ( + claiming ? ( + + + Claiming + + + ) : ( + + Claim + + ) + ) : ( + + Gas Refund Rewards is only available on Ethereum chain. Switch your network to continue{' '} + here + + } + width="244px" + > + + Claim + + + ) + ) : ( + + Connect Wallet + + ) + ) : selectedTab === KNCUtilityTabs.Pending ? ( + + + Available to claim in{' '} + + + + ) : null} + + + +
    + + + + Total Gas Refund = Available + Pending + Claimed Gas Refund} + placement="top" + > + Total Gas Refund + + + + + {account ? formattedNum(totalReward?.knc.toString() ?? '0') : '--'} KNC + + + {account + ? (totalReward?.usd ? '~' : '') + formattedNum(totalReward?.usd.toString() ?? '0', true) + : '$ --'} + + + + + {!!account && !!eligibleTxs?.transactions.length && ( + setShowEligibleTx(isShowEligibleTx => !isShowEligibleTx)} + style={{ whiteSpace: 'nowrap' }} + width="max-content" + > + + Your Transactions + + + )} + + + setShowEligibleTx(false)} /> +
    + ) +} diff --git a/src/pages/KyberDAO/KNCUtility/Table.tsx b/src/pages/KyberDAO/KNCUtility/Table.tsx new file mode 100644 index 0000000000..c07dfbedca --- /dev/null +++ b/src/pages/KyberDAO/KNCUtility/Table.tsx @@ -0,0 +1,60 @@ +import { rgba, transparentize } from 'polished' +import styled from 'styled-components' + +export const TableWrapper = styled.div` + width: 100%; + padding: 20px; + ${({ theme }) => theme.mediaWidth.upToExtraSmall` + padding: 16px 0; + `} + + display: flex; + flex-direction: column; + background: ${({ theme }) => rgba(theme.buttonGray, 0.8)}; + border-radius: 20px; +` + +export const Table = styled.div` + display: flex; + width: 100%; + flex-direction: column; +` +export const Row = styled.div<{ $background?: string }>` + width: 100%; + height: 36px; + padding: 0 20px; + background: unset; + + display: grid; + align-items: center; + grid-template-columns: 48px 1fr 96px 24px; + column-gap: 16px; + border-top: 1px solid ${({ theme }) => theme.border}; + + ${({ theme }) => theme.mediaWidth.upToExtraSmall` + column-gap: 8px; + `} + + &[role="button"] { + cursor: pointer; + } +` + +export const TableHeader = styled(Row)` + border-top: none; + background: linear-gradient(0deg, #134134 0%, #0f221e 100%); + background: linear-gradient( + 0deg, + ${({ theme }) => transparentize(0.5, theme.primary)} 0%, + ${({ theme }) => transparentize(0.8, theme.primary)} 100% + ); +` +export const TableRow = styled(Row)`` + +export const HeaderCell = styled.span<{ textAlign?: 'left' | 'center' | 'right' }>` + font-weight: 500; + font-size: 16px; + line-height: 36px; + color: ${({ theme }) => theme.text}; + text-align: ${({ textAlign }) => textAlign || 'left'}; +` diff --git a/src/pages/KyberDAO/KNCUtility/TxTable.tsx b/src/pages/KyberDAO/KNCUtility/TxTable.tsx new file mode 100644 index 0000000000..759035d652 --- /dev/null +++ b/src/pages/KyberDAO/KNCUtility/TxTable.tsx @@ -0,0 +1,66 @@ +import { rgba } from 'polished' +import styled from 'styled-components' + +export const TableWrapper = styled.div` + width: 100%; + padding: 20px; + ${({ theme }) => theme.mediaWidth.upToExtraSmall` + padding: 16px 0; + `} + + display: flex; + flex-direction: column; + background: ${({ theme }) => rgba(theme.buttonGray, 0.8)}; + border-radius: 20px; +` + +export const Table = styled.div` + display: flex; + width: 100%; + flex-direction: column; +` +export const Row = styled.div<{ $background?: string }>` + width: 100%; + height: 46px; + padding: 0 20px; + background: unset; + + display: grid; + align-items: center; + grid-template-columns: 2fr 1fr 1fr 1.5fr; + column-gap: 16px; + border-top: 1px solid ${({ theme }) => theme.border}; + + ${({ theme }) => theme.mediaWidth.upToMedium` + grid-template-columns: 2fr 1fr 1fr 1fr; + column-gap: 8px; + `} + + ${({ theme }) => theme.mediaWidth.upToExtraSmall` + grid-template-columns: 1fr 1fr; + `} + + &[role="button"] { + cursor: pointer; + } +` + +export const TableHeader = styled(Row)` + border-top: none; + background-color: ${({ theme }) => theme.background}; + border-radius: 8px 8px 0px 0px; + color: ${({ theme }) => theme.subText}; +` +export const TableRow = styled(Row)`` + +export const Cell = styled.span<{ textAlign?: 'left' | 'center' | 'right' }>` + font-size: 12px; + font-weight: 500; + line-height: 16px; + text-align: ${({ textAlign }) => textAlign || 'left'}; +` +export const HeaderCell = styled(Cell)`` + +export const RowCell = styled(Cell)` + color: ${({ theme }) => theme.text}; +` diff --git a/src/pages/KyberDAO/KNCUtility/index.tsx b/src/pages/KyberDAO/KNCUtility/index.tsx new file mode 100644 index 0000000000..1deb13ff50 --- /dev/null +++ b/src/pages/KyberDAO/KNCUtility/index.tsx @@ -0,0 +1,287 @@ +import { Trans } from '@lingui/macro' +import { formatUnits } from 'ethers/lib/utils' +import { NavLink } from 'react-router-dom' +import { useMedia } from 'react-use' +import { Flex, Text } from 'rebass' +import styled from 'styled-components' + +import bgimg from 'assets/images/about_background.png' +import kncDropping from 'assets/images/gas-refund/knc-dropping.png' +import kncs from 'assets/images/gas-refund/kncs.png' +import ringwaveGif from 'assets/images/gas-refund/ringwave.gif' +import ringwave from 'assets/images/gas-refund/ringwave.png' +import crystals from 'assets/svg/crystals.svg' +import { ButtonLight } from 'components/Button' +import Column from 'components/Column' +import { RowBetween } from 'components/Row' +import { APP_PATHS } from 'constants/index' +import { useActiveWeb3React } from 'hooks' +import { useStakingInfo } from 'hooks/kyberdao' +import useTheme from 'hooks/useTheme' +import { ExternalLink, MEDIA_WIDTHS } from 'theme' + +import KNCLogo from '../kncLogo' +import FAQ from './FAQ' +import GasRefundBox from './GasRefundBox' +import { HeaderCell, Table, TableHeader, TableRow } from './Table' + +const Wrapper = styled.div` + width: 100%; + background-image: url(${bgimg}); + background-size: 100% auto; + background-repeat: repeat-y; + z-index: 1; + background-color: transparent; + background-position: top; + display: flex; + flex-direction: column; + align-items: center; + + padding: 24px 48px; + ${({ theme }) => theme.mediaWidth.upToMedium` + padding: 16px; + `} +` + +const Container = styled.div` + max-width: 1224px; +` + +const Row = styled.div` + width: 100%; + margin: auto; + display: flex; + justify-content: space-between; + gap: 48px; + padding: 24px 0; + align-items: flex-start; + + ${({ theme }) => theme.mediaWidth.upToMedium` + width: 100%; + flex-direction: column; + align-items: center; + gap: 48px; + `} + + & > * { + flex: 1 1 0px; + max-width: 588px; + ${({ theme }) => theme.mediaWidth.upToMedium` + max-width: 700px; + `} + width: 100%; + } +` + +// todo: will add again, dont remove this +// const EndedTag = styled.div` +// padding: 2px 12px; +// width: fit-content; +// border-radius: 12px; +// background: ${({ theme }) => transparentize(0.8, theme.red)}; +// color: ${({ theme }) => theme.red}; +// font-size: 12px; +// font-weight: 500; +// ` + +const FormWrapper = styled.div` + background-color: ${({ theme }) => theme.background}; + border-radius: 20px; + padding: 16px; + width: 100%; +` + +const YourStakedKNC = styled(FormWrapper)` + display: flex; + justify-content: space-between; + align-items: center; + gap: 8px; + border: 1px solid ${({ theme }) => theme.primary}; +` + +export default function KNCUtility() { + const { account } = useActiveWeb3React() + const theme = useTheme() + const { stakedBalance } = useStakingInfo() + const upToMedium = useMedia(`(max-width: ${MEDIA_WIDTHS.upToMedium}px)`) + + return ( + + + + + + KNC Utility + + + + + Your Staked KNC + + + {account ? formatUnits(stakedBalance) : '--'} KNC + + + + + + + Stake here ↗ + + + + + + + + Stake your KNC (Kyber Network Crystal) tokens to vote on KIP proposals, plus enjoy multiple benefits + such as saving on gas fees, earning rewards, and more. + + + + + + + { + currentTarget.onerror = null // prevents looping + currentTarget.src = ringwave + }} + /> + + + + + + Gas Refund Program + + {/* + + Ended + + */} + + {upToMedium ||
    } + + + + + {upToMedium || kncs} + + + + How to participate + + + + To participate in KyberSwap's Gas Refund Program, you must first stake KNC and then meet the + necessary trading requirements: + + + + + Step 1 - Stake KNC on KyberDAO +
    + Step 2 - Trade on KyberSwap +
    +
    +
      +
    • + + + Value of each trade (calculated at the point of the trade) on KyberSwap has to be ≥ $200 + + +
    • +
    • + + Trades only on Ethereum chain are applicable + +
    • +
    • + + + The amount of the gas refunded will depend on your tier displayed below. Read more{' '} + + here ↗ + + + +
    • +
    + + + + Tier + + + KNC Staked + + + Gas Refund + + + + Tier 1 + 500 KNC + 10% + + + Tier 2 + 5,000 KNC + 15% + + + Tier 3 + 10,000 KNC + 20% + +
    +
    +
    + + + + FAQ + + + + + + {upToMedium ? null : ( + + Gas Refund + + )} + + + + ) +} diff --git a/src/pages/KyberDAO/KNCUtility/type.ts b/src/pages/KyberDAO/KNCUtility/type.ts new file mode 100644 index 0000000000..236c4ce5f2 --- /dev/null +++ b/src/pages/KyberDAO/KNCUtility/type.ts @@ -0,0 +1,5 @@ +export enum KNCUtilityTabs { + Available = 'claimable', + Pending = 'pending', + Claimed = 'claimed', +} diff --git a/src/pages/KyberDAO/StakeKNC/index.tsx b/src/pages/KyberDAO/StakeKNC/index.tsx index 5227ddaa60..7fdaf3e892 100644 --- a/src/pages/KyberDAO/StakeKNC/index.tsx +++ b/src/pages/KyberDAO/StakeKNC/index.tsx @@ -4,7 +4,7 @@ import { isMobile } from 'react-device-detect' import Skeleton from 'react-loading-skeleton' import { NavLink, useNavigate } from 'react-router-dom' import { Text } from 'rebass' -import styled from 'styled-components' +import styled, { css } from 'styled-components' import bgimg from 'assets/images/about_background.png' import governancePNG from 'assets/images/kyberdao/governance.png' @@ -13,11 +13,15 @@ import kyberCrystal from 'assets/images/kyberdao/kyber_crystal.png' import kyberdaoPNG from 'assets/images/kyberdao/kyberdao.png' import migratePNG from 'assets/images/kyberdao/migrate.png' import stakevotePNG from 'assets/images/kyberdao/stake_vote.png' +import GasRefundTier1 from 'assets/svg/refund1.svg' +import GasRefundTier2 from 'assets/svg/refund2.svg' +import GasRefundTier3 from 'assets/svg/refund3.svg' import { ButtonLight, ButtonPrimary } from 'components/Button' import Divider from 'components/Divider' import Row, { RowBetween, RowFit } from 'components/Row' +import { MouseoverTooltip } from 'components/Tooltip' import { APP_PATHS } from 'constants/index' -import { useStakingInfo } from 'hooks/kyberdao' +import { useGasRefundTier, useStakingInfo } from 'hooks/kyberdao' import useMixpanel, { MIXPANEL_TYPE } from 'hooks/useMixpanel' import useTheme from 'hooks/useTheme' import { ApplicationModal } from 'state/application/actions' @@ -75,18 +79,25 @@ const CardGroup = styled.div` width: 772px; order: 3; - ${({ theme }) => theme.mediaWidth.upToExtraSmall` + ${({ theme }) => theme.mediaWidth.upToSmall` width: 100vw; padding: 0 16px; `} ` -const Card = styled.div` +const Card = styled.div<{ background?: string }>` display: flex; border: 1px solid ${({ theme }) => theme.border}; border-radius: 20px; gap: 12px; width: 100%; padding: 24px 16px; + backdrop-filter: blur(25px); + ${({ background }) => + background && + css` + background: ${background}; + `} + backdrop-filter: blur(25px); ` const Image = styled.img` height: 44px; @@ -121,6 +132,7 @@ export default function StakeKNC() { }) } const kncPrice = useKNCPrice() + const { userTier, gasRefundPerCentage } = useGasRefundTier() return ( @@ -219,6 +231,38 @@ export default function StakeKNC() { Migrate + + KNC Utility + + + KNC Utility + + + + + Discover more staking KNC utility and benefits{' '} + here ↗ + + + + + + Tier {userTier} - You are eligible for{' '} + {gasRefundPerCentage * 100}% gas refund. + + } + > + {userTier === 1 ? ( + Tier 1 + ) : userTier === 2 ? ( + Tier 2 + ) : userTier === 3 ? ( + Tier 3 + ) : null} + + KyberDAO v1 @@ -235,20 +279,6 @@ export default function StakeKNC() { - - KNC Utility - - - KNC Utility - - - Coming soon - - - - - Note: Staking KNC is only available on Ethereum chain - diff --git a/src/pages/KyberDAO/TimerCountdown.tsx b/src/pages/KyberDAO/TimerCountdown.tsx index 4b5ede74a1..b26cd26ab1 100644 --- a/src/pages/KyberDAO/TimerCountdown.tsx +++ b/src/pages/KyberDAO/TimerCountdown.tsx @@ -1,59 +1,71 @@ import { transparentize } from 'polished' import { useEffect, useState } from 'react' import { Clock } from 'react-feather' -import { Text } from 'rebass' +import { SxStyleProp, Text } from 'rebass' import { useTheme } from 'styled-components' import { RowFit } from 'components/Row' -export default function TimerCountdown({ endTime }: { endTime: number }) { +export default function TimerCountdown({ + endTime, + maxLength = Number.MAX_SAFE_INTEGER, + sx, +}: { + endTime: number + maxLength?: number + sx?: SxStyleProp +}) { const theme = useTheme() - const [timeString, setTimeString] = useState('') + const [timeString, setTimeString] = useState('--') + useEffect(() => { - const interval = setInterval(() => { + const calculate = () => { const seconds = endTime - Math.floor(Date.now() / 1000) if (seconds < 0) return setTimeString('') if (seconds < 60) return setTimeString(Math.floor(seconds) + 's') + const levels = [ [Math.floor(seconds / 31536000), 'years'], [Math.floor((seconds % 31536000) / 86400), ' days'], - [Math.floor(((seconds % 31536000) % 86400) / 3600), 'h'], - [Math.floor((((seconds % 31536000) % 86400) % 3600) / 60), 'm'], - [seconds - Math.floor(seconds / 60) * 60, 's'], + [Math.floor((seconds % 86400) / 3600), 'h'], + [Math.floor((seconds % 3600) / 60), 'm'], + [seconds % 60, 's'], ] - let returntext = '' - let hideZero = true - for (let i = 0, max = levels.length; i < max; i++) { + const texts: string[] = [] + let hideZero = true // hide leading zero, e.g: 0 days 0h 10min -> 10min + for (let i = 0, count = 0; i < levels.length && count < maxLength; i++) { if (levels[i][0] === 0 && hideZero) { continue } else { hideZero = false } - returntext += ' ' + levels[i][0] + levels[i][1] + count++ + texts.push(String(levels[i][0]) + levels[i][1]) } - setTimeString(returntext.trim()) - }, 1000) + setTimeString(texts.join(' ')) + } + calculate() + const intervalId = setInterval(calculate, 1000) return () => { - clearInterval(interval) + clearInterval(intervalId) } - }, [endTime]) + }, [endTime, maxLength]) return ( - <> - - {' '} - - {timeString || '--'} - - - + + {' '} + + {timeString} + + ) } diff --git a/src/theme/color.ts b/src/theme/color.ts new file mode 100644 index 0000000000..b742e55b7c --- /dev/null +++ b/src/theme/color.ts @@ -0,0 +1,109 @@ +const white = '#FFFFFF' +const black = '#000000' + +export function colors(darkMode: boolean) { + return { + // base + white, + black, + + // text + text: darkMode ? '#ffffff' : '#222222', + darkText: '#222222', + textReverse: darkMode ? '#222222' : '#ffffff', + subText: darkMode ? '#A9A9A9' : '#5E5E5E', + disableText: darkMode ? '#373737' : '#B6B6B6', + + // backgrounds + background: darkMode ? '#1C1C1C' : '#ffffff', + background2: darkMode ? '#1C1C1C' : '#f5f5f5', + tabActive: darkMode ? '#313131' : '#ffffff', + tabBackground: darkMode ? '#0F0F0F' : '#E2E2E2', + + tableHeader: darkMode ? '#313131' : '#FBFBFB', + buttonBlack: darkMode ? '#0F0F0F' : '#f5f5f5', + buttonGray: darkMode ? '#292929' : '#E2E2E2', + + text2: darkMode ? '#C3C5CB' : '#565A69', + text3: darkMode ? '#6C7284' : '#888D9B', + text4: darkMode ? '#565A69' : '#C3C5CB', + text6: darkMode ? '#6d8591' : '#565A69', + text7: darkMode ? '#c9d2d7' : '#565A69', + text9: darkMode ? '#859aa5' : '#859aa5', + text10: darkMode ? '#00a2f7' : '#00a2f7', + text11: darkMode ? '#f4f4f4' : '#565A69', + text13: darkMode ? '#f5f5f5' : '#333333', + text15: darkMode ? '#3b3b3b' : '#8A8A8A', + text16: darkMode ? '#D8D8D8' : '#212121', + + // backgrounds + bg1: darkMode ? '#212429' : '#FFFFFF', + bg2: darkMode ? '#222c31' : '#F7F8FA', + bg3: darkMode ? '#40444F' : '#dcdbdc', + bg3Opacity4: darkMode ? '#40444F69' : '#69dcdbdc69', + bg4: darkMode ? '#565A69' : '#CED0D9', + bg5: darkMode ? '#6C7284' : '#888D9B', + bg7: darkMode ? '#31CB9E' : '#98e5ce', + bg8: darkMode ? '#1d7a5f' : '#31CB9E', + bg9: darkMode ? '#1d2a32' : '#ecebeb', + bg10: darkMode ? '#263239' : '#f5f5f5', + bg11: darkMode ? '#1b2226' : '#ebeaea', + bg13: darkMode ? '#1f292e' : '#e8e9ed', + bg14: darkMode ? '#40505a' : '#a9a9a9', + bg15: darkMode ? '#1f292e' : '#f5f5f5', + bg16: darkMode ? '#1f292e' : '#ffffff', + bg17: darkMode ? '#31cb9e33' : '#31cb9e1a', + bg18: darkMode ? '#1a4052' : '#ecebeb', + bg19: darkMode ? '#222c31' : '#ffffff', + bg20: darkMode ? '#243036' : '#F5F5F5', + bg21: darkMode + ? 'linear-gradient(90deg, rgba(29, 122, 95, 0.5) 0%, rgba(29, 122, 95, 0) 100%)' + : 'linear-gradient(90deg, rgba(49, 203, 158, 0.15) 0%, rgba(49, 203, 158, 0) 100%)', // success + bg22: darkMode + ? 'linear-gradient(90deg, rgba(255, 83, 123, 0.4) 0%, rgba(255, 83, 123, 0) 100%)' + : 'linear-gradient(90deg, rgba(255, 83, 123, 0.15) 0%, rgba(255, 83, 123, 0) 100%)', // error + bg23: darkMode + ? 'linear-gradient(90deg, rgba(255, 153, 1, 0.5) 0%, rgba(255, 153, 1, 0) 100%)' + : 'linear-gradient(90deg, rgba(255, 153, 1, 0.5) 0%, rgba(255, 153, 1, 0) 100%)', // warning + radialGradient: darkMode ? 'radial-gradient(#095143, #06291d)' : 'radial-gradient(#DAEBE6, #DAF1EC)', + + //specialty colors + modalBG: darkMode ? 'rgba(0,0,0,.425)' : 'rgba(0,0,0,0.3)', + advancedBG: darkMode ? '#1d272b' : '#ecebeb', + advancedBorder: darkMode ? '#303e46' : '#dcdbdc', + + //primary colors + primary: '#31CB9E', + primary30: darkMode ? '#1D4D3D' : '#C7E9DC', + + // border colors + border: darkMode ? '#505050' : '#C1C1C1', + btnOutline: darkMode ? '#31cb9e' : '#333333', + + // table colors + oddRow: darkMode ? '#283339' : '#ffffff', + evenRow: darkMode ? '#303e46' : '#f4f4f4', + + // other + red: darkMode ? '#FF537B' : '#FF6871', + warning: '#FF9901', + apr: '#0faaa2', + lightGreen: '#98E5CE', + darkerGreen: '#1D7A5F', + red1: '#FF6871', + red2: '#F82D3A', + red3: '#D60000', + darkGreen: '#1D7A5F', + green: '#31CB9E', + green1: '#27AE60', + yellow1: '#FFE270', + yellow2: '#F3841E', + blue1: '#31cb9e', + lightBlue: '#78d5ff', + darkBlue: '#1183b7', + blue: darkMode ? '#08A1E7' : '#31cb9e', + shadow: darkMode ? 'rgba(0, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0.04)', + } as const +} + +export type Colors = ReturnType diff --git a/src/theme/components.tsx b/src/theme/components.tsx index 8055f6ad90..1b8bcb7af9 100644 --- a/src/theme/components.tsx +++ b/src/theme/components.tsx @@ -87,7 +87,9 @@ export const LinkStyledButton = styled.button<{ disabled?: boolean }>` cursor: ${({ disabled }) => (disabled ? 'default' : 'pointer')}; color: ${({ theme, disabled }) => (disabled ? theme.text2 : theme.primary)}; - font-weight: 500; + font-weight: inherit; + font-size: inherit; + padding: 0; :hover { text-decoration: ${({ disabled }) => (disabled ? null : 'underline')}; diff --git a/src/theme/index.tsx b/src/theme/index.tsx index 66af6f5b1f..8df6296c1f 100644 --- a/src/theme/index.tsx +++ b/src/theme/index.tsx @@ -10,7 +10,7 @@ import styled, { import { useIsDarkMode } from 'state/user/hooks' -import { Colors } from './styled' +import { Colors, colors } from './color' export * from './components' @@ -35,114 +35,6 @@ const mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } return accumulator }, {} as { [width in keyof typeof MEDIA_WIDTHS]: typeof css }) -const white = '#FFFFFF' -const black = '#000000' - -function colors(darkMode: boolean): Colors { - return { - // base - white, - black, - - // text - text: darkMode ? '#ffffff' : '#222222', - darkText: '#222222', - textReverse: darkMode ? '#222222' : '#ffffff', - subText: darkMode ? '#A9A9A9' : '#5E5E5E', - disableText: darkMode ? '#373737' : '#B6B6B6', - - // backgrounds - background: darkMode ? '#1C1C1C' : '#ffffff', - background2: darkMode ? '#1C1C1C' : '#f5f5f5', - tabActive: darkMode ? '#313131' : '#ffffff', - tabBackground: darkMode ? '#0F0F0F' : '#E2E2E2', - - tableHeader: darkMode ? '#313131' : '#FBFBFB', - buttonBlack: darkMode ? '#0F0F0F' : '#f5f5f5', - buttonGray: darkMode ? '#292929' : '#E2E2E2', - - text2: darkMode ? '#C3C5CB' : '#565A69', - text3: darkMode ? '#6C7284' : '#888D9B', - text4: darkMode ? '#565A69' : '#C3C5CB', - text6: darkMode ? '#6d8591' : '#565A69', - text7: darkMode ? '#c9d2d7' : '#565A69', - text9: darkMode ? '#859aa5' : '#859aa5', - text10: darkMode ? '#00a2f7' : '#00a2f7', - text11: darkMode ? '#f4f4f4' : '#565A69', - text13: darkMode ? '#f5f5f5' : '#333333', - text15: darkMode ? '#3b3b3b' : '#8A8A8A', - text16: darkMode ? '#D8D8D8' : '#212121', - - // backgrounds - bg1: darkMode ? '#212429' : '#FFFFFF', - bg2: darkMode ? '#222c31' : '#F7F8FA', - bg3: darkMode ? '#40444F' : '#dcdbdc', - bg3Opacity4: darkMode ? '#40444F69' : '#69dcdbdc69', - bg4: darkMode ? '#565A69' : '#CED0D9', - bg5: darkMode ? '#6C7284' : '#888D9B', - bg7: darkMode ? '#31CB9E' : '#98e5ce', - bg8: darkMode ? '#1d7a5f' : '#31CB9E', - bg9: darkMode ? '#1d2a32' : '#ecebeb', - bg10: darkMode ? '#263239' : '#f5f5f5', - bg11: darkMode ? '#1b2226' : '#ebeaea', - bg13: darkMode ? '#1f292e' : '#e8e9ed', - bg14: darkMode ? '#40505a' : '#a9a9a9', - bg15: darkMode ? '#1f292e' : '#f5f5f5', - bg16: darkMode ? '#1f292e' : '#ffffff', - bg17: darkMode ? '#31cb9e33' : '#31cb9e1a', - bg18: darkMode ? '#1a4052' : '#ecebeb', - bg19: darkMode ? '#222c31' : '#ffffff', - bg20: darkMode ? '#243036' : '#F5F5F5', - bg21: darkMode - ? 'linear-gradient(90deg, rgba(29, 122, 95, 0.5) 0%, rgba(29, 122, 95, 0) 100%)' - : 'linear-gradient(90deg, rgba(49, 203, 158, 0.15) 0%, rgba(49, 203, 158, 0) 100%)', // success - bg22: darkMode - ? 'linear-gradient(90deg, rgba(255, 83, 123, 0.4) 0%, rgba(255, 83, 123, 0) 100%)' - : 'linear-gradient(90deg, rgba(255, 83, 123, 0.15) 0%, rgba(255, 83, 123, 0) 100%)', // error - bg23: darkMode - ? 'linear-gradient(90deg, rgba(255, 153, 1, 0.5) 0%, rgba(255, 153, 1, 0) 100%)' - : 'linear-gradient(90deg, rgba(255, 153, 1, 0.5) 0%, rgba(255, 153, 1, 0) 100%)', // warning - radialGradient: darkMode ? 'radial-gradient(#095143, #06291d)' : 'radial-gradient(#DAEBE6, #DAF1EC)', - - //specialty colors - modalBG: darkMode ? 'rgba(0,0,0,.425)' : 'rgba(0,0,0,0.3)', - advancedBG: darkMode ? '#1d272b' : '#ecebeb', - advancedBorder: darkMode ? '#303e46' : '#dcdbdc', - - //primary colors - primary: '#31CB9E', - primary30: darkMode ? '#1D4D3D' : '#C7E9DC', - - // border colors - border: darkMode ? '#505050' : '#C1C1C1', - btnOutline: darkMode ? '#31cb9e' : '#333333', - - // table colors - oddRow: darkMode ? '#283339' : '#ffffff', - evenRow: darkMode ? '#303e46' : '#f4f4f4', - - // other - red: darkMode ? '#FF537B' : '#FF6871', - warning: '#FF9901', - apr: '#0faaa2', - lightGreen: '#98E5CE', - darkerGreen: '#1D7A5F', - red1: '#FF6871', - red2: '#F82D3A', - red3: '#D60000', - darkGreen: '#1D7A5F', - green: '#31CB9E', - green1: '#27AE60', - yellow1: '#FFE270', - yellow2: '#F3841E', - blue1: '#31cb9e', - lightBlue: '#78d5ff', - darkBlue: '#1183b7', - blue: darkMode ? '#08A1E7' : '#31cb9e', - shadow: darkMode ? 'rgba(0, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0.04)', - } -} - function theme(darkMode: boolean): DefaultTheme { return { ...colors(darkMode), diff --git a/src/theme/styled.d.ts b/src/theme/styled.d.ts index 3cd50824fe..b72002ba88 100644 --- a/src/theme/styled.d.ts +++ b/src/theme/styled.d.ts @@ -1,97 +1,8 @@ import { FlattenSimpleInterpolation, ThemedCssFunction } from 'styled-components' -export type Color = string -export interface Colors { - // base - white: Color - black: Color - - // text - text: Color - darkText: Color - textReverse: Color - subText: Color - text2: Color - text3: Color - text4: Color - text6: Color - text7: Color - text9: Color - text10: Color - text11: Color - text13: Color - text15: Color - text16: Color - disableText: Color - - // backgrounds / greys - tableHeader: Color - background: Color - background2: Color - tabActive: Color - tabBackground: Color - bg1: Color - bg2: Color - bg3: Color - bg3Opacity4: Color - bg4: Color - bg5: Color - bg7: Color - bg8: Color - bg9: Color - bg10: Color - bg11: Color - bg13: Color - bg14: Color - bg15: Color - bg16: Color - bg17: Color - bg18: Color - bg19: Color - bg20: Color - bg21: Color - bg22: Color - bg23: Color - buttonBlack: Color - buttonGray: Color - radialGradient: Color - - modalBG: Color - advancedBG: Color - advancedBorder: Color - - //blues - primary: Color - primary30: Color +import { Colors } from './color' - // border colors - border: Color - btnOutline: Color - - // table colors - oddRow: Color - evenRow: Color - - // other - red: Color - red1: Color - red2: Color - red3: Color - green: Color - darkGreen: Color - green1: Color - yellow1: Color - yellow2: Color - blue1: Color - warning: Color - lightBlue: Color - darkBlue: Color - blue: Color - lightGreen: Color - darkerGreen: Color - apr: Color - shadow: Color -} +export type Color = string interface Grids { sm: number