From ae80a19334f4fde42846f8e66b928e5050b5f4ec Mon Sep 17 00:00:00 2001 From: Vinay Singh <122733374+vnaysngh-mudrex@users.noreply.github.com> Date: Sat, 18 May 2024 14:22:50 +0530 Subject: [PATCH] Revert "Iamoskvin/pools page" --- .env | 2 +- .env.production | 2 +- package.json | 4 - src/api/PoolsData.ts | 93 - src/apollo/client.js | 46 +- src/apollo/queries.js | 69 - src/components/FeeBadge/index.tsx | 17 - src/components/FormattedName/index.tsx | 59 - src/components/LocalLoader/index.tsx | 45 - src/components/Pools/index.tsx | 625 ----- src/constants/apiTimeframeOptions.ts | 6 - src/constants/misc.ts | 1 - src/constants/strkRewards.ts | 2 - src/graphql/data/apollo.ts | 74 +- src/hooks/useV3Positions.ts | 44 +- src/index.tsx | 1 + src/pages/Pool/PositionDetails.tsx | 115 - src/pages/Pool/index.tsx | 392 ++-- src/pages/Pool/styled.tsx | 228 +- src/pages/PoolDetails/PoolDetailsHeader.tsx | 219 ++ src/pages/PoolDetails/PoolDetailsStats.tsx | 263 +++ .../PoolDetails/PoolDetailsStatsButtons.tsx | 103 + .../PoolDetails/PoolDetailsTableSkeleton.tsx | 98 + .../PoolDetailsHeader.test.tsx.snap | 388 ++++ .../PoolDetailsStats.test.tsx.snap | 753 +++++++ .../PoolDetailsStatsButtons.test.tsx.snap | 211 ++ .../__snapshots__/index.test.tsx.snap | 2007 +++++++++++++++++ src/pages/PoolDetails/icons.tsx | 15 + src/pages/PoolDetails/index.tsx | 384 ++-- src/pages/PoolDetails/shared.ts | 14 + src/pages/RouteDefinitions.tsx | 2 - src/theme/colors.ts | 3 - src/utils/formatNum.js | 123 - yarn.lock | 229 -- 34 files changed, 4485 insertions(+), 2152 deletions(-) delete mode 100644 src/api/PoolsData.ts delete mode 100644 src/components/FeeBadge/index.tsx delete mode 100644 src/components/FormattedName/index.tsx delete mode 100644 src/components/LocalLoader/index.tsx delete mode 100644 src/components/Pools/index.tsx delete mode 100644 src/constants/apiTimeframeOptions.ts delete mode 100644 src/constants/strkRewards.ts delete mode 100644 src/pages/Pool/PositionDetails.tsx create mode 100644 src/pages/PoolDetails/PoolDetailsHeader.tsx create mode 100644 src/pages/PoolDetails/PoolDetailsStats.tsx create mode 100644 src/pages/PoolDetails/PoolDetailsStatsButtons.tsx create mode 100644 src/pages/PoolDetails/PoolDetailsTableSkeleton.tsx create mode 100644 src/pages/PoolDetails/__snapshots__/PoolDetailsHeader.test.tsx.snap create mode 100644 src/pages/PoolDetails/__snapshots__/PoolDetailsStats.test.tsx.snap create mode 100644 src/pages/PoolDetails/__snapshots__/PoolDetailsStatsButtons.test.tsx.snap create mode 100644 src/pages/PoolDetails/__snapshots__/index.test.tsx.snap create mode 100644 src/pages/PoolDetails/icons.tsx create mode 100644 src/pages/PoolDetails/shared.ts delete mode 100644 src/utils/formatNum.js diff --git a/.env b/.env index a181a6c4..85871d7e 100644 --- a/.env +++ b/.env @@ -2,7 +2,7 @@ ESLINT_NO_DEV_ERRORS=true REACT_APP_AMPLITUDE_PROXY_URL="https://api.uniswap.org/v1/amplitude-proxy" REACT_APP_AWS_API_REGION="us-east-2" -REACT_APP_AWS_API_ENDPOINT="https://api.v2.jediswap.xyz/graphql" +REACT_APP_AWS_API_ENDPOINT="https://beta.api.uniswap.org/v1/graphql" REACT_APP_BNB_RPC_URL="https://rough-sleek-hill.bsc.quiknode.pro/413cc98cbc776cda8fdf1d0f47003583ff73d9bf" REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847" REACT_APP_QUICKNODE_MAINNET_RPC_URL="https://magical-alien-tab.quiknode.pro/669e87e569a8277d3fbd9e202f9df93189f19f4c" diff --git a/.env.production b/.env.production index 1d80b331..47965b24 100644 --- a/.env.production +++ b/.env.production @@ -1,6 +1,6 @@ # These API keys are intentionally public. Please do not report them - thank you for your concern. REACT_APP_AMPLITUDE_PROXY_URL="https://api.uniswap.org/v1/amplitude-proxy" -REACT_APP_AWS_API_ENDPOINT="https://api.v2.jediswap.xyz/graphql" +REACT_APP_AWS_API_ENDPOINT="https://api.uniswap.org/v1/graphql" REACT_APP_BNB_RPC_URL="https://old-wispy-arrow.bsc.quiknode.pro/f5c060177236065c1058531a0615ab4f7a34a2fd" REACT_APP_FIREBASE_KEY="AIzaSyBcZWwTcTJHj_R6ipZcrJkXdq05PuX0Rs0" REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF" diff --git a/package.json b/package.json index 60377df7..06915c72 100644 --- a/package.json +++ b/package.json @@ -259,7 +259,6 @@ "node-fetch": "^3.3.2", "node-vibrant": "^3.2.1-alpha.1", "numbro": "^2.4.0", - "numeral": "2", "polished": "^3.3.2", "polyfill-object.fromentries": "^1.0.1", "qrcode.react": "^3.1.0", @@ -278,10 +277,7 @@ "react-redux": "^8.0.2", "react-router-dom": "^6.3.0", "react-spring": "^9.5.5", - "react-switch": "^7.0.0", "react-table": "^7.8.0", - "react-tooltip": "^5.26.3", - "react-use": "^17.5.0", "react-use-gesture": "^6.0.14", "react-virtualized-auto-sizer": "^1.0.2", "react-window": "^1.8.5", diff --git a/src/api/PoolsData.ts b/src/api/PoolsData.ts deleted file mode 100644 index f40cad07..00000000 --- a/src/api/PoolsData.ts +++ /dev/null @@ -1,93 +0,0 @@ -import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect, useState } from 'react' - -import { HISTORICAL_POOLS_DATA } from '../apollo/queries' - - -import { getPercentChange, get2DayPercentChange } from '../utils/formatNum' -import { apiTimeframeOptions } from '../constants/apiTimeframeOptions' -import { ApolloClient, NormalizedCacheObject } from '@apollo/client' -// import { useWhitelistedTokens } from './Application' - -const UPDATE = 'UPDATE' -const UPDATE_TOP_PAIRS = 'UPDATE_TOP_PAIRS' -const UPDATE_HOURLY_DATA = 'UPDATE_HOURLY_DATA' - - -async function getBulkPairData(apolloClient: ApolloClient, tokenList: string[]) { - try { - let historicalData = await apolloClient.query({ - query: HISTORICAL_POOLS_DATA({ - tokenIds: tokenList, - periods: [apiTimeframeOptions.oneDay, apiTimeframeOptions.twoDays, apiTimeframeOptions.oneWeek], - }), - fetchPolicy: 'cache-first', - }) - let oneDayData = historicalData?.data?.poolsData.reduce((obj: any, cur:any, i: number) => { - return { ...obj, [cur.pool.poolAddress]: cur?.period?.[apiTimeframeOptions.oneDay] } - }, {}) - - let twoDayData = historicalData?.data?.poolsData.reduce((obj: any, cur:any, i: number) => { - return { ...obj, [cur.pool.poolAddress]: cur?.period?.[apiTimeframeOptions.twoDays] } - }, {}) - - let oneWeekData = historicalData?.data?.poolsData.reduce((obj: any, cur:any, i: number) => { - return { ...obj, [cur.pool.poolAddress]: cur?.period?.[apiTimeframeOptions.oneWeek] } - }, {}) - let currentData = historicalData?.data?.poolsData.reduce((obj: any, cur:any, i: number) => { - return { ...obj, [cur.pool.poolAddress]: cur?.pool } - }, {}) - - - const poolList = Object.keys(currentData) - - let pairData = - poolList.map((poolAddress) => { - let pair = currentData[poolAddress] - let oneDayHistory = oneDayData?.[pair.poolAddress] - let twoDayHistory = twoDayData?.[pair.poolAddress] - let oneWeekHistory = oneWeekData?.[pair.poolAddress] - const data = parseData(pair, oneDayHistory, twoDayHistory, oneWeekHistory) - return data - }) - return pairData - } catch (e) { - console.log(e) - return null - } -} - -function parseData(data: any, oneDayData: any, twoDayData: any, oneWeekData: any) { - const oneDayVolumeUSD = oneDayData?.volumeUSD || 0 - const twoDayVolumeUSD = twoDayData?.volumeUSD || 0 - const volumeChangeUSD = get2DayPercentChange(oneDayVolumeUSD, twoDayVolumeUSD) - - const oneDayFeesUSD = oneDayData?.feesUSD || 0 - const twoDayFeesUSD = twoDayData?.feesUSD || 0 - const feesChangeUSD = get2DayPercentChange(oneDayFeesUSD, twoDayFeesUSD) - - const oneWeekVolumeUSD = oneWeekData?.volumeUSD || 0 - const newData = {...data} - // set volume properties - newData.oneDayVolumeUSD = parseFloat(oneDayVolumeUSD) - newData.oneWeekVolumeUSD = oneWeekVolumeUSD - newData.volumeChangeUSD = volumeChangeUSD - - // set fees properties - newData.oneDayFeesUSD = parseFloat(oneDayFeesUSD); - newData.feesChangeUSD = feesChangeUSD; - - // set liquidity properties - newData.liquidityChangeUSD = getPercentChange(oneDayData.totalValueLockedUSD, oneDayData.totalValueLockedUSDFirst) - - return newData -} - -export const getAllPools = async (apolloClient: ApolloClient, whitelistedIds: string[] = []) => { - try { - const bulkResults = getBulkPairData(apolloClient, whitelistedIds) - return bulkResults - } catch (e) { - console.log(e) - return null - } -} diff --git a/src/apollo/client.js b/src/apollo/client.js index 7e2526a4..6718bb5e 100644 --- a/src/apollo/client.js +++ b/src/apollo/client.js @@ -3,35 +3,33 @@ import { ChainId } from '@vnaysn/jediswap-sdk-core' import { InMemoryCache } from 'apollo-cache-inmemory' import { HttpLink } from 'apollo-link-http' -function createCache() { - return new InMemoryCache({ - dataIdFromObject: (object) => { - switch (object.__typename) { - case 'TokenDayData': { - return `${object.tokenAddress}${object.datetime}` - } - case 'FactoryDayData': { - return `${object.id}${object.dayId}` - } - case 'Token': { - return `${object.tokenAddress}${object.name}` - } - case 'Pool': { - return `${object.poolAddress}${object.datetime}` - } - default: { - return object.id || object._id - } +const cache = new InMemoryCache({ + dataIdFromObject: (object) => { + switch (object.__typename) { + case 'TokenDayData': { + return `${object.tokenAddress}${object.datetime}` } - }, - }) -} + case 'FactoryDayData': { + return `${object.id}${object.dayId}` + } + case 'Token': { + return `${object.tokenAddress}${object.name}` + } + case 'Pool': { + return `${object.poolAddress}${object.datetime}` + } + default: { + return object.id || object._id + } + } + }, +}) export const jediSwapClient = new ApolloClient({ link: new HttpLink({ uri: 'https://api.v2.jediswap.xyz/graphql', }), - cache: createCache(), + cache, shouldBatch: true, }) @@ -39,7 +37,7 @@ export const jediSwapClientSepolia = new ApolloClient({ link: new HttpLink({ uri: 'https://api.v2.sepolia.jediswap.xyz/graphql', }), - cache: createCache(), + cache, shouldBatch: true, }) diff --git a/src/apollo/queries.js b/src/apollo/queries.js index b52e7022..23c30d65 100644 --- a/src/apollo/queries.js +++ b/src/apollo/queries.js @@ -1,4 +1,3 @@ -import { apiTimeframeOptions } from 'constants/apiTimeframeOptions' import gql from 'graphql-tag' const TokenFields = ` @@ -16,33 +15,6 @@ const TokenFields = ` feesUSD } ` -const PoolFields = ` - fragment PoolFields on Pool { - poolAddress - token0 { - tokenAddress - symbol - name - #totalValueLocked - } - token1 { - tokenAddress - symbol - name - #totalValueLocked - } - volumeToken0 - volumeToken1 - volumeUSD - totalValueLockedUSD - totalValueLockedETH - totalValueLockedToken0 - totalValueLockedToken1 - token0Price - token1Price - fee - } -` export const TOKENS_DATA = ({ tokenIds = [] }) => { const tokenString = `[${tokenIds.map((token) => `"${token}"`).join(',')}]` @@ -58,44 +30,3 @@ export const TOKENS_DATA = ({ tokenIds = [] }) => { ` return gql(queryString) } - -export const HISTORICAL_POOLS_DATA = ({ tokenIds = [], periods = [] }) => { - const tokensString = `[${tokenIds.map((token) => `"${token}",`)}]` - const periodString = `[${periods.map((period) => `"${period}"`).join(',')}]` - - let queryString = ` - ${PoolFields} - query poolsData { - poolsData( - first: 500, - where: { - periodIn: ${periodString}, - bothTokenAddressIn: ${tokensString}, - } - ) { - pool { - ...PoolFields - } - period - } - } - ` - return gql(queryString) -} - -export const HISTORICAL_GLOBAL_DATA = () => { - const queryString = ` query jediswapFactories { - factoriesData { - ${apiTimeframeOptions.oneDay} - ${apiTimeframeOptions.twoDays} - } - }` - return gql(queryString) -} - -export const STRK_REWARDS_DATA = () => { - const queryString = ` query strkGrantData { - strkGrantData - }` - return gql(queryString) -} \ No newline at end of file diff --git a/src/components/FeeBadge/index.tsx b/src/components/FeeBadge/index.tsx deleted file mode 100644 index b07656f7..00000000 --- a/src/components/FeeBadge/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react' -import styled from 'styled-components' - -const FeeBadgeContainer = styled.div` - display: inline-flex; - padding: 4px 8px; - border-radius: 4px; - background: #444; - color: #fff; - font-size: 12px; - font-weight: 500; - line-height: 1; -` - -const FeeBadge = ({ children}: {children: string}) => {children} - -export default FeeBadge diff --git a/src/components/FormattedName/index.tsx b/src/components/FormattedName/index.tsx deleted file mode 100644 index 44a27a3c..00000000 --- a/src/components/FormattedName/index.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { useState } from 'react' -import styled from 'styled-components' -import Tooltip from '../Tooltip' - -const TextWrapper = styled.div<{ margin?: boolean, link?: boolean, fontSize?: string, adjustSize?: boolean }>` - position: relative; - margin-left: ${({ margin }) => margin && '4px'}; - color: ${({ theme, link }) => (link ? theme.jediWhite : theme.jediWhite)}; - font-size: ${({ fontSize }) => fontSize ?? 'inherit'}; - - :hover { - // cursor: pointer; - } - - @media screen and (max-width: 600px) { - font-size: ${({ adjustSize }) => adjustSize && '12px'}; - } -` - -const FormattedName = ({ text, maxCharacters, margin = false, adjustSize = false, fontSize, link, ...rest }: { - text: string; - maxCharacters: number; - margin?: boolean; - adjustSize?: boolean; - fontSize?: string; - link: boolean; -}) => { - const [showHover, setShowHover] = useState(false) - - if (!text) { - return null - } - - if (text.length > maxCharacters) { - return ( - - setShowHover(true)} - onMouseLeave={() => setShowHover(false)} - margin={margin} - adjustSize={adjustSize} - link={link} - fontSize={fontSize} - {...rest} - > - {' ' + text.slice(0, maxCharacters - 1) + '...'} - - - ) - } - - return ( - - {text} - - ) -} - -export default FormattedName diff --git a/src/components/LocalLoader/index.tsx b/src/components/LocalLoader/index.tsx deleted file mode 100644 index d34077db..00000000 --- a/src/components/LocalLoader/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react' -import styled, { css, keyframes } from 'styled-components' - -const pulse = keyframes` - 0% { transform: scale(1); } - 60% { transform: scale(1.1); } - 100% { transform: scale(1); } -` - -const Wrapper = styled.div<{ fill?: boolean, height?: string }>` - pointer-events: none; - display: flex; - align-items: center; - justify-content: center; - height: 100%; - width: 100%; - - ${(props) => - props.fill && !props.height - ? css` - height: 100vh; - ` - : css` - height: 180px; - `} -` - -const AnimatedImg = styled.div` - animation: ${pulse} 800ms linear infinite; - & > * { - width: 72px; - } -` - -const LocalLoader = ({ fill, ...attr }: {fill?: boolean}) => { - return ( - - - loading-icon - - - ) -} - -export default LocalLoader diff --git a/src/components/Pools/index.tsx b/src/components/Pools/index.tsx deleted file mode 100644 index f2e08c4f..00000000 --- a/src/components/Pools/index.tsx +++ /dev/null @@ -1,625 +0,0 @@ -import React, { useState, useEffect, useMemo } from 'react' -import { useMedia } from 'react-use' -import dayjs from 'dayjs' -// import LocalLoader from '../LocalLoader' -import utc from 'dayjs/plugin/utc' -import { Box, Flex, Text } from 'rebass' -import styled from 'styled-components' -import { renderToStaticMarkup } from 'react-dom/server' -import { Tooltip as ReactTooltip } from 'react-tooltip' - -// import { CustomLink } from '../Link' -import { Link as CustomLink, Link } from 'react-router-dom' -// import { withRouter } from 'react-router-dom' -import { formattedNum, formattedPercent } from '../../utils/formatNum.js' -import DoubleTokenLogo from '../DoubleLogo' - -import FormattedName from '../FormattedName' -// import { TYPE } from '../../Theme' -import { AutoColumn } from '../Column' -// import { useWhitelistedTokens } from '../../contexts/Application' -import Row, { AutoRow, RowFixed } from '../Row' -import FeeBadge from 'components/FeeBadge' -import { darkTheme } from 'theme/colors' -import { MEDIA_WIDTHS } from 'theme' -import { useDefaultActiveTokens } from 'hooks/Tokens' -import { useAccountDetails } from 'hooks/starknet-react' -import { validateAndParseAddress } from 'starknet' -import { WETH } from 'constants/tokens' -import StarknetIcon from 'assets/svg/starknet.svg' -import LocalLoader from 'components/LocalLoader' -import { ChainId } from '@vnaysn/jediswap-sdk-core' - -dayjs.extend(utc) - -const PageButtons = styled.div` - width: 100%; - display: flex; - justify-content: center; - margin-top: 2em; - margin-bottom: 0.5em; -` - -const Arrow = styled.div<{ faded?: boolean }>` - color: ${({ theme, faded }) => (faded ? theme.jediGrey : theme.jediBlue)}; - padding: 0 20px; - user-select: none; - font-size: 30px; - :hover { - cursor: pointer; - } -` -const ViewAll = styled.div` - text-align: center; - margin: 15px 0; - a { - color: ${({ theme }) => theme.jediBlue}; - text-decoration: none; - } -` - -const List = styled(Box)` - -webkit-overflow-scrolling: touch; -` -const PlaceholderContainer = styled.div` - padding: 20px; -` - -const DashGrid = styled.div<{ fade?: boolean; disbaleLinks?: boolean; focus?: boolean; center?: boolean }>` - display: grid; - grid-gap: 1em; - grid-template-columns: 100px 100px 1fr 1fr; - grid-template-areas: 'name rew liq vol'; - padding: 0 1.125rem; - - opacity: ${({ fade }) => (fade ? '0.6' : '1')}; - - > * { - justify-content: flex-end; - - :first-child { - justify-content: flex-start; - text-align: left; - } - } - - @media screen and (min-width: 740px) { - padding: 0 1.125rem; - grid-template-columns: 1.7fr 1fr 1fr}; - grid-template-areas: ' name liq vol pool '; - } - - @media screen and (min-width: 1080px) { - padding: 0 1.125rem; - grid-template-columns: 2.7fr 1fr 1fr 1fr 1fr; - grid-template-areas: ' name liq vol fees apy'; - } - - @media screen and (min-width: 1200px) { - grid-template-columns: 1.7fr 1fr 1fr 1fr 1fr 1fr; - grid-template-areas: ' name liq vol fees apy'; - } -` - -const ListWrapper = styled.div`` - -const ClickableText = styled(Text) <{ area: string }>` - color: ${({ theme }) => theme.text1}; - &:hover { - cursor: pointer; - opacity: 0.6; - } - text-align: end; - user-select: none; -` - -const DataText = styled(Flex) <{ area?: string }>` - align-items: center; - text-align: center; - color: ${({ theme }) => theme.text1}; - - & > * { - font-size: 14px; - } - - @media screen and (max-width: 600px) { - font-size: 12px; - } -` - -const LinkRow = styled(Link)` - align-items: center; - display: flex; - cursor: pointer; - user-select: none; - display: flex; - flex-direction: column; - justify-content: space-between; - color: ${({ theme }) => theme.neutral1}; - padding: 16px; - text-decoration: none; - font-weight: 535; - - & > div:not(:first-child) { - text-align: center; - } - :hover { - background-color: ${({ theme }) => theme.deprecated_hoverDefault}; - } - - @media screen and (min-width: ${MEDIA_WIDTHS.deprecated_upToSmall}px) { - /* flex-direction: row; */ - } - - ${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall` - flex-direction: column; - row-gap: 8px; - `}; -` -const Rewards = styled.div` - display: flex; - flex-direction: row; -` -const StrkBadgeOuter = styled.div` - display: flex; - flex-direction: row; - border-radius: 2px; - border: 1px solid #EC796B; - background: #0C0C4F; - padding: 3px; - width: 48px; - height: 16px; - // flex-shrink: 0; - align-items: center; -` -const Divider = styled(Box)` - height: 1px; - background-color: ${({ theme }) => theme.divider}; -` - -const SORT_FIELD = { - LIQ: 0, - VOL: 1, - VOL_7DAYS: 3, - FEES: 4, - APY: 5, -} - -const FIELD_TO_VALUE = (field: number) => { - switch (field) { - case SORT_FIELD.LIQ: - return 'totalValueLockedUSD' - case SORT_FIELD.VOL: - return 'oneDayVolumeUSD' - case SORT_FIELD.VOL_7DAYS: - return 'oneWeekVolumeUSD' - case SORT_FIELD.FEES: - return 'oneDayFeesUSD' - default: - return 'totalValueLockedUSD' - } -} - -const formatDataText = ( - value: string | 0 | JSX.Element, - trackedValue: string, - supressWarning = false, - textAlign: CanvasTextAlign = 'right' -) => { - return ( - -
{value}
-
- ) -} - -const DEFAULT_NO_PAIRS_PLACEHOLDER_TEXT = 'Pairs will appear here' - -function calcCommonApr(pairData: any) { - const feeRatio24H = pairData.oneDayFeesUSD / pairData.totalValueLockedUSD - const aprFee = feeRatio24H * 365 * 100 - const aprStarknet = pairData.aprStarknet *100 - const cleanedAprFee = isNaN(aprFee) || !isFinite(aprFee) ? 0 : aprFee - const cleanedAprStarknet = isNaN(aprStarknet) || !isFinite(aprStarknet) ? 0 : aprStarknet - const cleanedAprCommon = cleanedAprFee + cleanedAprStarknet - return cleanedAprCommon -} - -function PairList({ - pairs, - color, - disbaleLinks, - maxItems = 10, - useTracked = false, - waitForData = true, - noPairsPlaceholderText = DEFAULT_NO_PAIRS_PLACEHOLDER_TEXT, - showRewardedOnly = false -}: { - pairs: any - color?: string - disbaleLinks?: boolean - maxItems?: number - useTracked?: boolean - waitForData?: boolean - noPairsPlaceholderText?: string, - showRewardedOnly?: boolean -}) { - const below600 = useMedia('(max-width: 600px)') - const below740 = useMedia('(max-width: 740px)') - const below1080 = useMedia('(max-width: 1080px)') - // pagination - const [page, setPage] = useState(1) - const [maxPage, setMaxPage] = useState(1) - const ITEMS_PER_PAGE = maxItems - const { chainId } = useAccountDetails() - - const chainIdFinal = chainId || ChainId.MAINNET - const allTokens = useDefaultActiveTokens(chainIdFinal) - // sorting - const [sortDirection, setSortDirection] = useState(true) - const [sortedColumn, setSortedColumn] = useState(SORT_FIELD.LIQ) - - const filteredPairsAddresses = useMemo(() => { - return ( - pairs && - Object.keys(pairs) - .filter((address: string) => { - if (!showRewardedOnly) { - return true - } - const pair = pairs[address] - return pair.rewarded - }) - ) - }, [pairs, showRewardedOnly]) - - useEffect(() => { - setMaxPage(1) // edit this to do modular - setPage(1) - }, [pairs]) - - useEffect(() => { - if (filteredPairsAddresses) { - let extraPages = 1 - if (filteredPairsAddresses.length % ITEMS_PER_PAGE === 0) { - extraPages = 0 - } - setMaxPage(Math.floor(filteredPairsAddresses.length / ITEMS_PER_PAGE) + extraPages) - } - }, [ITEMS_PER_PAGE, filteredPairsAddresses]) - - const ListItem = ({ - pairAddress, - pairData, - index, - doubleCurrencyImageData, - }: { - pairAddress: string - pairData: any - index: number - doubleCurrencyImageData: any - }) => { - const feePercent = (pairData ? parseFloat(pairData.fee) / 10000 : 0) + '%' - let rewardsBadges = null; - const strkBadge = -
- -
-
STRK
-
- if (pairData.rewarded) { - rewardsBadges = - {pairData.aprStarknet && strkBadge} - - } - if (pairData && pairData.token0 && pairData.token1) { - const feeTier = pairData.fee / 10 ** 6 - const liquidity = formattedNum(pairData.totalValueLockedUSD, true) - - const volume = formattedNum( - pairData.oneDayVolumeUSD ? pairData.oneDayVolumeUSD : pairData.oneDayVolumeUntracked, - true - ) - - const fees = formattedNum(pairData.oneDayFeesUSD, true) - - const feeRatio24H = pairData.oneDayFeesUSD / pairData.totalValueLockedUSD - const aprFee = feeRatio24H * 365 * 100 - const aprStarknet = pairData.aprStarknet *100 - - const cleanedAprFee = isNaN(aprFee) || !isFinite(aprFee) ? 0 : aprFee - const displayAprFee = formattedPercent(cleanedAprFee, true) - - const cleanedAprStarknet = isNaN(aprStarknet) || !isFinite(aprStarknet) ? 0 : aprStarknet - const displayAprStarknet = formattedPercent(cleanedAprStarknet, true) - - const cleanedAprCommon = cleanedAprFee + cleanedAprStarknet - const displayAprCommon = formattedPercent(cleanedAprCommon, true, darkTheme.jediBlue, 700) - - // const apy = ((1 + feeRatio24H) ** 365 - 1) * 100 - // const cleanedApy = isNaN(apy) || !isFinite(apy) ? 0 : apy - // const displayApy = formattedPercent(cleanedApy, true) - const getTooltipMarkup = () => { - return ( -
- Fee APR: {displayAprFee} - STRK APR: {displayAprStarknet} -
- ) - } - - const weekVolume = formattedNum( - pairData.oneWeekVolumeUSD ? pairData.oneWeekVolumeUSD : pairData.oneWeekVolumeUntracked, - true - ) - - // const weekVolume = Math.round(pairData.oneWeekVolumeUSD) - if (below1080) { - return ( - -
-
- {doubleCurrencyImageData && ( - - )} - - {/* */} - - {/* */} - {feePercent} - -
-
-
-
Liquidity
-
{formatDataText(liquidity, pairData.totalValueLockedUSD, false, 'left')}
-
-
-
Volume (24H)
-
{formatDataText(volume, pairData.oneDayVolumeUSD, false, 'left')}
-
-
-
Fees (24H)
-
{formatDataText(fees, pairData.oneDayVolumeUSD, false, 'left')}
-
-
-
- - ) - } - return ( - - - - {/* {!below600 &&
{index}
} */} - {doubleCurrencyImageData && ( - - )} - - - {feePercent} - -
- {rewardsBadges} - {formatDataText(liquidity, pairData.totalValueLockedUSD)} - {formatDataText(volume, pairData.oneDayVolumeUSD)} - {/* {!below1080 && {formatDataText(weekVolume, pairData.oneWeekVolumeUSD)}} */} - {!below1080 && {formatDataText(fees, pairData.oneDayVolumeUSD)}} - {!below1080 && ( - - {formatDataText(displayAprCommon, String(displayAprCommon), pairData.oneDayVolumeUSD === 0)} - - )} -
- - ) - } else { - return null - } - } - - const pairList = - filteredPairsAddresses && - filteredPairsAddresses - .sort((addressA: string, addressB: string) => { - const pairA = pairs[addressA] - const pairB = pairs[addressB] - if (sortedColumn === SORT_FIELD.APY) { - const cleanedAprCommonA = calcCommonApr(pairA) - const cleanedAprCommonB = calcCommonApr(pairB) - - return cleanedAprCommonA > cleanedAprCommonB ? (sortDirection ? -1 : 1) * 1 : (sortDirection ? -1 : 1) * -1 - } - return parseFloat(pairA[FIELD_TO_VALUE(sortedColumn)]) > parseFloat(pairB[FIELD_TO_VALUE(sortedColumn)]) - ? (sortDirection ? -1 : 1) * 1 - : (sortDirection ? -1 : 1) * -1 - }) - .slice(ITEMS_PER_PAGE * (page - 1), page * ITEMS_PER_PAGE) - .map((pairAddress: string, index: number) => { - const pairData = pairs[pairAddress] - let doubleCurrencyImageData = undefined - if (pairData && pairData.token0 && pairData.token1 && chainIdFinal) { - doubleCurrencyImageData = { - token0: - pairData.token0.symbol === 'ETH' - ? WETH[chainIdFinal] - : allTokens[validateAndParseAddress(pairData.token0.tokenAddress)], - token1: - pairData.token1.symbol === 'ETH' - ? WETH[chainIdFinal] - : allTokens[validateAndParseAddress(pairData.token1.tokenAddress)], - } - } - return ( - pairAddress && ( -
- - {!below1080 && } -
- ) - ) - }) - - if (!pairList) { - return - } - - if (waitForData && !pairList.length) { - return - } - - if (!waitForData && !pairList.length) { - return ( - - {/* */} - {noPairsPlaceholderText} - {/* */} - - ) - } - - return ( - - {!below1080 && ( - <> - - - {/* */} - Pool Name - {/* */} - - - Rewards - - - { - setSortedColumn(SORT_FIELD.LIQ) - setSortDirection(sortedColumn !== SORT_FIELD.LIQ ? true : !sortDirection) - }} - > - Liquidity {sortedColumn === SORT_FIELD.LIQ ? (!sortDirection ? '↑' : '↓') : ''} - - - - { - setSortedColumn(SORT_FIELD.VOL) - setSortDirection(sortedColumn !== SORT_FIELD.VOL ? true : !sortDirection) - }} - > - Volume (24H) - {sortedColumn === SORT_FIELD.VOL ? (!sortDirection ? '↑' : '↓') : ''} - - - {/* {!below1080 && ( - - { - setSortedColumn(SORT_FIELD.VOL_7DAYS) - setSortDirection(sortedColumn !== SORT_FIELD.VOL_7DAYS ? true : !sortDirection) - }} - > - Volume (7D) {sortedColumn === SORT_FIELD.VOL_7DAYS ? (!sortDirection ? '↑' : '↓') : ''} - - - )} */} - {!below1080 && ( - - { - setSortedColumn(SORT_FIELD.FEES) - setSortDirection(sortedColumn !== SORT_FIELD.FEES ? true : !sortDirection) - }} - > - Fees (24H) {sortedColumn === SORT_FIELD.FEES ? (!sortDirection ? '↑' : '↓') : ''} - - - )} - {!below1080 && ( - - { - setSortedColumn(SORT_FIELD.APY) - setSortDirection(sortedColumn !== SORT_FIELD.APY ? true : !sortDirection) - }} - > - 1 yr Fee/Liquidity {sortedColumn === SORT_FIELD.APY ? (!sortDirection ? '↑' : '↓') : ''} - - - )} - - - - )} - {pairList} - -
{ - setPage(page === 1 ? page : page - 1) - }} - > - {'<'} -
-
{page + ' of ' + maxPage}
-
{ - setPage(page === maxPage ? page : page + 1) - }} - > - {'>'} -
-
- -
- ) -} - -// export default withRouter(PairList) -export default PairList diff --git a/src/constants/apiTimeframeOptions.ts b/src/constants/apiTimeframeOptions.ts deleted file mode 100644 index 6076e206..00000000 --- a/src/constants/apiTimeframeOptions.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const apiTimeframeOptions = { - oneDay: 'one_day', - twoDays: 'two_days', - oneWeek: 'one_week', - oneMonth: 'one_month', -} \ No newline at end of file diff --git a/src/constants/misc.ts b/src/constants/misc.ts index 7c7c075d..311381f7 100644 --- a/src/constants/misc.ts +++ b/src/constants/misc.ts @@ -34,4 +34,3 @@ export const BLOCKED_PRICE_IMPACT_NON_EXPERT: Percent = new Percent(15, 100) // export const ZERO_PERCENT = new Percent(0) export const ONE_HUNDRED_PERCENT = new Percent(1) - diff --git a/src/constants/strkRewards.ts b/src/constants/strkRewards.ts deleted file mode 100644 index f81e45c6..00000000 --- a/src/constants/strkRewards.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const STARKNET_REWARDS_API_URL = 'https://kx58j6x5me.execute-api.us-east-1.amazonaws.com//starknet/fetchFile?file=qa_strk_grant.json' -export const REWARDS_SELECTOR = 'Jediswap_v1' \ No newline at end of file diff --git a/src/graphql/data/apollo.ts b/src/graphql/data/apollo.ts index 33e63e83..22686815 100644 --- a/src/graphql/data/apollo.ts +++ b/src/graphql/data/apollo.ts @@ -11,45 +11,45 @@ export const apolloClient = new ApolloClient({ uri: GRAPHQL_URL, headers: { 'Content-Type': 'application/json', - // Origin: 'https://app.uniswap.org', + Origin: 'https://app.uniswap.org', }, cache: new InMemoryCache({ - // typePolicies: { - // Query: { - // fields: { - // nftBalances: relayStylePagination(['ownerAddress', 'filter']), - // nftAssets: relayStylePagination(), - // nftActivity: relayStylePagination(), - // // tell apollo client how to reference Token items in the cache after being fetched by queries that return Token[] - // token: { - // read(_, { args, toReference }): Reference | undefined { - // return toReference({ - // __typename: 'Token', - // chain: args?.chain, - // address: args?.address, - // }) - // }, - // }, - // }, - // }, - // Token: { - // // key by chain, address combination so that Token(chain, address) endpoint can read from cache - // /** - // * NOTE: In any query for `token` or `tokens`, you must include the `chain` and `address` fields - // * in order for result to normalize properly in the cache. - // */ - // keyFields: ['chain', 'address'], - // fields: { - // address: { - // read(address: string | null): string | null { - // // backend endpoint sometimes returns checksummed, sometimes lowercased addresses - // // always use lowercased addresses in our app for consistency - // return address?.toLowerCase() ?? null - // }, - // }, - // }, - // }, - // }, + typePolicies: { + Query: { + fields: { + nftBalances: relayStylePagination(['ownerAddress', 'filter']), + nftAssets: relayStylePagination(), + nftActivity: relayStylePagination(), + // tell apollo client how to reference Token items in the cache after being fetched by queries that return Token[] + token: { + read(_, { args, toReference }): Reference | undefined { + return toReference({ + __typename: 'Token', + chain: args?.chain, + address: args?.address, + }) + }, + }, + }, + }, + Token: { + // key by chain, address combination so that Token(chain, address) endpoint can read from cache + /** + * NOTE: In any query for `token` or `tokens`, you must include the `chain` and `address` fields + * in order for result to normalize properly in the cache. + */ + keyFields: ['chain', 'address'], + fields: { + address: { + read(address: string | null): string | null { + // backend endpoint sometimes returns checksummed, sometimes lowercased addresses + // always use lowercased addresses in our app for consistency + return address?.toLowerCase() ?? null + }, + }, + }, + }, + }, }), defaultOptions: { watchQuery: { diff --git a/src/hooks/useV3Positions.ts b/src/hooks/useV3Positions.ts index 404b6c74..6b7e208d 100644 --- a/src/hooks/useV3Positions.ts +++ b/src/hooks/useV3Positions.ts @@ -1,16 +1,14 @@ import { BigNumber } from '@ethersproject/bignumber' import { CallStateResult, useSingleCallResult, useSingleContractMultipleData } from 'lib/hooks/multicall' -import { useEffect, useMemo, useState } from 'react' +import { useMemo } from 'react' import { PositionDetails } from 'types/position' import { useContractRead } from '@starknet-react/core' import NFTPositionManagerABI from 'contracts/nonfungiblepositionmanager/abi.json' import { DEFAULT_CHAIN_ID, NONFUNGIBLE_POOL_MANAGER_ADDRESS } from 'constants/tokens' -import { BlockTag, cairo, uint256 } from 'starknet' +import { BlockTag, cairo } from 'starknet' import { useV3NFTPositionManagerContract } from './useContract' import { toInt } from 'utils/toInt' import { useAccountDetails } from './starknet-react' -import { providerInstance } from 'utils/getLibrary' -import { ChainId } from '@vnaysn/jediswap-sdk-core' export interface TickType { mag: BigNumber @@ -61,6 +59,7 @@ const flattenedPositionsV3 = (positionsV3: FlattenedPositions): FlattenedPositio const usePositionResults = (tokenId: number): UseV3Positions => { const { chainId } = useAccountDetails() + const { data, isLoading, error } = useContractRead({ functionName: 'get_position', args: [cairo.uint256(tokenId)], @@ -256,40 +255,3 @@ export function useV3Positions(account: string | null | undefined): UseV3Positio positions, } } - -export function useTokenIds(address: string | undefined, chainId: ChainId | undefined) { - const [tokenIds, setTokenIds] = useState([]) - const [loading, setLoading] = useState(false) - useEffect(() => { - const getTokenIds = async () => { - if (address && chainId) { - setLoading(true) - const provider = providerInstance(chainId ?? DEFAULT_CHAIN_ID) - const contract_address = NONFUNGIBLE_POOL_MANAGER_ADDRESS[chainId ?? DEFAULT_CHAIN_ID] - const tokenIdsResults = await provider.callContract({ - entrypoint: 'get_all_tokens_for_owner', - contractAddress: contract_address, - calldata: [address], - }) - if (tokenIdsResults && tokenIdsResults.result) { - // Slice the first index - const tokenIdsResultsArr = tokenIdsResults.result - - //converting array of uint256 tokenids into bn - const tokenIdsResultsArrWithoutLength = tokenIdsResultsArr.slice(1) - const returnDataIterator = tokenIdsResultsArrWithoutLength.flat()[Symbol.iterator]() - const tokenIdsArray = [...Array(tokenIdsResultsArrWithoutLength.length / 2)].map(() => { - return Number( - uint256.uint256ToBN({ low: returnDataIterator.next().value, high: returnDataIterator.next().value }) - ) - }) - setTokenIds(tokenIdsArray) - } - setLoading(false) - } - } - - getTokenIds() - }, [chainId, address]) - return { tokenIds, loading } -} diff --git a/src/index.tsx b/src/index.tsx index 5709b6d9..6341bbd5 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -14,6 +14,7 @@ import { HashRouter, useLocation } from 'react-router-dom' import { MulticallUpdater } from 'lib/state/multicall' import StarkMulticallUpdater from './state/multicall/updater' import { BlockNumberProvider } from 'lib/hooks/useBlockNumber' +import { apolloClient } from 'graphql/data/apollo' import { FeatureFlagsProvider } from 'featureFlags' // import { SystemThemeUpdater, ThemeColorMetaUpdater } from 'theme/components/ThemeToggle' import Web3Provider from './components/Web3Provider' diff --git a/src/pages/Pool/PositionDetails.tsx b/src/pages/Pool/PositionDetails.tsx deleted file mode 100644 index 48c52707..00000000 --- a/src/pages/Pool/PositionDetails.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { Trans } from '@lingui/macro' -import { useAccountDetails } from "hooks/starknet-react" -import { useV3PositionsFromTokenId } from "hooks/useV3Positions" -import { useMemo } from "react" -import { useUserHideClosedPositions } from "state/user/hooks" -import { FlattenedPositions } from 'hooks/useV3Positions' -import styled, { css, useTheme } from 'styled-components' -import { ButtonPrimary, ButtonText } from "components/Button" -import { PositionsLoadingPlaceholder } from "." -import { ThemedText } from 'theme/components' -import { Inbox } from 'react-feather' -import PositionList from 'components/PositionList' - -const ErrorContainer = styled.div` - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - margin: auto; - min-height: 25vh; - @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { - padding: 0px 52px; - } - - @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { - padding: 0px 52px; - } -` - -const IconStyle = css` - width: 48px; - height: 48px; - margin-bottom: 0.5rem; -` - -const InboxIcon = styled(Inbox)` - ${IconStyle} -` - - -export function PositionDetails(props: any) { - const { address } = useAccountDetails() - const { tokenIds, showConnectAWallet, toggleWalletDrawer, token0, token1, fee } = props - const [userHideClosedPositions, setUserHideClosedPositions] = useUserHideClosedPositions() - const { positions, loading: positionsLoading } = useV3PositionsFromTokenId(tokenIds, address) - let filteredPositions = positions - if (token0 && token1) { - filteredPositions = positions?.filter(pos => { - //@ts-ignore - if (pos?.token0.toString(16) === token0.slice(2) && pos?.token1.toString(16) === token1.slice(2) && pos?.fee === fee) { - return true - } - //@ts-ignore - if (pos?.token0.toString(16) === token1.slice(2) && pos?.token1.toString(16) === token0.slice(2) && pos?.fee === fee) { - return true - } - return false - }) - } - const theme = useTheme() - const [openPositions, closedPositions] = filteredPositions?.reduce<[FlattenedPositions[], FlattenedPositions[]]>( - (acc, p) => { - acc[!parseInt(p.liquidity.toString()) ? 1 : 0].push(p) - return acc - }, - [[], []] - ) ?? [[], []] - - const userSelectedPositionSet = useMemo( - () => [...openPositions, ...(userHideClosedPositions ? [] : closedPositions)], - [closedPositions, openPositions, userHideClosedPositions] - ) - - return ( - <> - <> - {positionsLoading ? ( - - ) : userSelectedPositionSet && userSelectedPositionSet.length > 0 ? ( - - ) : ( - - - -
- Your active liquidity positions will appear here. -
-
- {!showConnectAWallet && closedPositions.length > 0 && ( - setUserHideClosedPositions(!userHideClosedPositions)} - > - Show closed positions - - )} - {showConnectAWallet && ( - - Connect wallet - - )} -
- )} - - {/* {userSelectedPositionSet.length ? null : } */} - - ) -} \ No newline at end of file diff --git a/src/pages/Pool/index.tsx b/src/pages/Pool/index.tsx index 002752df..f36e0ca5 100644 --- a/src/pages/Pool/index.tsx +++ b/src/pages/Pool/index.tsx @@ -1,30 +1,118 @@ import { Trans } from '@lingui/macro' +import { BrowserEvent, InterfaceElementName, InterfaceEventName, InterfacePageName } from '@uniswap/analytics-events' import { useAccountDetails } from 'hooks/starknet-react' import { useEffect, useMemo, useState } from 'react' +import { AlertTriangle, BookOpen, ChevronDown, ChevronsRight, Inbox, Layers } from 'react-feather' import { Link } from 'react-router-dom' import styled, { css, useTheme } from 'styled-components' +import { Trace, TraceEvent } from 'analytics' import { useToggleAccountDrawer } from 'components/AccountDrawer' +import { ButtonGray, ButtonPrimary, ButtonText } from 'components/Button' import { AutoColumn } from 'components/Column' +import { FlyoutAlignment, Menu } from 'components/Menu' +import PositionList from 'components/PositionList' import Row, { AutoRow, RowBetween, RowFixed } from 'components/Row' import { SwitchLocaleLink } from 'components/SwitchLocaleLink' import { isSupportedChain } from 'constants/chains' // import { useFilterPossiblyMaliciousPositions } from 'hooks/useFilterPossiblyMaliciousPositions' -import { useTokenIds } from 'hooks/useV3Positions' +import { useNetworkSupportsV2 } from 'hooks/useNetworkSupportsV2' +import { FlattenedPositions, useV3PositionsFromTokenId } from 'hooks/useV3Positions' +import { useUserHideClosedPositions } from 'state/user/hooks' import { ThemedText } from 'theme/components' -import { ButtonRow, ErrorContainer, LoadingRows, MainContentWrapper, NetworkIcon, OnlyRewardedSwitcher, OnlyRewardedSwitcherContainer, OnlyRewardedSwitcherLabel, PageHeader, PageWrapper, Panel, PanelTopLight, PanelWrapper, ResponsiveButtonPrimary, ResponsiveButtonTabs, TitleRow } from './styled' -import { getAllPools } from 'api/PoolsData' -import Pools from 'components/Pools' -import { formattedNum, formattedPercent, get2DayPercentChange, getPercentChange } from 'utils/formatNum' -import { HISTORICAL_GLOBAL_DATA, STRK_REWARDS_DATA } from 'apollo/queries' -import { apiTimeframeOptions } from 'constants/apiTimeframeOptions' -import { ETH_ADDRESS } from 'constants/tokens' -import { getClient } from 'apollo/client' -import { ChainId } from '@vnaysn/jediswap-sdk-core' -import { useDefaultActiveTokens } from 'hooks/Tokens' -import { PositionDetails } from './PositionDetails' -import { ApolloQueryResult } from '@apollo/client' +import { LoadingRows } from './styled' +import fetchTokenIds from 'api/fetchTokenId' +import { DEFAULT_CHAIN_ID, NONFUNGIBLE_POOL_MANAGER_ADDRESS } from 'constants/tokens' +import { providerInstance } from 'utils/getLibrary' +import { cairo, hash, num, uint256 } from 'starknet' +import JSBI from 'jsbi' -export function PositionsLoadingPlaceholder() { +const PageWrapper = styled(AutoColumn)` + padding: 0px 8px 0px; + max-width: 920px; + width: 100%; + + @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { + padding-top: 20px; + } +` +const TitleRow = styled(RowBetween)` + color: ${({ theme }) => theme.neutral2}; + @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { + flex-wrap: wrap; + gap: 12px; + width: 100%; + padding-left: 12px; + } +` + +const PositionsText = styled.div` + color: ${({ theme }) => theme.jediWhite}; + font-family: DM Sans; + font-size: 20px; + font-style: normal; + font-weight: 500; + line-height: 100%; /* 20px */ +` +const ButtonRow = styled(AutoRow)`` + +const ErrorContainer = styled.div` + align-items: center; + display: flex; + flex-direction: column; + justify-content: center; + margin: auto; + min-height: 25vh; + @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { + padding: 0px 52px; + } + + @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { + padding: 0px 52px; + } +` + +const IconStyle = css` + width: 48px; + height: 48px; + margin-bottom: 0.5rem; +` + +const NetworkIcon = styled(AlertTriangle)` + ${IconStyle} +` + +const InboxIcon = styled(Inbox)` + ${IconStyle} +` + +const ResponsiveButtonPrimary = styled(ButtonPrimary)` + border-radius: 8px; + font-size: 16px; + padding: 6px 8px; + width: 175px; + margin-left: auto; + height: 38px; + @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { + width: 132px; + } +` + +const MainContentWrapper = styled.main<{ isWalletConnected?: boolean; filteredPositions?: any }>` + background-color: ${({ theme, isWalletConnected, filteredPositions }) => + isWalletConnected && filteredPositions ? 'rgba(196, 196, 196, 0.01)' : theme.jediNavyBlue}; + border-radius: 8px; + box-shadow: ${({ isWalletConnected, filteredPositions }) => + isWalletConnected && filteredPositions + ? `0px 0.76977px 30.79088px 0px rgba(227, 222, 255, 0.2) inset, + 0px 3.07909px 13.8559px 0px rgba(154, 146, 210, 0.3) inset, + 0px 75.43767px 76.9772px -36.94907px rgba(202, 172, 255, 0.3) inset, + 0px -63.12132px 52.3445px -49.26542px rgba(96, 68, 144, 0.3) inset` + : ''}; + @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { + } +` + +function PositionsLoadingPlaceholder() { return (
@@ -75,99 +163,104 @@ function WrongNetworkCard() { ) } -export default function Pool() { - const [poolsData, setpoolsData] = useState([]) - const { address, chainId } = useAccountDetails() +function PositionDetails(props: any) { + const { address } = useAccountDetails() + const { tokenIds, showConnectAWallet, toggleWalletDrawer } = props + const [userHideClosedPositions, setUserHideClosedPositions] = useUserHideClosedPositions() + const { positions, loading: positionsLoading } = useV3PositionsFromTokenId(tokenIds, address) + const theme = useTheme() + const [openPositions, closedPositions] = positions?.reduce<[FlattenedPositions[], FlattenedPositions[]]>( + (acc, p) => { + acc[!parseInt(p.liquidity.toString()) ? 1 : 0].push(p) + return acc + }, + [[], []] + ) ?? [[], []] - const { tokenIds, loading: loadingPositions } = useTokenIds(address, chainId); + const userSelectedPositionSet = useMemo( + () => [...openPositions, ...(userHideClosedPositions ? [] : closedPositions)], + [closedPositions, openPositions, userHideClosedPositions] + ) - const [showMyPositions, setShowMyPositions] = useState(false) - const [showRewardedOnly, setShowRewardedOnly] = useState(false) - const [globalPoolsData, setGlobalPoolsData] = useState({}) + return ( + <> + <> + {positionsLoading ? ( + + ) : userSelectedPositionSet && userSelectedPositionSet.length > 0 ? ( + + ) : ( + + + +
+ Your active liquidity positions will appear here. +
+
+ {!showConnectAWallet && closedPositions.length > 0 && ( + setUserHideClosedPositions(!userHideClosedPositions)} + > + Show closed positions + + )} + {showConnectAWallet && ( + + Connect wallet + + )} +
+ )} + + {/* {userSelectedPositionSet.length ? null : } */} + + ) +} - const chainIdFinal = chainId || ChainId.MAINNET - const allTokens = useDefaultActiveTokens(chainIdFinal) - const whitelistedIds = Object.keys(allTokens) - const graphqlClient = getClient(chainIdFinal) - //fetch pools data and rewards data +export default function Pool() { + const { address, chainId } = useAccountDetails() + const [tokenIds, setTokenIds] = useState([]) + const [loadingPositions, setLoadingPositions] = useState(false) + //fetch Token Ids useEffect(() => { - let ignore = false; - const getPoolsData = async () => { - if (whitelistedIds.length === 0) { - return - } - const requests = [ - getAllPools(graphqlClient, [...whitelistedIds, ETH_ADDRESS]), //add ETH token - graphqlClient.query({ - query: STRK_REWARDS_DATA(), - fetchPolicy: 'cache-first' + const getTokenIds = async () => { + if (address && chainId) { + setLoadingPositions(true) + const provider = providerInstance(chainId ?? DEFAULT_CHAIN_ID) + const contract_address = NONFUNGIBLE_POOL_MANAGER_ADDRESS[chainId ?? DEFAULT_CHAIN_ID] + const tokenIdsResults = await provider.callContract({ + entrypoint: 'get_all_tokens_for_owner', + contractAddress: contract_address, + calldata: [address], }) - ]; - const [poolsDataRawResult, rewardsRespResult] = await Promise.allSettled(requests); - let poolsDataRaw: any = null - if (poolsDataRawResult.status === "fulfilled") { - poolsDataRaw = poolsDataRawResult.value as ApolloQueryResult;; - } - let jediRewards: any = null; - if (rewardsRespResult.status === "fulfilled") { - const rewardsResp = rewardsRespResult.value as ApolloQueryResult; - jediRewards = rewardsResp.data?.strkGrantData; - } - const poolsData: any = {} - poolsDataRaw?.forEach((data: any) => { - const rewardName = data?.token0?.symbol + '/' + data?.token1?.symbol - const rewardsData = jediRewards?.[rewardName] - - if (rewardsData) { - data.aprStarknet = rewardsData.apr - } + if (tokenIdsResults && tokenIdsResults.result) { + // Slice the first index + const tokenIdsResultsArr = tokenIdsResults.result - data.rewarded = data.aprStarknet ? true : false - - poolsData[data.poolAddress] = data - }) - if (!ignore) { - setpoolsData(poolsData) - } - } - - getPoolsData() - return () => { - ignore = true - } - }, [Object.keys(allTokens).join(','), chainIdFinal]) - - //fetch global pools data data - useEffect(() => { - let ignore = false; - const getGlobalPoolsData = async () => { - try { - const historicalData = await graphqlClient.query({ - query: HISTORICAL_GLOBAL_DATA(), - fetchPolicy: 'cache-first', - }) - const oneDayData = historicalData.data.factoriesData[0][apiTimeframeOptions.oneDay] - const twoDaysData = historicalData.data.factoriesData[0][apiTimeframeOptions.twoDays] - if (!ignore) { - setGlobalPoolsData({ - totalValueLockedUSD: oneDayData.totalValueLockedUSD, - totalValueLockedUSDChange: getPercentChange(oneDayData.totalValueLockedUSD, oneDayData.totalValueLockedUSDFirst), - volumeUSD: oneDayData.volumeUSD, - volumeUSDChange: get2DayPercentChange(oneDayData.volumeUSD, twoDaysData.volumeUSD), - feesUSD: oneDayData.feesUSD, - feesUSDChange: get2DayPercentChange(oneDayData.feesUSD, twoDaysData.feesUSD), + //converting array of uint256 tokenids into bn + const tokenIdsResultsArrWithoutLength = tokenIdsResultsArr.slice(1) + const returnDataIterator = tokenIdsResultsArrWithoutLength.flat()[Symbol.iterator]() + const tokenIdsArray = [...Array(tokenIdsResultsArrWithoutLength.length / 2)].map(() => { + return Number( + uint256.uint256ToBN({ low: returnDataIterator.next().value, high: returnDataIterator.next().value }) + ) }) + setTokenIds(tokenIdsArray) } - } catch (e) { - console.log(e) + setLoadingPositions(false) } } - getGlobalPoolsData() - return () => { - ignore = true - } - }, [chainIdFinal]) + getTokenIds() + }, [chainId, address]) const toggleWalletDrawer = useToggleAccountDrawer() // const filteredPositions = useFilterPossiblyMaliciousPositions(userSelectedPositionSet) @@ -177,113 +270,26 @@ export default function Pool() { } const showConnectAWallet = Boolean(!address) - const poolsTable = ( -
- - Only Pools with Rewards - setShowRewardedOnly(checked)} - checked={showRewardedOnly} - handleDiameter={20} - uncheckedIcon={false} - checkedIcon={false} - width={35} - height={14} - offHandleColor={'#959595'} - onHandleColor={'#50D5FF'} - offColor={'#372554'} - onColor={'#26346d'} - /> - - - - -
- ) return ( - - POOLS - - {/* */} - - - - - - Total Liquidity - - -
- {formattedNum(globalPoolsData.totalValueLockedUSD, true)} -
-
- {formattedPercent(globalPoolsData.totalValueLockedUSDChange)} -
-
-
-
- - - - Volume (24hr) -
- - -
- {formattedNum(globalPoolsData.volumeUSD, true)} -
-
- {formattedPercent(globalPoolsData.volumeUSDChange)} -
-
- - - - - - Total fees (24hr) - - -
- {formattedNum(globalPoolsData.feesUSD, true)} -
-
- {formattedPercent(globalPoolsData.feesUSDChange)} -
-
-
-
- - - {/* */} - + - setShowMyPositions(false)} style={{ fontSize: "0.875rem" }}> - Top Pools - - setShowMyPositions(true)} style={{ fontSize: "0.875rem" }}> - My Positions - - + My Positions + + New position - {showMyPositions ? ( - loadingPositions ? ( - - ) : ( - - ) + {loadingPositions ? ( + ) : ( - poolsTable + )} {/* {userSelectedPositionSet.length ? null : } */} diff --git a/src/pages/Pool/styled.tsx b/src/pages/Pool/styled.tsx index 7a430056..ff6571dd 100644 --- a/src/pages/Pool/styled.tsx +++ b/src/pages/Pool/styled.tsx @@ -1,13 +1,7 @@ import { Text } from 'rebass'; -import styled, { css } from 'styled-components'; -import { Box as RebassBox } from 'rebass' -import Switch from 'react-switch' +import styled from 'styled-components'; import { LoadingRows as BaseLoadingRows } from 'components/Loader/styled'; -import { AutoColumn } from 'components/Column'; -import { AutoRow, RowBetween } from 'components/Row'; -import { AlertTriangle } from 'react-feather'; -import { ButtonPrimary } from 'components/Button'; export const Wrapper = styled.div` position: relative; @@ -79,223 +73,3 @@ export const LoadingRows = styled(BaseLoadingRows)` margin-bottom: 2em; } `; - -export const PageWrapper = styled(AutoColumn)` - padding: 0px 8px 0px; - max-width: 1020px; - width: 100%; - - @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { - padding-top: 20px; - } -` - -export const TitleRow = styled(RowBetween)` - color: ${({ theme }) => theme.neutral2}; - @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { - flex-wrap: wrap; - gap: 12px; - width: 100%; - padding-left: 12px; - } -` - -export const ButtonRow = styled(AutoRow)`` - -export const ErrorContainer = styled.div` - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; - margin: auto; - min-height: 25vh; - @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { - padding: 0px 52px; - } - - @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { - padding: 0px 52px; - } -` - -const IconStyle = css` - width: 48px; - height: 48px; - margin-bottom: 0.5rem; -` - -export const NetworkIcon = styled(AlertTriangle)` - ${IconStyle} -` - -export const ResponsiveButtonPrimary = styled(ButtonPrimary)` - font-family: 'DM Sans'; - border-radius: 8px; - font-size: 16px; - padding: 6px 8px; - width: 175px; - margin-left: auto; - height: 38px; - @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { - width: 132px; - } -` - -export const ResponsiveButtonTabs = styled(ButtonPrimary) <{ secondary: boolean; active: boolean }>` - font-family: 'DM Sans'; - border-radius: 4px; - font-size: 16px; - padding: 6px 8px; - background: ${({ theme, active }) => (!active ? 'transparent' : theme.jediWhite)}; - box-shadow: 0px 3.079px 13.856px 0px rgba(154, 146, 210, 0.3) inset, - 0px 0.77px 30.791px 0px rgba(227, 222, 255, 0.2) inset; - color: ${({ theme, active }) => (!active ? theme.jediWhite : theme.jediPink)}; - width: 121px; - margin-left: 0; - height: 38px; - &:hover { - background: ${({ theme, active }) => (!active ? 'transparent' : theme.jediWhite)}; - color: ${({ theme, active }) => (!active ? theme.jediWhite : theme.jediPink)}; - } - &:active { - background: ${({ theme, active }) => (!active ? 'transparent' : theme.jediWhite)}; - color: ${({ theme, active }) => (!active ? theme.jediWhite : theme.jediPink)}; - } - &:focus { - background: ${({ theme, active }) => (!active ? 'transparent' : theme.jediWhite)}; - color: ${({ theme, active }) => (!active ? theme.jediWhite : theme.jediPink)}; - } - @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { - width: 50%; - margin-bottom: 10px; - } -` -export const MainContentWrapper = styled.main<{ isWalletConnected?: boolean; filteredPositions?: any }>` - background-color: ${({ theme, isWalletConnected, filteredPositions }) => - isWalletConnected && filteredPositions ? 'rgba(196, 196, 196, 0.01)' : 'transparent'}; - border-radius: 8px; - box-shadow: ${({ isWalletConnected, filteredPositions }) => - isWalletConnected && filteredPositions - ? `0px 0.76977px 30.79088px 0px rgba(227, 222, 255, 0.2) inset, - 0px 3.07909px 13.8559px 0px rgba(154, 146, 210, 0.3) inset, - 0px 75.43767px 76.9772px -36.94907px rgba(202, 172, 255, 0.3) inset, - 0px -63.12132px 52.3445px -49.26542px rgba(96, 68, 144, 0.3) inset` - : ''}; - @media (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) { - } -` - -export const PanelWrapper = styled.div` - display: flex; - gap: 12px; - width: 100%; - align-items: start; - @media screen and (max-width: 1024px) { - flex-direction: column; - } -` -const panelPseudo = css` - :after { - content: ''; - position: absolute; - left: 0; - right: 0; - height: 10px; - } - - @media only screen and (min-width: 40em) { - :after { - content: unset; - } - } -` - -export const Panel = styled(RebassBox) <{ - hover?: boolean - background?: boolean - area?: boolean - grouped?: boolean - rounded?: boolean - last?: boolean -}>` - position: relative; - // background-color: ${({ theme }) => theme.advancedBG}; - border-radius: 8px; - padding: 1.25rem; - width: 100%; - height: 100%; - overflow: hidden; - display: flex; - flex-direction: column; - justify-content: flex-start; - background: rgba(196, 196, 196, 0.01); - box-shadow: 0px 0.77px 30.791px 0px rgba(227, 222, 255, 0.2) inset, - 0px 3.079px 13.856px 0px rgba(154, 146, 210, 0.3) inset, - 0px 75.438px 76.977px -36.949px rgba(202, 172, 255, 0.3) inset, - 0px -63.121px 52.345px -49.265px rgba(96, 68, 144, 0.3) inset; - - :hover { - cursor: ${({ hover }) => hover && 'pointer'}; - border: ${({ hover, theme }) => hover && '1px solid' + theme.bg5}; - } - - ${(props) => props.background && `background-color: ${props.theme.advancedBG};`} - - ${(props) => (props.area ? `grid-area: ${props.area};` : null)} - - ${(props) => - props.grouped && - css` - @media only screen and (min-width: 40em) { - &:first-of-type { - border-radius: 20px 20px 0 0; - } - &:last-of-type { - border-radius: 0 0 20px 20px; - } - } - `} - - ${(props) => - props.rounded && - css` - border-radius: 8px; - @media only screen and (min-width: 40em) { - border-radius: 10px; - } - `}; - - ${(props) => !props.last && panelPseudo} -` -export const PanelTopLight = styled(Panel)` - box-shadow: 0px 0.77px 30.791px 0px rgba(227, 222, 255, 0.2) inset, - 0px 3.079px 13.856px 0px rgba(154, 146, 210, 0.3) inset, - 0px 75.438px 76.977px -36.949px rgba(202, 172, 255, 0.3) inset, - 0px -63.121px 52.345px -49.265px rgba(96, 68, 144, 0.3) inset, 0px 5.388px 8.467px -3.079px #fff inset, - 0px 30.021px 43.107px -27.712px rgba(255, 255, 255, 0.5) inset; -` - -export const PageHeader = styled.div` - color: ${({ theme }) => theme.jediWhite}; - font-family: "Avenir LT Std"; - font-size: 24px; - font-weight: 750; - margin-bottom: 20px; -` - -export const OnlyRewardedSwitcherContainer = styled.div` -display: flex; -align-items: center; -gap: 16px; -margin: 15px 0; -` - -export const OnlyRewardedSwitcherLabel = styled.div` -font-family: Avenir LT Std; -font-size: 18px; -font-weight: 750; -line-height: 18px; -text-align: left; -` - -export const OnlyRewardedSwitcher = styled(Switch)`` \ No newline at end of file diff --git a/src/pages/PoolDetails/PoolDetailsHeader.tsx b/src/pages/PoolDetails/PoolDetailsHeader.tsx new file mode 100644 index 00000000..bd890393 --- /dev/null +++ b/src/pages/PoolDetails/PoolDetailsHeader.tsx @@ -0,0 +1,219 @@ +import { Trans } from '@lingui/macro' +import { ChainId, Currency } from '@vnaysn/jediswap-sdk-core' +import React from 'react' +import { Link } from 'react-router-dom' +import styled from 'styled-components' + +import blankTokenUrl from 'assets/svg/blank_token.svg' +import Column from 'components/Column' +import { ChainLogo } from 'components/Logo/ChainLogo' +import Row from 'components/Row' +import { LoadingBubble } from 'components/Tokens/loading' +import { BIPS_BASE } from 'constants/misc' +import { chainIdToBackendName } from 'graphql/data/util' +import { useCurrency } from 'hooks/Tokens' +import useTokenLogoSource from 'hooks/useAssetLogoSource' +import { ClickableStyle, ThemedText } from 'theme/components' +import { shortenAddress } from 'utils' +import { ReversedArrowsIcon } from './icons' +import { DetailBubble } from './shared' + +const HeaderColumn = styled(Column)` + gap: 36px; +` + +const StyledLink = styled(Link)` + text-decoration: none; + ${ClickableStyle} +` + +const FeeTier = styled(ThemedText.LabelMicro)` + background: ${({ theme }) => theme.surface2}; + padding: 2px 6px; + border-radius: 4px; +` + +const ToggleReverseArrows = styled(ReversedArrowsIcon)` + ${ClickableStyle} +` + +const IconBubble = styled(LoadingBubble)` + width: 32px; + height: 32px; + border-radius: 50%; +` + +interface Token { + id: string + symbol: string +} + +interface PoolDetailsHeaderProps { + chainId?: ChainId + poolAddress?: string + token0?: Token + token1?: Token + feeTier?: number + toggleReversed: React.DispatchWithoutAction + loading?: boolean +} + +export function PoolDetailsHeader({ + chainId, + poolAddress, + token0, + token1, + feeTier, + toggleReversed, + loading, +}: PoolDetailsHeaderProps) { + const currencies = [useCurrency(token0?.id, chainId) ?? undefined, useCurrency(token1?.id, chainId) ?? undefined] + const chainName = chainIdToBackendName(chainId) + const origin = `/tokens/${chainName}` + + if (loading) { + return ( + + + + + + + + + + ) + } + + return ( + + + + + Explore + + +  {'>'}  + {/* TODO: When Explore Pool table is added, link directly back to it */} + + + Pool + + +  {'>'}  + + {token0?.symbol} / {token1?.symbol} ({shortenAddress(poolAddress)}) + + + + + {chainId && ( + + )} + + {token0?.symbol} / {token1?.symbol} + + + {!!feeTier && {feeTier / BIPS_BASE}%} + + + + ) +} + +const StyledLogoParentContainer = styled.div` + position: relative; + top: 0; + left: 0; +` + +function DoubleCurrencyAndChainLogo({ + chainId, + currencies, +}: { + chainId: ChainId + currencies: Array +}) { + return ( + + + + + ) +} + +const L2LogoContainer = styled.div` + background-color: ${({ theme }) => theme.surface2}; + border-radius: 2px; + height: 12px; + left: 60%; + position: absolute; + top: 60%; + outline: 2px solid ${({ theme }) => theme.surface1}; + width: 12px; + display: flex; + align-items: center; + justify-content: center; +` + +function SquareL2Logo({ chainId }: { chainId: ChainId }) { + if (chainId === ChainId.MAINNET) { + return null + } + + return ( + + + + ) +} + +function DoubleCurrencyLogo({ chainId, currencies }: { chainId: ChainId; currencies: Array }) { + const [src, nextSrc] = useTokenLogoSource(currencies?.[0]?.wrapped.address, chainId, currencies?.[0]?.isNative) + const [src2, nextSrc2] = useTokenLogoSource(currencies?.[1]?.wrapped.address, chainId, currencies?.[1]?.isNative) + + return +} + +const DoubleLogoContainer = styled.div` + display: flex; + gap: 2px; + position: relative; + top: 0; + left: 0; + img { + width: 16px; + height: 32px; + object-fit: cover; + } + img:first-child { + border-radius: 16px 0 0 16px; + object-position: 0 0; + } + img:last-child { + border-radius: 0 16px 16px 0; + object-position: 100% 0; + } +` + +const CircleLogoImage = styled.img` + width: 32px; + height: 32px; + border-radius: 50%; +` + +interface DoubleLogoProps { + logo1?: string + logo2?: string + onError1?: () => void + onError2?: () => void +} + +function DoubleLogo({ logo1, onError1, logo2, onError2 }: DoubleLogoProps) { + return ( + + + + + ) +} diff --git a/src/pages/PoolDetails/PoolDetailsStats.tsx b/src/pages/PoolDetails/PoolDetailsStats.tsx new file mode 100644 index 00000000..4b6a3cbc --- /dev/null +++ b/src/pages/PoolDetails/PoolDetailsStats.tsx @@ -0,0 +1,263 @@ +import { Trans } from '@lingui/macro' +import { ReactNode, useMemo } from 'react' +import { Text } from 'rebass' +import styled, { css, useTheme } from 'styled-components' + +import Column from 'components/Column' +import CurrencyLogo from 'components/Logo/CurrencyLogo' +import Row from 'components/Row' +import { LoadingBubble } from 'components/Tokens/loading' +import { DeltaArrow } from 'components/Tokens/TokenDetails/Delta' +import { PoolData } from 'graphql/thegraph/PoolData' +import { useCurrency } from 'hooks/Tokens' +import { useColor } from 'hooks/useColor' +import { useScreenSize } from 'hooks/useScreenSize' +import { BREAKPOINTS } from 'theme' +import { colors } from 'theme/colors' +import { ThemedText } from 'theme/components' +import { NumberType, useFormatter } from 'utils/formatNumbers' +import { DetailBubble } from './shared' +import { ChainId } from '@vnaysn/jediswap-sdk-core' + +const HeaderText = styled(Text)` + font-weight: 485; + font-size: 24px; + line-height: 36px; + @media (max-width: ${BREAKPOINTS.lg - 1}px) { + width: 100%; + } +` + +const StatsWrapper = styled(Column)` + gap: 24px; + padding: 20px; + border-radius: 20px; + background: ${({ theme }) => theme.surface2}; + width: 100%; + + @media (max-width: ${BREAKPOINTS.lg - 1}px) { + flex-direction: row; + background: ${({ theme }) => theme.surface1}; + flex-wrap: wrap; + padding: 20px 0px; + justify-content: space-between; + } +` + +const StatItemColumn = styled(Column)` + gap: 8px; + flex: 1; + min-width: 180px; + + @media (max-width: ${BREAKPOINTS.sm}px) { + min-width: 150px; + } +` + +const PoolBalanceSymbols = styled(Row)` + justify-content: space-between; + + @media (max-width: ${BREAKPOINTS.lg - 1}px) { + flex-direction: column; + } +` + +const PoolBalanceTokenNames = styled(Row)` + font-weight: 485; + font-size: 18px; + line-height: 24px; + width: max-content; + + @media (max-width: ${BREAKPOINTS.lg - 1}px) { + font-size: 20px; + line-height: 28px; + width: 100%; + } +` + +const leftBarChartStyles = css` + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; + border-right: 1px solid ${({ theme }) => theme.surface2}; +` + +const rightBarChartStyles = css` + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; + border-left: 1px solid ${({ theme }) => theme.surface2}; +` + +const BalanceChartSide = styled.div<{ percent: number; $color: string; isLeft: boolean }>` + height: 8px; + width: ${({ percent }) => percent * 100}%; + background: ${({ $color }) => $color}; + ${({ isLeft }) => (isLeft ? leftBarChartStyles : rightBarChartStyles)} +` + +const StatSectionBubble = styled(LoadingBubble)` + width: 180px; + height: 40px; +` + +const StatHeaderBubble = styled(LoadingBubble)` + width: 116px; + height: 24px; + border-radius: 8px; +` + +interface PoolDetailsStatsProps { + poolData?: PoolData + isReversed?: boolean + chainId?: ChainId + loading?: boolean +} + +export function PoolDetailsStats({ poolData, isReversed, chainId, loading }: PoolDetailsStatsProps) { + const isScreenSize = useScreenSize() + const screenIsNotLarge = isScreenSize.lg + const { formatNumber } = useFormatter() + const theme = useTheme() + + const currency0 = useCurrency(poolData?.token0?.id, chainId) ?? undefined + const currency1 = useCurrency(poolData?.token1?.id, chainId) ?? undefined + + const color0 = useColor(currency0?.wrapped, theme.surface2, theme.darkMode) + let color1 = useColor(currency1?.wrapped, theme.surface2, theme.darkMode) + if (color0 === color1 && color0 === theme.accent1) { + color1 = colors.blue400 + } + + const [token0, token1] = useMemo(() => { + if (poolData) { + const fullWidth = poolData?.tvlToken0 / poolData?.token0Price + poolData?.tvlToken1 + const token0FullData = { + ...poolData?.token0, + price: poolData?.token0Price, + tvl: poolData?.tvlToken0, + color: color0, + percent: poolData?.tvlToken0 / poolData?.token0Price / fullWidth, + currency: currency0, + } + const token1FullData = { + ...poolData?.token1, + price: poolData?.token1Price, + tvl: poolData?.tvlToken1, + color: color1, + percent: poolData?.tvlToken1 / fullWidth, + currency: currency1, + } + return isReversed ? [token1FullData, token0FullData] : [token0FullData, token1FullData] + } + return [undefined, undefined] + }, [color0, color1, currency0, currency1, isReversed, poolData]) + + if (loading || !token0 || !token1 || !poolData) { + return ( + + + + + {Array.from({ length: 4 }).map((_, i) => ( + + + + + ))} + + ) + } + + return ( + + + Stats + + + + Pool balances + + + + {!screenIsNotLarge && ( + + )} + {formatNumber({ + input: token0.tvl, + type: NumberType.TokenNonTx, + })} +   + {token0.symbol} + + + {!screenIsNotLarge && ( + + )} + {formatNumber({ + input: token1.tvl, + type: NumberType.TokenNonTx, + })} +   + {token1.symbol} + + + {screenIsNotLarge && ( + + {token0.color && } + {token1.color && } + + )} + + TVL} value={poolData.tvlUSD} delta={poolData.tvlUSDChange} /> + 24H volume} value={poolData.volumeUSD} delta={poolData.volumeUSDChange} /> + 24H fees} value={poolData.volumeUSD * (poolData.feeTier / 1000000)} /> + + ) +} + +const StatsTextContainer = styled(Row)` + gap: 4px; + width: 100%; + align-items: flex-end; + + @media (max-width: ${BREAKPOINTS.lg - 1}px) { + flex-direction: column; + gap: 0px; + align-items: flex-start; + } +` + +const StatItemText = styled(Text)` + color: ${({ theme }) => theme.neutral1}; + font-size: 36px; + font-weight: 485; + line-height: 44px; + + @media (max-width: ${BREAKPOINTS.lg - 1}px) { + font-size: 20px; + line-height: 28px; + } +` + +function StatItem({ title, value, delta }: { title: ReactNode; value: number; delta?: number }) { + const { formatNumber, formatDelta } = useFormatter() + + return ( + + {title} + + + {formatNumber({ + input: value, + type: NumberType.FiatTokenStats, + })} + + {!!delta && ( + + + {formatDelta(delta)} + + )} + + + ) +} diff --git a/src/pages/PoolDetails/PoolDetailsStatsButtons.tsx b/src/pages/PoolDetails/PoolDetailsStatsButtons.tsx new file mode 100644 index 00000000..7bd08b7b --- /dev/null +++ b/src/pages/PoolDetails/PoolDetailsStatsButtons.tsx @@ -0,0 +1,103 @@ +import { Trans } from '@lingui/macro' +import { useAccountDetails } from 'hooks/starknet-react' +import { useNavigate } from 'react-router-dom' +import styled from 'styled-components' + +import { PositionInfo } from 'components/AccountDrawer/MiniPortfolio/Pools/cache' +import useMultiChainPositions from 'components/AccountDrawer/MiniPortfolio/Pools/useMultiChainPositions' +import { ButtonEmphasis, ButtonSize, ThemeButton } from 'components/Button' +import Row from 'components/Row' +import { LoadingBubble } from 'components/Tokens/loading' +import { Token } from 'graphql/thegraph/types-and-hooks' +import { useCurrency } from 'hooks/Tokens' +import { useSwitchChain } from 'hooks/useSwitchChain' +import { BREAKPOINTS } from 'theme' +import { currencyId } from 'utils/currencyId' +import { ChainId } from '@vnaysn/jediswap-sdk-core' + +const PoolDetailsStatsButtonsRow = styled(Row)` + gap: 12px; + + @media (max-width: ${BREAKPOINTS.lg - 1}px) { + display: none; + } +` + +const PoolButton = styled(ThemeButton)` + padding: 12px 16px 12px 12px; + border-radius: 900px; + width: 50%; +` + +const ButtonBubble = styled(LoadingBubble)` + height: 44px; + width: 175px; + border-radius: 900px; +` + +interface PoolDetailsStatsButtonsProps { + chainId?: ChainId + token0?: Token + token1?: Token + feeTier?: number + loading?: boolean +} + +function findMatchingPosition(positions: PositionInfo[], token0?: Token, token1?: Token, feeTier?: number) { + return positions?.find( + (position) => + (position?.details.token0.toLowerCase() === token0?.id || + position?.details.token0.toLowerCase() === token1?.id) && + (position?.details.token1.toLowerCase() === token0?.id || + position?.details.token1.toLowerCase() === token1?.id) && + position?.details.fee == feeTier && + !position.closed + ) +} + +export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, loading }: PoolDetailsStatsButtonsProps) { + const { chainId: walletChainId, connector, address: account } = useAccountDetails() + const { positions: userOwnedPositions } = useMultiChainPositions(account ?? '', chainId ? [chainId] : undefined) + const position = userOwnedPositions && findMatchingPosition(userOwnedPositions, token0, token1, feeTier) + const tokenId = position?.details.tokenId + const switchChain = useSwitchChain() + const navigate = useNavigate() + const currency0 = useCurrency(token0?.id, walletChainId) + const currency1 = useCurrency(token1?.id, walletChainId) + const handleOnClick = async (toSwap: boolean) => { + // if (currency0 && currency1) { + // if (walletChainId !== chainId && chainId) { + // await switchChain(connector, chainId) + // } + // navigate( + // toSwap + // ? `/swap?inputCurrency=${currencyId(currency0)}&outputCurrency=${currencyId(currency1)}` + // : `/increase/${currencyId(currency0)}/${currencyId(currency1)}/${feeTier}${tokenId ? `/${tokenId}` : ''}` + // ) + // } + } + + if (loading || !currency0 || !currency1) { + return ( + + + + + ) + } + + return ( + + handleOnClick(false)} + data-testid="pool-details-add-liquidity-button" + > + Add liquidity + + handleOnClick(true)} data-testid="pool-details-swap-button"> + Swap + + + ) +} diff --git a/src/pages/PoolDetails/PoolDetailsTableSkeleton.tsx b/src/pages/PoolDetails/PoolDetailsTableSkeleton.tsx new file mode 100644 index 00000000..37c6a0ff --- /dev/null +++ b/src/pages/PoolDetails/PoolDetailsTableSkeleton.tsx @@ -0,0 +1,98 @@ +import { Trans } from '@lingui/macro'; +import { ArrowDown } from 'react-feather'; +import styled from 'styled-components'; + +import Column from 'components/Column'; +import { ScrollBarStyles } from 'components/Common'; +import Row from 'components/Row'; +import { ThemedText } from 'theme/components'; +import { DetailBubble, SmallDetailBubble } from './shared'; + +const Table = styled(Column)` + gap: 24px; + border-radius: 20px; + border: 1px solid ${({ theme }) => theme.surface3}; + padding-bottom: 12px; + overflow-y: hidden; + ${ScrollBarStyles} +`; + +const TableRow = styled(Row)<{ $borderBottom?: boolean }>` + justify-content: space-between; + border-bottom: ${({ $borderBottom, theme }) => ($borderBottom ? `1px solid ${theme.surface3}` : 'none')}}; + padding: 12px; + min-width: max-content; +`; + +const TableElement = styled(ThemedText.BodySecondary)<{ + alignRight?: boolean + small?: boolean + large?: boolean +}>` + display: flex; + padding: 0px 8px; + flex: ${({ small }) => (small ? 'unset' : '1')}; + width: ${({ small }) => (small ? '44px' : 'auto')}; + min-width: ${({ large, small }) => (large ? '136px' : small ? 'unset' : '121px')} !important; + justify-content: ${({ alignRight }) => (alignRight ? 'flex-end' : 'flex-start')}; +`; +{ + /* TODO(WEB-2735): When making real datatable, merge in this code and deprecate this skeleton file */ +} +export function PoolDetailsTableSkeleton() { + return ( + + + + + + Time + + + + Type + + + USD + + + + + + + + + Maker + + + Txn + + + {Array.from({ length: 10 }).map((_, i) => ( + + + + + + + + + + + + + + + + + + + + + + + + ))} +
+ ); +} diff --git a/src/pages/PoolDetails/__snapshots__/PoolDetailsHeader.test.tsx.snap b/src/pages/PoolDetails/__snapshots__/PoolDetailsHeader.test.tsx.snap new file mode 100644 index 00000000..5f635c21 --- /dev/null +++ b/src/pages/PoolDetails/__snapshots__/PoolDetailsHeader.test.tsx.snap @@ -0,0 +1,388 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PoolDetailsHeader loading skeleton is shown 1`] = ` + + .c5 { + box-sizing: border-box; + margin: 0; + min-width: 0; +} + +.c6 { + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + gap: 8px; +} + +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c4 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + gap: 8px; +} + +.c2 { + border-radius: 12px; + border-radius: 12px; + height: 24px; + width: 50%; + width: 50%; + -webkit-animation: fAQEyV 1.5s infinite; + animation: fAQEyV 1.5s infinite; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + background: linear-gradient( to left, #22222212 25%, rgba(53,53,53,0.07) 50%, #22222212 75% ); + will-change: background-position; + background-size: 400%; +} + +.c3 { + height: 16px; + width: 300px; +} + +.c8 { + height: 16px; + width: 137px; +} + +.c1 { + gap: 36px; +} + +.c7 { + width: 32px; + height: 32px; + border-radius: 50%; +} + +
+
+
+
+
+
+
+
+
+ +`; + +exports[`PoolDetailsHeader renders header text correctly 1`] = ` + + .c2 { + box-sizing: border-box; + margin: 0; + min-width: 0; +} + +.c8 { + box-sizing: border-box; + margin: 0; + min-width: 0; + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; +} + +.c3 { + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c7 { + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + gap: 18px; +} + +.c9 { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + gap: 8px; +} + +.c5 { + color: #7D7D7D; +} + +.c6 { + color: #222222; +} + +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c1 { + gap: 36px; +} + +.c4 { + -webkit-text-decoration: none; + text-decoration: none; + -webkit-text-decoration: none; + text-decoration: none; + cursor: pointer; + -webkit-transition-duration: 125ms; + transition-duration: 125ms; +} + +.c4:hover { + opacity: 0.6; +} + +.c4:active { + opacity: 0.4; +} + +.c13 { + background: #F9F9F9; + padding: 2px 6px; + border-radius: 4px; +} + +.c14 { + -webkit-text-decoration: none; + text-decoration: none; + cursor: pointer; + -webkit-transition-duration: 125ms; + transition-duration: 125ms; +} + +.c14:hover { + opacity: 0.6; +} + +.c14:active { + opacity: 0.4; +} + +.c10 { + position: relative; + top: 0; + left: 0; +} + +.c11 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + gap: 2px; + position: relative; + top: 0; + left: 0; +} + +.c11 img { + width: 16px; + height: 32px; + object-fit: cover; +} + +.c11 img:first-child { + border-radius: 16px 0 0 16px; + object-position: 0 0; +} + +.c11 img:last-child { + border-radius: 0 16px 16px 0; + object-position: 100% 0; +} + +.c12 { + width: 32px; + height: 32px; + border-radius: 50%; +} + +
+
+ +
+ Explore +
+
+
+  >  +
+ +
+ Pool +
+
+
+  >  +
+
+ USDC / WETH (0x88e6...5640) +
+
+
+
+
+
+ + +
+
+
+ USDC / WETH +
+
+
+ 0.05% +
+ + + +
+
+
+`; diff --git a/src/pages/PoolDetails/__snapshots__/PoolDetailsStats.test.tsx.snap b/src/pages/PoolDetails/__snapshots__/PoolDetailsStats.test.tsx.snap new file mode 100644 index 00000000..8f406f71 --- /dev/null +++ b/src/pages/PoolDetails/__snapshots__/PoolDetailsStats.test.tsx.snap @@ -0,0 +1,753 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PoolDetailsStats pool balance chart not visible on mobile 1`] = ` + + .c5 { + box-sizing: border-box; + margin: 0; + min-width: 0; +} + +.c14 { + box-sizing: border-box; + margin: 0; + min-width: 0; + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + padding: 4px 0px; +} + +.c6 { + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c15 { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + padding: 4px 0px; +} + +.c4 { + color: #7D7D7D; +} + +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c11 { + opacity: 0; + -webkit-transition: opacity 250ms ease-in; + transition: opacity 250ms ease-in; + width: 20px; + height: 20px; + border-radius: 50%; +} + +.c10 { + width: 20px; + height: 20px; + background: #22222212; + -webkit-transition: background-color 250ms ease-in; + transition: background-color 250ms ease-in; + box-shadow: 0 0 1px white; + border-radius: 50%; +} + +.c9 { + position: relative; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} + +.c16 { + color: #FF5F52; +} + +.c2 { + font-weight: 485; + font-size: 24px; + line-height: 36px; +} + +.c1 { + gap: 24px; + padding: 20px; + border-radius: 20px; + background: #F9F9F9; + width: 100%; +} + +.c3 { + gap: 8px; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + min-width: 180px; +} + +.c7 { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.c8 { + font-weight: 485; + font-size: 18px; + line-height: 24px; + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; +} + +.c12 { + gap: 4px; + width: 100%; + -webkit-align-items: flex-end; + -webkit-box-align: flex-end; + -ms-flex-align: flex-end; + align-items: flex-end; +} + +.c13 { + color: #222222; + font-size: 36px; + font-weight: 485; + line-height: 44px; +} + +@media (max-width:1023px) { + .c2 { + width: 100%; + } +} + +@media (max-width:1023px) { + .c1 { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + background: #FFFFFF; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding: 20px 0px; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + } +} + +@media (max-width:640px) { + .c3 { + min-width: 150px; + } +} + +@media (max-width:1023px) { + .c7 { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + } +} + +@media (max-width:1023px) { + .c8 { + font-size: 20px; + line-height: 28px; + width: 100%; + } +} + +@media (max-width:1023px) { + .c12 { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + gap: 0px; + -webkit-align-items: flex-start; + -webkit-box-align: flex-start; + -ms-flex-align: flex-start; + align-items: flex-start; + } +} + +@media (max-width:1023px) { + .c13 { + font-size: 20px; + line-height: 28px; + } +} + +
+
+ Stats +
+
+
+ Pool balances +
+
+
+
+
+ UNKNOWN logo +
+
+ 90.93M USDC +
+
+
+
+ WETH logo +
+
+ 82,526.49 WETH +
+
+
+
+
+ TVL +
+
+
+ $223.2M +
+
+ + + +
+ 0.37% +
+
+
+
+
+
+ 24H volume +
+
+
+ $233.4M +
+
+ + + +
+ 17.75% +
+
+
+
+
+
+ 24H fees +
+
+
+ $116.7K +
+
+
+
+
+`; + +exports[`PoolDetailsStats renders stats text correctly 1`] = ` + + .c5 { + box-sizing: border-box; + margin: 0; + min-width: 0; +} + +.c13 { + box-sizing: border-box; + margin: 0; + min-width: 0; + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + padding: 4px 0px; +} + +.c6 { + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c14 { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + padding: 4px 0px; +} + +.c4 { + color: #7D7D7D; +} + +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c15 { + color: #FF5F52; +} + +.c2 { + font-weight: 485; + font-size: 24px; + line-height: 36px; +} + +.c1 { + gap: 24px; + padding: 20px; + border-radius: 20px; + background: #F9F9F9; + width: 100%; +} + +.c3 { + gap: 8px; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + min-width: 180px; +} + +.c7 { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.c8 { + font-weight: 485; + font-size: 18px; + line-height: 24px; + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; +} + +.c9 { + height: 8px; + width: 40.698463777008904%; + background: #0066d9; + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; + border-right: 1px solid #F9F9F9; +} + +.c10 { + height: 8px; + width: 59.3015362229911%; + background: #FC72FF; + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; + border-left: 1px solid #F9F9F9; +} + +.c11 { + gap: 4px; + width: 100%; + -webkit-align-items: flex-end; + -webkit-box-align: flex-end; + -ms-flex-align: flex-end; + align-items: flex-end; +} + +.c12 { + color: #222222; + font-size: 36px; + font-weight: 485; + line-height: 44px; +} + +@media (max-width:1023px) { + .c2 { + width: 100%; + } +} + +@media (max-width:1023px) { + .c1 { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + background: #FFFFFF; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding: 20px 0px; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + } +} + +@media (max-width:640px) { + .c3 { + min-width: 150px; + } +} + +@media (max-width:1023px) { + .c7 { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + } +} + +@media (max-width:1023px) { + .c8 { + font-size: 20px; + line-height: 28px; + width: 100%; + } +} + +@media (max-width:1023px) { + .c11 { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + gap: 0px; + -webkit-align-items: flex-start; + -webkit-box-align: flex-start; + -ms-flex-align: flex-start; + align-items: flex-start; + } +} + +@media (max-width:1023px) { + .c12 { + font-size: 20px; + line-height: 28px; + } +} + +
+
+ Stats +
+
+
+ Pool balances +
+
+
+ 90.93M USDC +
+
+ 82,526.49 WETH +
+
+
+
+
+
+
+
+
+ TVL +
+
+
+ $223.2M +
+
+ + + +
+ 0.37% +
+
+
+
+
+
+ 24H volume +
+
+
+ $233.4M +
+
+ + + +
+ 17.75% +
+
+
+
+
+
+ 24H fees +
+
+
+ $116.7K +
+
+
+
+ +`; diff --git a/src/pages/PoolDetails/__snapshots__/PoolDetailsStatsButtons.test.tsx.snap b/src/pages/PoolDetails/__snapshots__/PoolDetailsStatsButtons.test.tsx.snap new file mode 100644 index 00000000..a6f743b4 --- /dev/null +++ b/src/pages/PoolDetails/__snapshots__/PoolDetailsStatsButtons.test.tsx.snap @@ -0,0 +1,211 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PoolDetailsStatsButton loading skeleton shown correctly 1`] = ` + + .c0 { + box-sizing: border-box; + margin: 0; + min-width: 0; +} + +.c1 { + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c3 { + border-radius: 12px; + border-radius: 12px; + height: 24px; + width: 50%; + width: 50%; + -webkit-animation: fAQEyV 1.5s infinite; + animation: fAQEyV 1.5s infinite; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + background: linear-gradient( to left, #22222212 25%, rgba(53,53,53,0.07) 50%, #22222212 75% ); + will-change: background-position; + background-size: 400%; +} + +.c2 { + gap: 12px; +} + +.c4 { + height: 44px; + width: 175px; + border-radius: 900px; +} + +@media (max-width:1023px) { + .c2 { + display: none; + } +} + +
+
+
+
+ +`; + +exports[`PoolDetailsStatsButton renders both buttons correctly 1`] = ` + + .c0 { + box-sizing: border-box; + margin: 0; + min-width: 0; +} + +.c1 { + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c6 { + background-color: transparent; + bottom: 0; + border-radius: inherit; + height: 100%; + left: 0; + position: absolute; + right: 0; + top: 0; + -webkit-transition: 150ms ease background-color; + transition: 150ms ease background-color; + width: 100%; +} + +.c3 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + background-color: #FFEFFF; + border-radius: 16px; + border: 0; + color: #FC72FF; + cursor: pointer; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + font-size: 16px; + font-weight: 535; + gap: 12px; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + line-height: 20px; + padding: 10px 12px; + position: relative; + -webkit-transition: 150ms ease opacity; + transition: 150ms ease opacity; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.c3:active .c5 { + background-color: #B8C0DC3d; +} + +.c3:focus .c5 { + background-color: #B8C0DC3d; +} + +.c3:hover .c5 { + background-color: #98A1C014; +} + +.c3:disabled { + cursor: default; + opacity: 0.6; +} + +.c3:disabled:active .c5, +.c3:disabled:focus .c5, +.c3:disabled:hover .c5 { + background-color: transparent; +} + +.c2 { + gap: 12px; +} + +.c4 { + padding: 12px 16px 12px 12px; + border-radius: 900px; + width: 50%; +} + +@media (max-width:1023px) { + .c2 { + display: none; + } +} + +
+ + +
+
+`; diff --git a/src/pages/PoolDetails/__snapshots__/index.test.tsx.snap b/src/pages/PoolDetails/__snapshots__/index.test.tsx.snap new file mode 100644 index 00000000..b4e98fb6 --- /dev/null +++ b/src/pages/PoolDetails/__snapshots__/index.test.tsx.snap @@ -0,0 +1,2007 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PoolDetailsPage pool header is displayed when data is received from thegraph 1`] = ` + + .c0 { + box-sizing: border-box; + margin: 0; + min-width: 0; +} + +.c11 { + box-sizing: border-box; + margin: 0; + min-width: 0; + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; +} + +.c52 { + box-sizing: border-box; + margin: 0; + min-width: 0; + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + padding: 4px 0px; +} + +.c1 { + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c10 { + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + gap: 18px; +} + +.c12 { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + gap: 8px; +} + +.c53 { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + padding: 4px 0px; +} + +.c8 { + color: #7D7D7D; +} + +.c9 { + color: #222222; +} + +.c64 { + -webkit-text-decoration: none; + text-decoration: none; + cursor: pointer; + -webkit-transition-duration: 125ms; + transition-duration: 125ms; + color: #FC72FF; + stroke: #FC72FF; + font-weight: 500; +} + +.c64:hover { + opacity: 0.6; +} + +.c64:active { + opacity: 0.4; +} + +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c5 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; + gap: 8px; +} + +.c19 { + border-radius: 12px; + border-radius: 12px; + height: 24px; + width: 50%; + width: 50%; + -webkit-animation: fAQEyV 1.5s infinite; + animation: fAQEyV 1.5s infinite; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + background: linear-gradient( to left, #22222212 25%, rgba(53,53,53,0.07) 50%, #22222212 75% ); + will-change: background-position; + background-size: 400%; +} + +.c42 { + background-color: transparent; + bottom: 0; + border-radius: inherit; + height: 100%; + left: 0; + position: absolute; + right: 0; + top: 0; + -webkit-transition: 150ms ease background-color; + transition: 150ms ease background-color; + width: 100%; +} + +.c39 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + background-color: #FFEFFF; + border-radius: 16px; + border: 0; + color: #FC72FF; + cursor: pointer; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + font-size: 16px; + font-weight: 535; + gap: 12px; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + line-height: 20px; + padding: 10px 12px; + position: relative; + -webkit-transition: 150ms ease opacity; + transition: 150ms ease opacity; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.c39:active .c41 { + background-color: #B8C0DC3d; +} + +.c39:focus .c41 { + background-color: #B8C0DC3d; +} + +.c39:hover .c41 { + background-color: #98A1C014; +} + +.c39:disabled { + cursor: default; + opacity: 0.6; +} + +.c39:disabled:active .c41, +.c39:disabled:focus .c41, +.c39:disabled:hover .c41 { + background-color: transparent; +} + +.c54 { + color: #FF5F52; +} + +.c61 { + opacity: 0; + -webkit-transition: opacity 250ms ease-in; + transition: opacity 250ms ease-in; + width: 20px; + height: 20px; + border-radius: 50%; +} + +.c60 { + width: 20px; + height: 20px; + background: #22222212; + -webkit-transition: background-color 250ms ease-in; + transition: background-color 250ms ease-in; + box-shadow: 0 0 1px white; + border-radius: 50%; +} + +.c59 { + position: relative; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} + +.c67 { + color: #CECECE; + font-weight: 485; + font-size: 16px; +} + +.c18 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + height: 436px; + margin-bottom: 24px; + -webkit-align-items: flex-start; + -webkit-box-align: flex-start; + -ms-flex-align: flex-start; + align-items: flex-start; + width: 100%; +} + +.c24 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: flex-end; + -webkit-box-align: flex-end; + -ms-flex-align: flex-end; + align-items: flex-end; + height: 100%; + margin-bottom: 44px; + padding-bottom: 66px; + overflow: hidden; +} + +.c20 { + height: 16px; + width: 180px; +} + +.c21 { + height: 32px; + border-radius: 8px; +} + +.c22 { + margin-top: 4px; + height: 40px; +} + +.c25 { + -webkit-animation: wave 8s cubic-bezier(0.36,0.45,0.63,0.53) infinite; + animation: wave 8s cubic-bezier(0.36,0.45,0.63,0.53) infinite; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + overflow: hidden; + margin-top: 90px; +} + +.c23 { + height: 6px; +} + +.c57 { + gap: 12px; + width: 100%; +} + +.c58 { + gap: 8px; + width: 100%; +} + +.c62 { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.c63 { + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; +} + +.c65 { + gap: 8px; + padding: 8px 12px; + border-radius: 20px; + color: #FC72FF; + background-color: #FC72FF1f; + font-size: 14px; + font-weight: 535; + line-height: 16px; + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + -webkit-text-decoration: none; + text-decoration: none; + cursor: pointer; + -webkit-transition-duration: 125ms; + transition-duration: 125ms; +} + +.c65:hover { + opacity: 0.6; +} + +.c65:active { + opacity: 0.4; +} + +.c66 { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; + line-height: 24px; + white-space: pre-wrap; +} + +.c33 { + height: 16px; + width: 80px; +} + +.c36 { + height: 20px; + width: 20px; + border-radius: 100px; +} + +.c6 { + gap: 36px; +} + +.c7 { + -webkit-text-decoration: none; + text-decoration: none; + -webkit-text-decoration: none; + text-decoration: none; + cursor: pointer; + -webkit-transition-duration: 125ms; + transition-duration: 125ms; +} + +.c7:hover { + opacity: 0.6; +} + +.c7:active { + opacity: 0.4; +} + +.c16 { + background: #F9F9F9; + padding: 2px 6px; + border-radius: 4px; +} + +.c17 { + -webkit-text-decoration: none; + text-decoration: none; + cursor: pointer; + -webkit-transition-duration: 125ms; + transition-duration: 125ms; +} + +.c17:hover { + opacity: 0.6; +} + +.c17:active { + opacity: 0.4; +} + +.c13 { + position: relative; + top: 0; + left: 0; +} + +.c14 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + gap: 2px; + position: relative; + top: 0; + left: 0; +} + +.c14 img { + width: 16px; + height: 32px; + object-fit: cover; +} + +.c14 img:first-child { + border-radius: 16px 0 0 16px; + object-position: 0 0; +} + +.c14 img:last-child { + border-radius: 0 16px 16px 0; + object-position: 100% 0; +} + +.c15 { + width: 32px; + height: 32px; + border-radius: 50%; +} + +.c44 { + font-weight: 485; + font-size: 24px; + line-height: 36px; +} + +.c43 { + gap: 24px; + padding: 20px; + border-radius: 20px; + background: #F9F9F9; + width: 100%; +} + +.c45 { + gap: 8px; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + min-width: 180px; +} + +.c46 { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.c47 { + font-weight: 485; + font-size: 18px; + line-height: 24px; + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; +} + +.c48 { + height: 8px; + width: 40.698463777008904%; + background: #FC72FF; + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; + border-right: 1px solid #F9F9F9; +} + +.c49 { + height: 8px; + width: 59.3015362229911%; + background: #4C82FB; + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; + border-left: 1px solid #F9F9F9; +} + +.c50 { + gap: 4px; + width: 100%; + -webkit-align-items: flex-end; + -webkit-box-align: flex-end; + -ms-flex-align: flex-end; + align-items: flex-end; +} + +.c51 { + color: #222222; + font-size: 36px; + font-weight: 485; + line-height: 44px; +} + +.c38 { + gap: 12px; +} + +.c40 { + padding: 12px 16px 12px 12px; + border-radius: 900px; + width: 50%; +} + +.c28 { + gap: 24px; + border-radius: 20px; + border: 1px solid #22222212; + padding-bottom: 12px; + overflow-y: hidden; + -webkit-scrollbar-width: thin; + -moz-scrollbar-width: thin; + -ms-scrollbar-width: thin; + scrollbar-width: thin; + -webkit-scrollbar-color: #22222212 transparent; + -moz-scrollbar-color: #22222212 transparent; + -ms-scrollbar-color: #22222212 transparent; + scrollbar-color: #22222212 transparent; + height: 100%; +} + +.c28::-webkit-scrollbar { + background: transparent; + height: 4px; + overflow-x: scroll; +} + +.c28::-webkit-scrollbar-thumb { + background: #22222212; + border-radius: 8px; +} + +.c29 { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + border-bottom: 1px solid #22222212; + padding: 12px; + min-width: -webkit-max-content; + min-width: -moz-max-content; + min-width: max-content; +} + +.c35 { + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + border-bottom: none; + padding: 12px; + min-width: -webkit-max-content; + min-width: -moz-max-content; + min-width: max-content; +} + +.c30 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0px 8px; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + width: auto; + min-width: 136px !important; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c31 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0px 8px; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + width: auto; + min-width: 121px !important; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c32 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0px 8px; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + width: auto; + min-width: 121px !important; + -webkit-box-pack: end; + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end; +} + +.c34 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0px 8px; + -webkit-flex: unset; + -ms-flex: unset; + flex: unset; + width: 44px; + min-width: unset !important; + -webkit-box-pack: end; + -webkit-justify-content: flex-end; + -ms-flex-pack: end; + justify-content: flex-end; +} + +.c2 { + padding: 48px; + width: 100%; + -webkit-align-items: flex-start; + -webkit-box-align: flex-start; + -ms-flex-align: flex-start; + align-items: flex-start; + gap: 60px; +} + +.c4 { + gap: 24px; + width: 65vw; + overflow: hidden; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c26 { + border: 0.5px solid #22222212; + margin: 16px 0px; + width: 100%; +} + +.c27 { + width: 180px; + height: 32px; +} + +.c37 { + gap: 24px; + margin: 0 48px 0 auto; + width: 22vw; + min-width: 360px; +} + +.c55 { + gap: 24px; + padding: 20px; +} + +.c56 { + width: 100%; + font-size: 24px; + font-weight: 485; + line-height: 32px; +} + +@media (max-width:1023px) and (min-width:640px) { + .c57 { + max-width: 45%; + } +} + +@media (max-width:1023px) { + .c44 { + width: 100%; + } +} + +@media (max-width:1023px) { + .c43 { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + background: #FFFFFF; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding: 20px 0px; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + } +} + +@media (max-width:640px) { + .c45 { + min-width: 150px; + } +} + +@media (max-width:1023px) { + .c46 { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + } +} + +@media (max-width:1023px) { + .c47 { + font-size: 20px; + line-height: 28px; + width: 100%; + } +} + +@media (max-width:1023px) { + .c50 { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + gap: 0px; + -webkit-align-items: flex-start; + -webkit-box-align: flex-start; + -ms-flex-align: flex-start; + align-items: flex-start; + } +} + +@media (max-width:1023px) { + .c51 { + font-size: 20px; + line-height: 28px; + } +} + +@media (max-width:1023px) { + .c38 { + display: none; + } +} + +@media (max-width:1023px) { + .c2 { + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + gap: unset; + } +} + +@media (max-width:639px) { + .c2 { + padding: 48px 16px; + } +} + +@media (max-width:1023px) { + .c4 { + width: 100%; + } +} + +@media (max-width:1023px) { + .c37 { + margin: 44px 0px; + width: 100%; + min-width: unset; + } +} + +@media (max-width:1023px) and (min-width:640px) { + .c55 { + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding: unset; + } +} + +@media (max-width:639px) { + .c55 { + padding: unset; + } +} + +
+
+
+
+
+ +
+ Explore +
+
+
+  >  +
+ +
+ Pool +
+
+
+  >  +
+
+ USDC / WETH (0x88e6...5640) +
+
+
+
+
+
+ + +
+
+
+ USDC / WETH +
+
+
+ 0.05% +
+ + + +
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + Time +
+
+
+ Type +
+
+ USD +
+
+
+
+
+
+
+
+ Maker +
+
+ Txn +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+ Stats +
+
+
+ Pool balances +
+
+
+ 90.93M USDC +
+
+ 82,526.49 WETH +
+
+
+
+
+
+
+
+
+ TVL +
+
+
+ $223.2M +
+
+ + + +
+ 0.37% +
+
+
+
+
+
+ 24H volume +
+
+
+ $233.4M +
+
+ + + +
+ 17.75% +
+
+
+
+
+
+ 24H fees +
+
+
+ $116.7K +
+
+
+
+
+
+ Info +
+
+
+
+
+ UNKNOWN logo +
+
+
+ Unknown Token +
+
+ UNKNOWN +
+
+ +
+ + No token information available + +
+
+
+
+
+
+ WETH logo +
+
+
+ Wrapped Ether +
+
+ WETH +
+
+ +
+ + No token information available + +
+
+
+
+
+ +`; diff --git a/src/pages/PoolDetails/icons.tsx b/src/pages/PoolDetails/icons.tsx new file mode 100644 index 00000000..67bab2e1 --- /dev/null +++ b/src/pages/PoolDetails/icons.tsx @@ -0,0 +1,15 @@ +type SVGProps = React.SVGProps & { + fill?: string + height?: string | number + width?: string | number + gradientId?: string +} + +export const ReversedArrowsIcon = (props: SVGProps) => ( + + + +); diff --git a/src/pages/PoolDetails/index.tsx b/src/pages/PoolDetails/index.tsx index f7f08bab..f1976bae 100644 --- a/src/pages/PoolDetails/index.tsx +++ b/src/pages/PoolDetails/index.tsx @@ -1,256 +1,170 @@ -import styled, { css, useTheme } from 'styled-components' -import { useEffect, useMemo, useState } from 'react' -import { Link, useParams } from 'react-router-dom' -import { validateAndParseAddress } from 'starknet' +import { Trans } from '@lingui/macro' +import { useReducer } from 'react' +import { useParams } from 'react-router-dom' +import { Text } from 'rebass' +import styled from 'styled-components' -import { AutoColumn } from "components/Column"; -import Row, { AutoRow, RowBetween, RowFixed } from "components/Row"; -import { formattedNum, formattedPercent } from "utils/formatNum"; -import { getAllPools } from 'api/PoolsData'; -import { useDefaultActiveTokens } from 'hooks/Tokens' -import { useAccountDetails } from 'hooks/starknet-react' -import { ETH_ADDRESS, WETH } from 'constants/tokens' -import DoubleTokenLogo from '../../components/DoubleLogo' -import FeeBadge from 'components/FeeBadge' -import { ButtonGray, ButtonPrimary, ButtonText } from 'components/Button' -import CurrencyLogo from 'components/Logo/CurrencyLogo' -import { PositionDetails } from '../Pool/PositionDetails' -import { useToggleAccountDrawer } from 'components/AccountDrawer' -import { getClient } from 'apollo/client' -import { ChainId } from '@vnaysn/jediswap-sdk-core' -import { useTokenIds } from 'hooks/useV3Positions' -import { PageWrapper, PanelWrapper, PanelTopLight, ResponsiveButtonPrimary } from 'pages/Pool/styled' +import NotFound from 'pages/NotFound' +import Column from 'components/Column' +import Row from 'components/Row' +import { LoadingBubble } from 'components/Tokens/loading' +import { LoadingChart } from 'components/Tokens/TokenDetails/Skeleton' +import { TokenDescription } from 'components/Tokens/TokenDetails/TokenDescription' +import { getValidUrlChainName, supportedChainIdFromGQLChain } from 'graphql/data/util' +import { usePoolData } from 'graphql/thegraph/PoolData' +import { BREAKPOINTS } from 'theme' +import { isAddressValidForStarknet } from 'utils/addresses' +import { PoolDetailsHeader } from './PoolDetailsHeader' +import { PoolDetailsStats } from './PoolDetailsStats' +import { PoolDetailsStatsButtons } from './PoolDetailsStatsButtons' +import { PoolDetailsTableSkeleton } from './PoolDetailsTableSkeleton' +import { DetailBubble, SmallDetailBubble } from './shared' -const ResponsiveButtonTabs = styled(ButtonPrimary) <{ secondary: boolean; active: boolean }>` - font-family: 'DM Sans'; - border-radius: 4px; - font-size: 16px; - padding: 6px 12px; - background: ${({ theme, active }) => (!active ? 'transparent' : theme.jediWhite)}; - box-shadow: 0px 3.079px 13.856px 0px rgba(154, 146, 210, 0.3) inset, - 0px 0.77px 30.791px 0px rgba(227, 222, 255, 0.2) inset; - color: ${({ theme, active }) => (!active ? theme.jediWhite : theme.jediPink)}; - // width: 121px; - margin-left: 0; - height: 26px; - &:hover { - background: ${({ theme, active }) => (!active ? 'transparent' : theme.jediWhite)}; - color: ${({ theme, active }) => (!active ? theme.jediWhite : theme.jediPink)}; - } - &:active { - background: ${({ theme, active }) => (!active ? 'transparent' : theme.jediWhite)}; - color: ${({ theme, active }) => (!active ? theme.jediWhite : theme.jediPink)}; - } - &:focus { - background: ${({ theme, active }) => (!active ? 'transparent' : theme.jediWhite)}; - color: ${({ theme, active }) => (!active ? theme.jediWhite : theme.jediPink)}; +const PageWrapper = styled(Row)` + padding: 48px; + width: 100%; + align-items: flex-start; + gap: 60px; + + @media (max-width: ${BREAKPOINTS.lg - 1}px) { + flex-direction: column; + gap: unset; } - @media (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) { - width: 50%; - margin-bottom: 10px; + + @media (max-width: ${BREAKPOINTS.sm - 1}px) { + padding: 48px 16px; } ` -const FixedPanel = styled.div` - width: fit-content; - padding: 12px 20px; - border-radius: 4px; - background: rgba(255, 255, 255, 0.1); - cursor: pointer; + +const LeftColumn = styled(Column)` + gap: 24px; + width: 65vw; + overflow: hidden; + justify-content: flex-start; + + @media (max-width: ${BREAKPOINTS.lg - 1}px) { + width: 100%; + } ` -export default function PoolDetails() { - const { poolId } = useParams<{ poolId?: string }>() - const [poolData, setpoolData] = useState({}) - const { address, chainId } = useAccountDetails() +const HR = styled.hr` + border: 0.5px solid ${({ theme }) => theme.surface3}; + margin: 16px 0px; + width: 100%; +` - const { tokenIds, loading: loadingPositions } = useTokenIds(address, chainId); +const ChartHeaderBubble = styled(LoadingBubble)` + width: 180px; + height: 32px; +` - const toggleWalletDrawer = useToggleAccountDrawer() - const showConnectAWallet = Boolean(!address) +const LinkColumn = styled(Column)` + gap: 16px; + padding: 20px; +` - const chainIdFinal = chainId || ChainId.MAINNET - const allTokens = useDefaultActiveTokens(chainIdFinal) - const whitelistedIds = Object.keys(allTokens) - const graphqlClient = getClient(chainIdFinal) +const RightColumn = styled(Column)` + gap: 24px; + margin: 0 48px 0 auto; + width: 22vw; + min-width: 360px; - //fetch pools data - useEffect(() => { - let ignore = false; - const getPoolsData = async () => { - if (whitelistedIds.length === 0) { - return - } - const poolsDataRaw: any = await getAllPools(graphqlClient, [...whitelistedIds, ETH_ADDRESS]) //add ETH token - if (poolId && poolsDataRaw) { - const poolData: any = poolsDataRaw.find((data: any) => data?.poolAddress === poolId) - if (!ignore) { - if (poolData) { - setpoolData(poolData) - } - } - } - } + @media (max-width: ${BREAKPOINTS.lg - 1}px) { + margin: 44px 0px; + width: 100%; + min-width: unset; + } +` - getPoolsData() - return () => { - ignore = true - } - }, [Object.keys(allTokens).join(','), chainIdFinal]) +const TokenDetailsWrapper = styled(Column)` + gap: 24px; + padding: 20px; + @media (max-width: ${BREAKPOINTS.lg - 1}px) and (min-width: ${BREAKPOINTS.sm}px) { + flex-direction: row; + flex-wrap: wrap; + padding: unset; + } - const { - poolAddress, - token0, - token1, - fee, - totalValueLockedUSD, - liquidityChangeUSD, - oneDayVolumeUSD, - volumeChangeUSD, - oneDayFeesUSD, - feesChangeUSD, - totalValueLockedToken0, - totalValueLockedToken1, - token0Price, - token1Price, - loadingEnd, - } = poolData - let doubleCurrencyImageData = undefined - if (poolData && poolData.token0 && poolData.token1 && chainIdFinal) { - doubleCurrencyImageData = { - token0: - poolData.token0.symbol === 'ETH' - ? WETH[chainIdFinal] - : allTokens[validateAndParseAddress(poolData.token0.tokenAddress)], - token1: - poolData.token1.symbol === 'ETH' - ? WETH[chainIdFinal] - : allTokens[validateAndParseAddress(poolData.token1.tokenAddress)], - } + @media (max-width: ${BREAKPOINTS.sm - 1}px) { + padding: unset; } - const feePercent = (fee ? parseFloat(fee) / 10000 : 0) + '%' - const [currentPriceDisplayMode, setCurrentPriceDisplayMode] = useState('token0') - const formattedSymbol0 = token0?.symbol.length > 6 ? token0?.symbol.slice(0, 5) + '...' : token0?.symbol - const formattedSymbol1 = token1?.symbol.length > 6 ? token1?.symbol.slice(0, 5) + '...' : token1?.symbol +` +const TokenDetailsHeader = styled(Text)` + width: 100%; + font-size: 24px; + font-weight: 485; + line-height: 32px; +` + +export default function PoolDetailsPage() { + const { poolAddress, chainName } = useParams<{ + poolAddress: string + chainName: string + }>() + const chain = getValidUrlChainName(chainName) + const chainId = chain && supportedChainIdFromGQLChain(chain) + const { data: poolData, loading } = usePoolData(poolAddress ?? '', chainId) + const [isReversed, toggleReversed] = useReducer((x) => !x, false) + const token0 = isReversed ? poolData?.token1 : poolData?.token0 + const token1 = isReversed ? poolData?.token0 : poolData?.token1 + const isInvalidPool = + !chainName || !poolAddress || !getValidUrlChainName(chainName) || !isAddressValidForStarknet(poolAddress) + const poolNotFound = (!loading && !poolData) || isInvalidPool + + if (poolNotFound) { + return + } return ( - - ← Back to top pools - - - {doubleCurrencyImageData && ( - + + - )} - {poolData?.token0?.symbol} - {poolData?.token1?.symbol} - {feePercent} - - + New position - - - -
- {/* */} - Current Price: - {/* */} -
- {currentPriceDisplayMode === 'token0' && ( - - - {token0 && token1 ? `1 ${formattedSymbol0} = ${formattedNum(token1Price)} ${formattedSymbol1}` : '-'} - - )} - {currentPriceDisplayMode === 'token1' && ( - - - {token0 && token1 ? `1 ${formattedSymbol1} = ${formattedNum(token0Price)} ${formattedSymbol0}` : '-'} - - )} -
-
- setCurrentPriceDisplayMode('token0')} style={{fontSize: '0.875rem', borderRadius: "4px 0px 0px 4px"}}> - {poolData?.token0?.symbol} - - setCurrentPriceDisplayMode('token1')} style={{fontSize: '0.875rem', borderRadius: "0px 4px 4px 0px"}}> - {poolData?.token1?.symbol} - -
-
-
- - - - - - - Total Liquidity - - -
- {formattedNum(totalValueLockedUSD, true)} -
-
- {formattedPercent(liquidityChangeUSD)} -
-
-
-
- - - - Volume (24hr) -
- - -
- {formattedNum(oneDayVolumeUSD, true)} -
-
- {formattedPercent(volumeChangeUSD)} -
-
- - - - - - Total fees (24hr) - - -
- {formattedNum(oneDayFeesUSD, true)} -
-
- {formattedPercent(feesChangeUSD)} -
-
-
-
- - - {loadingPositions ? ( -
- ) : ( - + +
+ + + + + - ) - } - + + {(token0 || token1 || loading) && + (loading ? ( + + + {Array.from({ length: 3 }).map((_, i) => ( + + + + + ))} + + ) : ( + + + Info + + {token0 && } + {token1 && } + + ))} + + ) -} \ No newline at end of file +} diff --git a/src/pages/PoolDetails/shared.ts b/src/pages/PoolDetails/shared.ts new file mode 100644 index 00000000..6a744c53 --- /dev/null +++ b/src/pages/PoolDetails/shared.ts @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +import { LoadingBubble } from 'components/Tokens/loading'; + +export const DetailBubble = styled(LoadingBubble)<{ $height?: number; $width?: number }>` + height: ${({ $height }) => ($height ? `${$height}px` : '16px')}; + width: ${({ $width }) => ($width ? `${$width}px` : '80px')}; +`; + +export const SmallDetailBubble = styled(LoadingBubble)` + height: 20px; + width: 20px; + border-radius: 100px; +`; diff --git a/src/pages/RouteDefinitions.tsx b/src/pages/RouteDefinitions.tsx index 044ec951..4e14f649 100644 --- a/src/pages/RouteDefinitions.tsx +++ b/src/pages/RouteDefinitions.tsx @@ -2,7 +2,6 @@ import { lazy, ReactNode, useMemo } from 'react'; import { useLocation } from 'react-router-dom'; import { isBrowserRouterEnabled } from 'utils/env'; -import PoolDetails from './PoolDetails'; import Swap from './Swap'; import { RedirectPathToSwapOnly, RedirectToSwap } from './Swap/redirects'; @@ -80,7 +79,6 @@ export const routes: RouteDefinition[] = [ getElement: () => , }), createRouteDefinition({ path: '/remove/:tokenId', getElement: () => }), - createRouteDefinition({ path: '/explore/pools/:poolId', getElement: () => }), // @ts-ignore createRouteDefinition({ path: '*', getElement: () => }), ]; diff --git a/src/theme/colors.ts b/src/theme/colors.ts index c2ea6398..df80a3cd 100644 --- a/src/theme/colors.ts +++ b/src/theme/colors.ts @@ -187,9 +187,6 @@ const commonTheme = { signalGreen, jediGreyBorder, signalRed, - text1: 'FAFAFA', - advancedBG: 'rgba(0,0,0,0.1)', - bg5: '#565A69' } export const darkTheme = { diff --git a/src/utils/formatNum.js b/src/utils/formatNum.js deleted file mode 100644 index fe28fc13..00000000 --- a/src/utils/formatNum.js +++ /dev/null @@ -1,123 +0,0 @@ -import { Text } from 'rebass' -import Numeral from 'numeral' - -export const getPercentChange = (valueNow, value24HoursAgo) => { - const adjustedPercentChange = ((parseFloat(valueNow) - parseFloat(value24HoursAgo)) / parseFloat(value24HoursAgo)) * 100 - if (isNaN(adjustedPercentChange) || !isFinite(adjustedPercentChange)) { - return 0 - } - return adjustedPercentChange -} - -export const get2DayPercentChange = (oneDayData, twoDaysData) => { - // get volume info for both 24 hour periods - let yesterdayData = twoDaysData - oneDayData - - const adjustedPercentChange = ((oneDayData - yesterdayData) / (yesterdayData)) * 100 - - if (isNaN(adjustedPercentChange) || !isFinite(adjustedPercentChange)) { - return 0 - } - return adjustedPercentChange -} - -export function formattedPercent(percent, useAbs = false, color = '', fontWeight=500) { - const green = '#21E70F' - const red = '#FC4D4D' - percent = parseFloat(percent) - if (!percent || percent === 0) { - return 0% - } - - if (percent < 0.0001 && percent > 0) { - return ( - - {'< 0.0001%'} - - ) - } - - if (percent < 0 && percent > -0.0001) { - return ( - - {'< 0.0001%'} - - ) - } - - if (percent > 999999) { - return ( - - {'> 999999%'} - - ) - } - - let fixedPercent = percent.toFixed(2) - if (fixedPercent === '0.00') { - return 0% - } - if (fixedPercent > 0) { - if (fixedPercent > 100) { - return {`${useAbs ? '' : '+'}${percent?.toFixed(0).toLocaleString()}%`} - } else { - return {`${useAbs ? '' : '+'}${fixedPercent}%`} - } - } else { - return {`${fixedPercent}%`} - } -} - -export const formatDollarAmount = (num, digits) => { - const formatter = new Intl.NumberFormat([], { - style: 'currency', - currency: 'USD', - minimumFractionDigits: digits, - maximumFractionDigits: digits, - }) - return formatter.format(num) -} - -export const toK = (num, maxSigns = 2) => { - return Numeral(num).format( - `0.[${Array.from({ length: maxSigns }) - .map(() => 0) - .join('')}]a` - ) -} - -export const formattedNum = (number, usd = false) => { - if (isNaN(number) || number === '' || number === undefined) { - return usd ? '$0' : 0 - } - let num = parseFloat(number) - - if (num > 500000000) { - return (usd ? '$' : '') + toK(num.toFixed(0), true) - } - - if (num === 0) { - if (usd) { - return '$0' - } - return 0 - } - - if (num < 0.0001 && num > 0) { - return usd ? '< $0.0001' : '< 0.0001' - } - - if (num > 1000) { - return usd ? formatDollarAmount(num, 0) : Number(parseFloat(num).toFixed(0)).toLocaleString() - } - - if (usd) { - if (num < 1) { - return formatDollarAmount(num, 4) - } else { - return formatDollarAmount(num, 2) - } - } - - return Number(parseFloat(num).toFixed(4)).toString() -} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index ec999c26..e648276d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1368,13 +1368,6 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/runtime@^7.1.2": - version "7.24.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.0.tgz#584c450063ffda59697021430cb47101b085951e" - integrity sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw== - dependencies: - regenerator-runtime "^0.14.0" - "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.22.5", "@babel/template@^7.3.3": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" @@ -2433,26 +2426,6 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@floating-ui/core@^1.0.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.0.tgz#fa41b87812a16bf123122bf945946bae3fdf7fc1" - integrity sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g== - dependencies: - "@floating-ui/utils" "^0.2.1" - -"@floating-ui/dom@^1.6.1": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.3.tgz#954e46c1dd3ad48e49db9ada7218b0985cee75ef" - integrity sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw== - dependencies: - "@floating-ui/core" "^1.0.0" - "@floating-ui/utils" "^0.2.0" - -"@floating-ui/utils@^0.2.0", "@floating-ui/utils@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2" - integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q== - "@fontsource/ibm-plex-mono@^4.5.1": version "4.5.1" resolved "https://registry.yarnpkg.com/@fontsource/ibm-plex-mono/-/ibm-plex-mono-4.5.1.tgz#f09822dc9d143710687ff8bd05248a86cc2dc4d1" @@ -3606,11 +3579,6 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/sourcemap-codec@^1.4.15": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== - "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -5952,11 +5920,6 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" -"@types/js-cookie@^2.2.6": - version "2.2.7" - resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.7.tgz#226a9e31680835a6188e887f3988e60c04d3f6a3" - integrity sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA== - "@types/js-yaml@^4.0.0": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138" @@ -7820,11 +7783,6 @@ dependencies: tslib "^2.3.0" -"@xobotyi/scrollbar-width@^1.9.5": - version "1.9.5" - resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" - integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== - "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -9531,11 +9489,6 @@ classnames@^2.2.5, classnames@^2.3.1: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== -classnames@^2.3.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" - integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== - clean-css@^5.2.2: version "5.3.2" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.2.tgz#70ecc7d4d4114921f5d298349ff86a31a9975224" @@ -9959,13 +9912,6 @@ copy-to-clipboard@^3.2.0: dependencies: toggle-selection "^1.0.6" -copy-to-clipboard@^3.3.1: - version "3.3.3" - resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" - integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== - dependencies: - toggle-selection "^1.0.6" - core-js-compat@^3.25.1, core-js-compat@^3.31.0: version "3.31.1" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.1.tgz#5084ad1a46858df50ff89ace152441a63ba7aae0" @@ -10131,13 +10077,6 @@ css-has-pseudo@^3.0.4: dependencies: postcss-selector-parser "^6.0.9" -css-in-js-utils@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz#640ae6a33646d401fc720c54fc61c42cd76ae2bb" - integrity sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A== - dependencies: - hyphenate-style-name "^1.0.3" - css-loader@^6.5.1: version "6.7.3" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.3.tgz#1e8799f3ccc5874fdd55461af51137fcc5befbcd" @@ -10342,11 +10281,6 @@ csstype@^3.0.2, csstype@^3.0.7: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== -csstype@^3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" - integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== - cypress-hardhat@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/cypress-hardhat/-/cypress-hardhat-2.5.0.tgz#db00ad115d170a2d33ce8d92a54a9a339e695a40" @@ -12438,11 +12372,6 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fast-loops@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fast-loops/-/fast-loops-1.1.3.tgz#ce96adb86d07e7bf9b4822ab9c6fac9964981f75" - integrity sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g== - fast-querystring@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/fast-querystring/-/fast-querystring-1.1.2.tgz#a6d24937b4fc6f791b4ee31dcb6f53aeafb89f53" @@ -12460,11 +12389,6 @@ fast-safe-stringify@^2.0.6: resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz" integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== -fast-shallow-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" - integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== - fast-stable-stringify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313" @@ -12477,11 +12401,6 @@ fast-url-parser@1.1.3, fast-url-parser@^1.1.3: dependencies: punycode "^1.3.2" -fastest-stable-stringify@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76" - integrity sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q== - fastq@^1.6.0: version "1.12.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.12.0.tgz#ed7b6ab5d62393fb2cc591c853652a5c318bf794" @@ -13681,11 +13600,6 @@ husky@^8.0.3: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== -hyphenate-style-name@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" - integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== - iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" @@ -13821,14 +13735,6 @@ ini@^1.3.5, ini@~1.3.0: resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -inline-style-prefixer@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-7.0.0.tgz#991d550735d42069f528ac1bcdacd378d1305442" - integrity sha512-I7GEdScunP1dQ6IM2mQWh6v0mOYdYmH3Bp31UecKdrcUgcURTcctSe1IECdUznSHKSmsHtjrT3CwCPI1pyxfUQ== - dependencies: - css-in-js-utils "^3.1.0" - fast-loops "^1.1.3" - inquirer@^7.3.3: version "7.3.3" resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz" @@ -15399,11 +15305,6 @@ js-base64@^3.7.2: resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.2.tgz#816d11d81a8aff241603d19ce5761e13e41d7745" integrity sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ== -js-cookie@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" - integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== - js-sdsl@^4.1.4: version "4.4.0" resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" @@ -16603,20 +16504,6 @@ nan@^2.14.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== -nano-css@^5.6.1: - version "5.6.1" - resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.6.1.tgz#964120cb1af6cccaa6d0717a473ccd876b34c197" - integrity sha512-T2Mhc//CepkTa3X4pUhKgbEheJHYAxD0VptuqFhDbGMUWVV2m+lkNiW/Ieuj35wrfC8Zm0l7HvssQh7zcEttSw== - dependencies: - "@jridgewell/sourcemap-codec" "^1.4.15" - css-tree "^1.1.2" - csstype "^3.1.2" - fastest-stable-stringify "^2.0.2" - inline-style-prefixer "^7.0.0" - rtl-css-js "^1.16.1" - stacktrace-js "^2.0.2" - stylis "^4.3.0" - nano-time@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef" @@ -16844,11 +16731,6 @@ numbro@^2.4.0: dependencies: bignumber.js "^8 || ^9" -numeral@2: - version "2.0.6" - resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506" - integrity sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA== - nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" @@ -18816,56 +18698,16 @@ react-style-singleton@^2.1.0: invariant "^2.2.4" tslib "^1.0.0" -react-switch@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/react-switch/-/react-switch-7.0.0.tgz#400990bb9822864938e343ed24f13276a617bdc0" - integrity sha512-KkDeW+cozZXI6knDPyUt3KBN1rmhoVYgAdCJqAh7st7tk8YE6N0iR89zjCWO8T8dUTeJGTR0KU+5CHCRMRffiA== - dependencies: - prop-types "^15.7.2" - react-table@^7.8.0: version "7.8.0" resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.8.0.tgz#07858c01c1718c09f7f1aed7034fcfd7bda907d2" integrity sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA== -react-tooltip@^5.26.3: - version "5.26.3" - resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-5.26.3.tgz#bcb9a53e15bdbf9ae007ddf8bf413a317a637054" - integrity sha512-MpYAws8CEHUd/RC4GaDCdoceph/T4KHM5vS5Dbk8FOmLMvvIht2ymP2htWdrke7K6lqPO8rz8+bnwWUIXeDlzg== - dependencies: - "@floating-ui/dom" "^1.6.1" - classnames "^2.3.0" - -react-universal-interface@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b" - integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw== - react-use-gesture@^6.0.14: version "6.0.14" resolved "https://registry.npmjs.org/react-use-gesture/-/react-use-gesture-6.0.14.tgz" integrity sha512-d9cnZJ0DOFd3FIO76J776DyhtbODgbxGKu19lvc1aSNTnRV5EKr9V4Uda188l2Qh0Va3pqWGxEQlw72r2cmnFQ== -react-use@^17.5.0: - version "17.5.0" - resolved "https://registry.yarnpkg.com/react-use/-/react-use-17.5.0.tgz#1fae45638828a338291efa0f0c61862db7ee6442" - integrity sha512-PbfwSPMwp/hoL847rLnm/qkjg3sTRCvn6YhUZiHaUa3FA6/aNoFX79ul5Xt70O1rK+9GxSVqkY0eTwMdsR/bWg== - dependencies: - "@types/js-cookie" "^2.2.6" - "@xobotyi/scrollbar-width" "^1.9.5" - copy-to-clipboard "^3.3.1" - fast-deep-equal "^3.1.3" - fast-shallow-equal "^1.0.0" - js-cookie "^2.2.1" - nano-css "^5.6.1" - react-universal-interface "^0.6.2" - resize-observer-polyfill "^1.5.1" - screenfull "^5.1.0" - set-harmonic-interval "^1.0.1" - throttle-debounce "^3.0.1" - ts-easing "^0.2.0" - tslib "^2.1.0" - react-virtualized-auto-sizer@^1.0.2: version "1.0.5" resolved "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.5.tgz" @@ -19039,11 +18881,6 @@ regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.3, regenerator-runtime@^ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regenerator-runtime@^0.14.0: - version "0.14.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" - integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== - regenerator-transform@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" @@ -19452,13 +19289,6 @@ rpc-websockets@^7.5.1: bufferutil "^4.0.1" utf-8-validate "^5.0.2" -rtl-css-js@^1.16.1: - version "1.16.1" - resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.16.1.tgz#4b48b4354b0ff917a30488d95100fbf7219a3e80" - integrity sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg== - dependencies: - "@babel/runtime" "^7.1.2" - run-async@^2.4.0: version "2.4.1" resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" @@ -19643,11 +19473,6 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" -screenfull@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba" - integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== - scrypt-js@3.0.1, scrypt-js@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" @@ -19823,11 +19648,6 @@ set-function-name@^2.0.0: functions-have-names "^1.2.3" has-property-descriptors "^1.0.0" -set-harmonic-interval@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" - integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g== - setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" @@ -20073,11 +19893,6 @@ source-map-support@0.5.21, source-map-support@^0.5.13, source-map-support@^0.5.2 buffer-from "^1.0.0" source-map "^0.6.0" -source-map@0.5.6: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" - integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA== - source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -20182,13 +19997,6 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== -stack-generator@^2.0.5: - version "2.0.10" - resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.10.tgz#8ae171e985ed62287d4f1ed55a1633b3fb53bb4d" - integrity sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ== - dependencies: - stackframe "^1.3.4" - stack-utils@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" @@ -20201,28 +20009,6 @@ stackframe@^1.1.1: resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303" integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA== -stackframe@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" - integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== - -stacktrace-gps@^3.0.4: - version "3.1.2" - resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz#0c40b24a9b119b20da4525c398795338966a2fb0" - integrity sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ== - dependencies: - source-map "0.5.6" - stackframe "^1.3.4" - -stacktrace-js@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b" - integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg== - dependencies: - error-stack-parser "^2.0.6" - stack-generator "^2.0.5" - stacktrace-gps "^3.0.4" - stacktrace-parser@^0.1.10: version "0.1.10" resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" @@ -20637,11 +20423,6 @@ stylehacks@^5.1.1: browserslist "^4.21.4" postcss-selector-parser "^6.0.4" -stylis@^4.3.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.1.tgz#ed8a9ebf9f76fe1e12d462f5cc3c4c980b23a7eb" - integrity sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ== - sucrase@^3.32.0: version "3.32.0" resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.32.0.tgz#c4a95e0f1e18b6847127258a75cf360bc568d4a7" @@ -20938,11 +20719,6 @@ throttle-debounce@^2.1.0: resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.3.0.tgz#fd31865e66502071e411817e241465b3e9c372e2" integrity sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ== -throttle-debounce@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb" - integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg== - throttleit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" @@ -21115,11 +20891,6 @@ tryer@^1.0.1: resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== -ts-easing@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" - integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== - ts-essentials@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38"