From 4b75208266ad391b737bbeda9bcef01f489084d0 Mon Sep 17 00:00:00 2001 From: Mati Dastugue Date: Tue, 20 Sep 2022 22:43:21 -0300 Subject: [PATCH 1/8] [Tokens Table] Sort by 24h volume (#213) * Tokens table refactor + Sort by 24h volume * Fix issues * Update src/hooks/useGetTokens.ts Co-authored-by: Leandro Boscariol * Fix timestamp + visual issues * Improve logic + styles * pick the penultimate tokenDailyTotal instead of the latest * set date range in the tooltip * format range tooltip Co-authored-by: Leandro Boscariol Co-authored-by: Ramiro Vazquez --- .../components/TokensTableWidget/index.tsx | 3 +- .../common/TextWithTooltip/index.tsx | 2 +- src/components/token/TokenTable/index.tsx | 59 ++++- src/hooks/useGetTokens.ts | 231 +++++++----------- 4 files changed, 143 insertions(+), 152 deletions(-) diff --git a/src/apps/explorer/components/TokensTableWidget/index.tsx b/src/apps/explorer/components/TokensTableWidget/index.tsx index c73a28cb3..a61cfc54b 100644 --- a/src/apps/explorer/components/TokensTableWidget/index.tsx +++ b/src/apps/explorer/components/TokensTableWidget/index.tsx @@ -101,7 +101,7 @@ export const TokensTableWidget: React.FC = () => { handleNextPage, handlePreviousPage, } = useTable({ initialState: { pageOffset: 0, pageSize: RESULTS_PER_PAGE } }) - const { tokens, isLoading, error } = useGetTokens(networkId, tableState) + const { tokens, isLoading, error } = useGetTokens(networkId) const filteredTokens = useFlexSearch(query, tokens, ['name', 'symbol', 'address']) const resultsLength = query.length ? filteredTokens.length : tokens.length @@ -121,6 +121,7 @@ export const TokensTableWidget: React.FC = () => { const filterData = (): Token[] => { const data = query ? (filteredTokens as Token[]) : tokens + return data .map((token) => ({ ...token, diff --git a/src/apps/explorer/components/common/TextWithTooltip/index.tsx b/src/apps/explorer/components/common/TextWithTooltip/index.tsx index e7715be2c..56c9adc01 100644 --- a/src/apps/explorer/components/common/TextWithTooltip/index.tsx +++ b/src/apps/explorer/components/common/TextWithTooltip/index.tsx @@ -14,7 +14,7 @@ const Wrapper = styled.div` ` interface TextTooltipProps { children: React.ReactNode - textInTooltip: string + textInTooltip: string | React.ReactNode tooltipPlacement?: Placement } diff --git a/src/components/token/TokenTable/index.tsx b/src/components/token/TokenTable/index.tsx index 399f50c8d..1abbbfd23 100644 --- a/src/components/token/TokenTable/index.tsx +++ b/src/components/token/TokenTable/index.tsx @@ -3,6 +3,7 @@ import styled, { DefaultTheme, useTheme } from 'styled-components' import { createChart, IChartApi } from 'lightweight-charts' import BigNumber from 'bignumber.js' import { formatPrice, TokenErc20 } from '@gnosis.pm/dex-js' +import { format, fromUnixTime, startOfToday } from 'date-fns' import { Token } from 'hooks/useGetTokens' import { useNetworkId } from 'state/network' @@ -31,6 +32,25 @@ const Wrapper = styled(StyledUserDetailsTable)` border-bottom: 0.1rem solid ${({ theme }): string => theme.tableRowBorder}; > tr { min-height: 7.4rem; + &.header-row { + display: none; + ${media.mobile} { + display: flex; + background: transparent; + border: none; + padding: 0; + margin: 0; + box-shadow: none; + min-height: 2rem; + td { + padding: 0; + margin: 0; + .mobile-header { + margin: 0; + } + } + } + } } > tr > td:first-child { padding: 0 2rem; @@ -46,7 +66,8 @@ const Wrapper = styled(StyledUserDetailsTable)` :nth-child(5), :nth-child(6), :nth-child(7) { - justify-content: right; + justify-content: center; + text-align: center; } } > tbody > tr > td:nth-child(8), @@ -76,7 +97,7 @@ const Wrapper = styled(StyledUserDetailsTable)` border: 0.1rem solid ${({ theme }): string => theme.tableRowBorder}; box-shadow: 0px 4px 12px ${({ theme }): string => theme.boxShadow}; border-radius: 6px; - margin-top: 16px; + margin-top: 10px; padding: 12px; &:hover { background: none; @@ -164,13 +185,15 @@ const TokenWrapper = styled.div` const HeaderValue = styled.span<{ captionColor?: 'green' | 'red1' | 'grey' }>` color: ${({ theme, captionColor }): string => (captionColor ? theme[captionColor] : theme.textPrimary1)}; - ${media.mobile} { flex-wrap: wrap; text-align: end; } ` +const TooltipWrapper = styled.div` + text-align: center; +` const ChartWrapper = styled.div` position: relative; ${media.mobile} { @@ -254,6 +277,7 @@ const RowToken: React.FC = ({ token, index }) => { lastWeekPricePercentageDifference, lastDayUsdVolume, totalVolumeUsd, + timestamp, } = token const erc20 = { name, address, symbol, decimals } as TokenErc20 const network = useNetworkId() @@ -351,13 +375,21 @@ const RowToken: React.FC = ({ token, index }) => { + {lastDayUsdVolume ? ( + <> + + ${formatPrice({ price: new BigNumber(lastDayUsdVolume), decimals: 2, thousands: true })} + +
+ From: {timestamp ? format(fromUnixTime(timestamp), 'P pp zzzz') : ''} +
+ To: {format(fromUnixTime(startOfToday().setUTCHours(0) / 1000), 'P pp zzzz')} + + ) : ( + '$0' + )} + } > ${lastDayUsdVolume && numberFormatter(lastDayUsdVolume)} @@ -400,6 +432,11 @@ const TokenTable: React.FC = (props) => { } else { tableContent = ( <> + + + Sorted by Volume(24h): from highest to lowest + + {items.map((item, i) => ( ))} @@ -420,7 +457,7 @@ const TokenTable: React.FC = (props) => { Price Price (24h) Price (7d) - Volume (24h) + Volume (24h)↓ Total volume Price (last 7 days) diff --git a/src/hooks/useGetTokens.ts b/src/hooks/useGetTokens.ts index ff28843c3..51d43357f 100644 --- a/src/hooks/useGetTokens.ts +++ b/src/hooks/useGetTokens.ts @@ -1,127 +1,94 @@ import { useCallback, useEffect, useState } from 'react' import { gql } from '@apollo/client' -import { subDays, subHours } from 'date-fns' +import { subDays, subHours, startOfToday, startOfYesterday } from 'date-fns' import { Network, UiError } from 'types' import { COW_SDK, NATIVE_TOKEN_PER_NETWORK } from 'const' -import { TableState } from 'apps/explorer/components/TokensTableWidget/useTable' import { TokenErc20 } from '@gnosis.pm/dex-js' import { UTCTimestamp } from 'lightweight-charts' import { getPercentageDifference, isNativeToken } from 'utils' -export function useGetTokens(networkId: Network | undefined, tableState: TableState): GetTokensResult { +export function useGetTokens(networkId: Network | undefined): GetTokensResult { const [isLoading, setIsLoading] = useState(false) - const [historicalDataLoaded, setHistoricalDataLoaded] = useState<{ [pageIndex: string]: boolean }>({}) const [error, setError] = useState() const [tokens, setTokens] = useState([]) + const processTokenData = useCallback( + (data: SubgraphHistoricalDataResponse, lastDayUsdVolume: number, timestamp: number) => { + const { averagePrice: priceUsd } = data.tokenHourlyTotals[0] + const lastDayTimestampFrom = Number(lastHoursTimestamp(25)) + const lastDayTimestampTo = Number(lastHoursTimestamp(23)) + const lastWeekTimestampFrom = Number(lastDaysTimestamp(8)) + const lastWeekTimestampTo = Number(lastDaysTimestamp(6)) + const lastDayPrice = data.tokenHourlyTotals.find( + (x) => x.timestamp >= lastDayTimestampFrom && x.timestamp <= lastDayTimestampTo, + )?.averagePrice + const lastWeekPrice = data.tokenHourlyTotals.find( + (x) => x.timestamp >= lastWeekTimestampFrom && x.timestamp <= lastWeekTimestampTo, + )?.averagePrice + + return { + timestamp, + lastDayUsdVolume, + lastDayPricePercentageDifference: lastDayPrice + ? getPercentageDifference(Number(priceUsd), Number(lastDayPrice)) + : undefined, + lastWeekPricePercentageDifference: lastWeekPrice + ? getPercentageDifference(Number(priceUsd), Number(lastWeekPrice)) + : undefined, + lastWeekUsdPrices: data.tokenHourlyTotals + .map((x) => ({ + time: Number(x.timestamp) as UTCTimestamp, + value: Number(x.averagePrice), + })) + .sort((a, b) => a.time - b.time), + } + }, + [], + ) + const fetchTokens = useCallback( async (network: Network): Promise => { setIsLoading(true) setTokens([]) - setHistoricalDataLoaded({}) + const todayTimestamp = startOfToday().setUTCHours(0) / 1000 + const yesterdayTimestamp = startOfYesterday().setUTCHours(0) / 1000 + const lastWeekTimestamp = Number(lastDaysTimestamp(8)) // last 8 days try { - const response = await COW_SDK.cowSubgraphApi.runQuery<{ tokens: TokenResponse[] }>( + const response = await COW_SDK.cowSubgraphApi.runQuery<{ tokenDailyTotals: TokenDailyTotalsResponse[] }>( GET_TOKENS_QUERY, - undefined, - { chainId: network }, - ) - if (response) { - const tokens = enhanceNativeToken(response.tokens, network) - setTokens(tokens) - } - } catch (e) { - const msg = `Failed to fetch tokens` - console.error(msg, e) - setError({ message: msg, type: 'error' }) - } finally { - setIsLoading(false) - } - }, - [setTokens], - ) - - const getTokens = useCallback( - (tokenIds: string[]): { [tokenId: string]: Promise | undefined } => { - const lastDayTimestamp = Number(lastHoursTimestamp(25)) // last 25 hours - const lastWeekTimestamp = Number(lastDaysTimestamp(8)) // last 8 days - const responses = {} - for (const tokenId of tokenIds) { - const response = COW_SDK.cowSubgraphApi.runQuery( - GET_HISTORICAL_DATA_QUERY, { - address: tokenId, - lastDayTimestamp, + todayTimestamp, + yesterdayTimestamp, lastWeekTimestamp, }, - { chainId: networkId }, + { chainId: network }, ) - responses[tokenId] = response - } - - return responses - }, - [networkId], - ) - - const processTokenData = useCallback((data: SubgraphHistoricalDataResponse) => { - const lastDayUsdVolume = data.tokenHourlyTotals.reduce((acc, curr) => acc + Number(curr.totalVolumeUsd), 0) - const priceUsd = data.tokenHourlyTotals[0]?.averagePrice - - const lastDayTimestampFrom = Number(lastHoursTimestamp(25)) - const lastDayTimestampTo = Number(lastHoursTimestamp(23)) - const lastWeekTimestampFrom = Number(lastDaysTimestamp(8)) - const lastWeekTimestampTo = Number(lastDaysTimestamp(6)) - const lastDayPrice = data.tokenHourlyTotals.find( - (x) => x.timestamp >= lastDayTimestampFrom && x.timestamp <= lastDayTimestampTo, - )?.averagePrice - const lastWeekPrice = data.tokenHourlyTotals.find( - (x) => x.timestamp >= lastWeekTimestampFrom && x.timestamp <= lastWeekTimestampTo, - )?.averagePrice - - return { - lastDayUsdVolume, - lastDayPricePercentageDifference: lastDayPrice - ? getPercentageDifference(Number(priceUsd), Number(lastDayPrice)) - : undefined, - lastWeekPricePercentageDifference: lastWeekPrice - ? getPercentageDifference(Number(priceUsd), Number(lastWeekPrice)) - : undefined, - lastWeekUsdPrices: data.tokenHourlyTotals - .map((x) => ({ - time: Number(x.timestamp) as UTCTimestamp, - value: Number(x.averagePrice), - })) - .sort((a, b) => a.time - b.time), - } - }, []) - - const getTokensHistoricalData = useCallback( - async (tokenIds: string[]): Promise<{ [tokenId: string]: TokenData } | void> => { - setIsLoading(true) - try { - const tokens = await getTokens(tokenIds) - const tokensData = {} as { [tokenId: string]: TokenData } - - for (const address of Object.keys(tokens)) { - const response = await tokens[address] - if (!response) { - continue + if (response) { + const tokensData: { [tokenId: string]: TokenData } = {} + for (const tokenDailyTotal of response.tokenDailyTotals) { + const { token, totalVolumeUsd, timestamp } = tokenDailyTotal + const tokenData = processTokenData( + { tokenHourlyTotals: token.hourlyTotals }, + Number(totalVolumeUsd), + timestamp, + ) + tokensData[token.address] = { ...tokenData } } - - const tokenData = processTokenData(response) - tokensData[address] = { ...tokenData } + const tokens = addHistoricalData( + response.tokenDailyTotals.map((tokenDaily) => tokenDaily.token), + tokensData, + ) + setTokens(enhanceNativeToken(tokens, network)) } - - return tokensData } catch (e) { - const msg = `Failed to fetch tokens' data` + const msg = `Failed to fetch tokens` console.error(msg, e) setError({ message: msg, type: 'error' }) } finally { setIsLoading(false) } }, - [getTokens, processTokenData], + [processTokenData], ) useEffect(() => { @@ -132,29 +99,6 @@ export function useGetTokens(networkId: Network | undefined, tableState: TableSt fetchTokens(networkId) }, [fetchTokens, networkId]) - useEffect(() => { - if (tokens.length === 0 || !networkId || !tableState.pageIndex || historicalDataLoaded[tableState.pageIndex]) { - return - } - - const pageTokens = tokens.slice( - (tableState.pageIndex - 1) * tableState.pageSize, - tableState.pageIndex * tableState.pageSize, - ) - const tokenIds = pageTokens.map((token) => token.id) - - const setHistoricalData = async function (): Promise { - const historicalData = await getTokensHistoricalData(tokenIds) - - if (historicalData) { - setTokens((tokens) => [...addHistoricalData(tokens, historicalData)]) - setHistoricalDataLoaded((loaded) => ({ ...loaded, [tableState.pageIndex || 0]: true })) - } - } - - setHistoricalData() - }, [tableState.pageIndex, tableState.pageSize, tokens, networkId, getTokensHistoricalData, historicalDataLoaded]) - return { tokens, error, isLoading } } @@ -165,36 +109,42 @@ type GetTokensResult = { } export const GET_TOKENS_QUERY = gql` - query GetTokens { - tokens(first: 50, orderBy: totalVolumeUsd, orderDirection: desc, where: { totalVolumeUsd_not: null }) { - id - address - name - symbol - decimals - totalVolumeUsd - priceUsd - } - } -` - -export const GET_HISTORICAL_DATA_QUERY = gql` - query GetHistoricalData($address: ID!, $lastWeekTimestamp: Int!) { - tokenHourlyTotals( - orderBy: timestamp + query GetTokens($todayTimestamp: Int!, $yesterdayTimestamp: Int!, $lastWeekTimestamp: Int!) { + tokenDailyTotals( + first: 50 + orderBy: totalVolumeUsd orderDirection: desc - where: { token: $address, timestamp_gt: $lastWeekTimestamp } + where: { timestamp_gte: $yesterdayTimestamp, timestamp_lt: $todayTimestamp } ) { + timestamp + totalVolumeUsd token { + id address + name + symbol + decimals + totalVolumeUsd + priceUsd + hourlyTotals( + first: 1000 + orderBy: timestamp + orderDirection: desc + where: { timestamp_gt: $lastWeekTimestamp } + ) { + timestamp + averagePrice + } } - timestamp - totalVolumeUsd - averagePrice } } ` +export type TokenDailyTotalsResponse = { + timestamp: number + totalVolumeUsd: string + token: TokenResponse +} export type TokenResponse = { id: string name: string @@ -203,6 +153,7 @@ export type TokenResponse = { decimals: number priceUsd: string totalVolumeUsd: string + hourlyTotals: TokenHourlyTotals[] } export type TokenHourlyTotals = { @@ -220,6 +171,7 @@ export type Token = { lastDayPricePercentageDifference?: number | null lastWeekPricePercentageDifference?: number | null lastDayUsdVolume?: number | null + timestamp?: number | null lastWeekUsdPrices?: Array<{ time: UTCTimestamp; value: number }> | null } & TokenResponse & TokenErc20 @@ -228,18 +180,19 @@ function addHistoricalData(tokens: Token[], prices: { [tokenId: string]: TokenDa for (const address of Object.keys(prices)) { const token = tokens.find((token) => token.address === address) const values = prices[address] - if (token) { + token.timestamp = values.timestamp token.lastDayUsdVolume = values.lastDayUsdVolume token.lastDayPricePercentageDifference = values.lastDayPricePercentageDifference token.lastWeekPricePercentageDifference = values.lastWeekPricePercentageDifference token.lastWeekUsdPrices = values.lastWeekUsdPrices } } - return tokens + return tokens.sort((a, b) => (b.lastDayUsdVolume ?? -1) - (a.lastDayUsdVolume ?? -1)) } export type TokenData = { + timestamp: number lastDayUsdVolume?: number lastDayPricePercentageDifference?: number lastWeekPricePercentageDifference?: number From 46b87fdd616f62757e2413953e52e230386405ec Mon Sep 17 00:00:00 2001 From: Nenad Vracar <34926005+nenadV91@users.noreply.github.com> Date: Fri, 23 Sep 2022 15:36:19 +0200 Subject: [PATCH 2/8] Implement react-ga4 (#206) * Initial ga4 implementation * Fix for page titles * Change implementation to react-helmet --- package.json | 5 +- src/api/analytics/index.tsx | 63 ------------ src/apps/explorer/ExplorerApp.tsx | 27 +----- src/apps/explorer/const.ts | 2 + src/apps/explorer/pages/AppData/index.tsx | 5 + src/apps/explorer/pages/Home/index.tsx | 5 + src/apps/explorer/pages/NotFound.tsx | 6 ++ src/apps/explorer/pages/Order.tsx | 5 + src/apps/explorer/pages/SearchNotFound.tsx | 5 + .../explorer/pages/TransactionDetails.tsx | 5 + src/apps/explorer/pages/UserDetails.tsx | 5 + src/apps/safe-swap/SafeSwapApp.tsx | 25 +---- src/components/analytics/ExternalLink.tsx | 61 ++++++++---- .../analytics/GoogleAnalyticsProvider.ts | 53 +++++++++++ src/components/analytics/NetworkAnalytics.tsx | 19 ---- src/components/analytics/RouteAnalytics.tsx | 10 -- src/components/analytics/index.ts | 95 +++++++++++++++++++ .../BlockExplorerLink/BlockExplorerLink.tsx | 2 +- .../MenuDropdown/InternalExternalLink.tsx | 2 +- src/components/common/RouteAnalytics.tsx | 10 -- src/components/orders/DetailsTable/index.tsx | 4 +- src/utils/anonymizeLink.ts | 32 +++++++ yarn.lock | 37 +++++++- 23 files changed, 311 insertions(+), 172 deletions(-) delete mode 100644 src/api/analytics/index.tsx create mode 100644 src/components/analytics/GoogleAnalyticsProvider.ts delete mode 100644 src/components/analytics/NetworkAnalytics.tsx delete mode 100644 src/components/analytics/RouteAnalytics.tsx create mode 100644 src/components/analytics/index.ts delete mode 100644 src/components/common/RouteAnalytics.tsx create mode 100644 src/utils/anonymizeLink.ts diff --git a/package.json b/package.json index 76fe521f6..905bd1a55 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "@sentry/react": "^6.18.1", "@sentry/tracing": "^6.18.1", "@types/react": "^16.9.19", + "@types/react-helmet": "^6.1.5", "@types/react-is": "^16.7.1", "@types/react-select": "3.0.21", "@types/resize-observer-browser": "^0.1.4", @@ -102,7 +103,8 @@ "react-cytoscapejs": "^1.2.1", "react-device-detect": "^1.15.0", "react-dom": "^16.12.0", - "react-ga": "^3.3.0", + "react-ga4": "^1.4.1", + "react-helmet": "^6.1.0", "react-hook-form": "6.9.2", "react-hot-loader": "^4.12.19", "react-inlinesvg": "^3.0.0", @@ -111,6 +113,7 @@ "react-toastify": "^5.5.0", "styled-components": "^5.0.0", "styled-theming": "^2.2.0", + "web-vitals": "^2.1.0", "web3": "1.2.9", "web3modal": "1.9.0" }, diff --git a/src/api/analytics/index.tsx b/src/api/analytics/index.tsx deleted file mode 100644 index d8870ba16..000000000 --- a/src/api/analytics/index.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import ReactGA, { InitializeOptions } from 'react-ga' -import { isMobile } from 'react-device-detect' -import { AnalyticsDimension } from 'types' -export { pageview as viewPage, event as triggerEvent, set as setDimensions } from 'react-ga' - -function getCustomBrowserType(): string { - if (isMobile) { - return window.ethereum ? 'Mobile web3' : 'Mobile regular' - } else { - return window.web3 ? 'Desktop web3' : 'Desktop regular' - } -} - -interface InitAnalyticsParams { - /** - * UA Analytics code - */ - trackingCode?: string - - /** - * React-ga init options. - * - * See https://github.com/react-ga/react-ga - */ - options?: InitializeOptions - - /** - * Map a dimension to the actual key name. - * - * Two different apps can use different dimension for the same concept, this is why, every app can specify the - * dimension name for each type. - * - * dimensionsKey maps the logical name defined in the constant, to - * - */ - dimensionNames?: Record -} - -export function initAnalytics(params: InitAnalyticsParams): void { - const { trackingCode, options, dimensionNames = {} } = params - if (typeof trackingCode === 'string') { - ReactGA.initialize(trackingCode, options) - } else { - ReactGA.initialize('test', { testMode: true, debug: true }) - } - - // Set the browser dimension - const browserTypeDimension = dimensionNames[AnalyticsDimension.BROWSER_TYPE] - console.log('browserTypeDimension', browserTypeDimension) - if (browserTypeDimension) { - ReactGA.set({ - [browserTypeDimension]: getCustomBrowserType(), - }) - } - - // Track errors - window.addEventListener('error', (error) => { - ReactGA.exception({ - description: `${error.message} @ ${error.filename}:${error.lineno}:${error.colno}`, - fatal: true, - }) - }) -} diff --git a/src/apps/explorer/ExplorerApp.tsx b/src/apps/explorer/ExplorerApp.tsx index c8520c059..69280d748 100644 --- a/src/apps/explorer/ExplorerApp.tsx +++ b/src/apps/explorer/ExplorerApp.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { BrowserRouter, HashRouter, Route, Switch, useRouteMatch, Redirect } from 'react-router-dom' +import { BrowserRouter, HashRouter, Route, Switch, useRouteMatch, Redirect, useLocation } from 'react-router-dom' import { hot } from 'react-hot-loader/root' import { withGlobalContext } from 'hooks/useGlobalState' @@ -11,10 +11,7 @@ import { GenericLayout } from 'components/layout' import { Header } from './layout/Header' import { NetworkUpdater, RedirectMainnet, RedirectXdai } from 'state/network' -import { initAnalytics } from 'api/analytics' -import RouteAnalytics from 'components/analytics/RouteAnalytics' -import NetworkAnalytics from 'components/analytics/NetworkAnalytics' -import { DIMENSION_NAMES } from './const' +import { useAnalyticsReporter } from 'components/analytics' import * as Sentry from '@sentry/react' import { Integrations } from '@sentry/tracing' import { environmentName } from 'utils/env' @@ -38,13 +35,6 @@ if (SENTRY_DSN) { }) } -// Init analytics -const GOOGLE_ANALYTICS_ID: string | undefined = process.env.GOOGLE_ANALYTICS_ID -initAnalytics({ - trackingCode: GOOGLE_ANALYTICS_ID, - dimensionNames: DIMENSION_NAMES, -}) - // eslint-disable-next-line @typescript-eslint/no-explicit-any const Router: typeof BrowserRouter & typeof HashRouter = (window as any).IS_IPFS ? HashRouter : BrowserRouter @@ -111,24 +101,17 @@ function StateUpdaters(): JSX.Element { return } -const Analytics = (): JSX.Element => ( - <> - - - -) - /** App content */ const AppContent = (): JSX.Element => { + const location = useLocation() const { path } = useRouteMatch() - const pathPrefix = path == '/' ? '' : path + useAnalyticsReporter(location, 'Explorer') + return ( }> - - { return ( + + AppData Details - {APP_TITLE} + AppData Details { const networkId = useNetworkId() || undefined return ( + + {APP_TITLE} +

Search on CoW Protocol Explorer

diff --git a/src/apps/explorer/pages/NotFound.tsx b/src/apps/explorer/pages/NotFound.tsx index ae266bf4e..3ac4fef3c 100644 --- a/src/apps/explorer/pages/NotFound.tsx +++ b/src/apps/explorer/pages/NotFound.tsx @@ -1,10 +1,13 @@ import React from 'react' import styled from 'styled-components' +import { Helmet } from 'react-helmet' + import { ContentCard as Content, StyledLink, Title, Wrapper as WrapperTemplate } from 'apps/explorer/pages/styled' import { getNetworkFromId } from '@gnosis.pm/dex-js' import { useNetworkId } from 'state/network' import { media } from 'theme/styles/media' +import { APP_TITLE } from 'apps/explorer/const' const Wrapper = styled(WrapperTemplate)` max-width: 118rem; @@ -20,6 +23,9 @@ const NotFoundRequestPage: React.FC = () => { return ( + + Page not found - {APP_TITLE} + Page not found

We're sorry, the page you requested could not be found.

diff --git a/src/apps/explorer/pages/Order.tsx b/src/apps/explorer/pages/Order.tsx index f1fab59ce..3138b2a19 100644 --- a/src/apps/explorer/pages/Order.tsx +++ b/src/apps/explorer/pages/Order.tsx @@ -1,4 +1,5 @@ import React from 'react' +import { Helmet } from 'react-helmet' import { useOrderIdParam } from 'hooks/useSanitizeOrderIdAndUpdateUrl' import { isAnOrderId } from 'utils' @@ -7,6 +8,7 @@ import RedirectToSearch from 'components/RedirectToSearch' import { OrderWidget } from 'apps/explorer/components/OrderWidget' import { Wrapper as WrapperMod } from './styled' import styled from 'styled-components' +import { APP_TITLE } from 'apps/explorer/const' const Wrapper = styled(WrapperMod)` max-width: 140rem; @@ -25,6 +27,9 @@ const Order: React.FC = () => { return ( + + Order Details - {APP_TITLE} + ) diff --git a/src/apps/explorer/pages/SearchNotFound.tsx b/src/apps/explorer/pages/SearchNotFound.tsx index e6ed4b046..eeb3b3e05 100644 --- a/src/apps/explorer/pages/SearchNotFound.tsx +++ b/src/apps/explorer/pages/SearchNotFound.tsx @@ -1,11 +1,16 @@ import React from 'react' +import { Helmet } from 'react-helmet' import { OrderAddressNotFound } from 'components/orders/OrderNotFound' import { Wrapper } from './styled' +import { APP_TITLE } from 'apps/explorer/const' const SearchNotFound: React.FC = () => { return ( + + Search not found - {APP_TITLE} + ) diff --git a/src/apps/explorer/pages/TransactionDetails.tsx b/src/apps/explorer/pages/TransactionDetails.tsx index 98042ba48..2567d9773 100644 --- a/src/apps/explorer/pages/TransactionDetails.tsx +++ b/src/apps/explorer/pages/TransactionDetails.tsx @@ -1,11 +1,13 @@ import React from 'react' import { useParams } from 'react-router' +import { Helmet } from 'react-helmet' import { isATxHash } from 'utils' import RedirectToSearch from 'components/RedirectToSearch' import { Wrapper } from 'apps/explorer/pages/styled' import { useNetworkId } from 'state/network' import { TransactionsTableWidget } from 'apps/explorer/components/TransactionsTableWidget' +import { APP_TITLE } from 'apps/explorer/const' const TransactionDetails: React.FC = () => { const { txHash } = useParams<{ txHash: string }>() @@ -17,6 +19,9 @@ const TransactionDetails: React.FC = () => { return ( + + Transaction Details - {APP_TITLE} + ) diff --git a/src/apps/explorer/pages/UserDetails.tsx b/src/apps/explorer/pages/UserDetails.tsx index c2872885b..3f513945e 100644 --- a/src/apps/explorer/pages/UserDetails.tsx +++ b/src/apps/explorer/pages/UserDetails.tsx @@ -1,6 +1,7 @@ import React from 'react' import { useParams } from 'react-router' import styled from 'styled-components' +import { Helmet } from 'react-helmet' import OrdersTableWidget from '../components/OrdersTableWidget' import { useNetworkId } from 'state/network' @@ -9,6 +10,7 @@ import RedirectToSearch from 'components/RedirectToSearch' import { useResolveEns } from 'hooks/useResolveEns' import { TitleAddress, Wrapper as WrapperMod, FlexContainerVar } from 'apps/explorer/pages/styled' import CowLoading from 'components/common/CowLoading' +import { APP_TITLE } from 'apps/explorer/const' const Wrapper = styled(WrapperMod)` > h1 { @@ -27,6 +29,9 @@ const UserDetails: React.FC = () => { return ( + + User Details - {APP_TITLE} + {addressAccount ? ( <> diff --git a/src/apps/safe-swap/SafeSwapApp.tsx b/src/apps/safe-swap/SafeSwapApp.tsx index 21b6b0fb0..65d321ce7 100644 --- a/src/apps/safe-swap/SafeSwapApp.tsx +++ b/src/apps/safe-swap/SafeSwapApp.tsx @@ -1,6 +1,6 @@ import React from 'react' import SafeProvider from '@gnosis.pm/safe-apps-react-sdk' -import { BrowserRouter, HashRouter, Redirect, Route, Switch, useRouteMatch } from 'react-router-dom' +import { BrowserRouter, HashRouter, Redirect, Route, Switch, useRouteMatch, useLocation } from 'react-router-dom' import { hot } from 'react-hot-loader/root' import { withGlobalContext } from 'hooks/useGlobalState' @@ -12,17 +12,8 @@ import { GenericLayout } from 'components/layout' import { Header } from './layout/Header' import { NetworkUpdater, RedirectMainnet } from 'state/network' -import { initAnalytics } from 'api/analytics' -import RouteAnalytics from 'components/analytics/RouteAnalytics' -import NetworkAnalytics from 'components/analytics/NetworkAnalytics' -import { DIMENSION_NAMES } from './const' -// Init analytics -const GOOGLE_ANALYTICS_ID: string | undefined = process.env.GOOGLE_ANALYTICS_ID -initAnalytics({ - trackingCode: GOOGLE_ANALYTICS_ID, - dimensionNames: DIMENSION_NAMES, -}) +import { useAnalyticsReporter } from 'components/analytics' // eslint-disable-next-line @typescript-eslint/no-explicit-any const Router: typeof BrowserRouter & typeof HashRouter = (window as any).IS_IPFS ? HashRouter : BrowserRouter @@ -50,24 +41,18 @@ function StateUpdaters(): JSX.Element { return } -const Analytics = (): JSX.Element => ( - <> - - - -) - /** App content */ const AppContent = (): JSX.Element => { + const location = useLocation() const { path } = useRouteMatch() + useAnalyticsReporter(location, 'SafeSwapApp') + const pathPrefix = path == '/' ? '' : path return ( }> - - diff --git a/src/components/analytics/ExternalLink.tsx b/src/components/analytics/ExternalLink.tsx index 1aaf22238..42c2d2ce6 100644 --- a/src/components/analytics/ExternalLink.tsx +++ b/src/components/analytics/ExternalLink.tsx @@ -1,24 +1,49 @@ -import React, { PropsWithChildren, ReactNode } from 'react' -import { OutboundLink, OutboundLinkProps } from 'react-ga' +import React, { HTMLProps } from 'react' +import { outboundLink } from 'components/analytics' +import { anonymizeLink } from 'utils/anonymizeLink' -type PropsOriginal = PropsWithChildren> -type Props = Omit & { - eventLabel?: string - onClick?: (event: React.MouseEvent) => void - children?: ReactNode - href: string -} +export function handleClickExternalLink(event: React.MouseEvent): void { + const { target, href } = event.currentTarget + + const anonymizedHref = anonymizeLink(href) -export const ExternalLink: React.FC = (props: Props) => { - const { eventLabel, children, href, onClick } = props - const outboundProps = { - ...props, - onClick, - eventLabel: eventLabel || href, + // don't prevent default, don't redirect if it's a new tab + if (target === '_blank' || event.ctrlKey || event.metaKey) { + outboundLink({ label: anonymizedHref }, () => { + console.debug('Fired outbound link event', anonymizedHref) + }) + } else { + event.preventDefault() + // send a ReactGA event and then trigger a location change + outboundLink({ label: anonymizedHref }, () => { + window.location.href = anonymizedHref + }) } +} + +/** + * Outbound link that handles firing google analytics events + */ +export function ExternalLink({ + target = '_blank', + href, + rel = 'noopener noreferrer', + onClickOptional, + ...rest +}: Omit, 'as' | 'ref' | 'onClick'> & { + href: string + onClickOptional?: React.MouseEventHandler +}): JSX.Element { return ( - - {children} - + { + if (onClickOptional) onClickOptional(event) + handleClickExternalLink(event) + }} + {...rest} + /> ) } diff --git a/src/components/analytics/GoogleAnalyticsProvider.ts b/src/components/analytics/GoogleAnalyticsProvider.ts new file mode 100644 index 000000000..dade20acd --- /dev/null +++ b/src/components/analytics/GoogleAnalyticsProvider.ts @@ -0,0 +1,53 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import ReactGA from 'react-ga4' +import { GaOptions, InitOptions, UaEventOptions } from 'react-ga4/types/ga4' + +/** + * Google Analytics Provider containing all methods used throughout app to log events to Google Analytics. + */ +export default class GoogleAnalyticsProvider { + public sendEvent(event: string | UaEventOptions, params?: any): void { + ReactGA.event(event, params) + } + + public initialize( + GA_MEASUREMENT_ID: InitOptions[] | string, + options?: { + legacyDimensionMetric?: boolean + nonce?: string + testMode?: boolean + gaOptions?: GaOptions | any + gtagOptions?: any + }, + ): void { + ReactGA.initialize(GA_MEASUREMENT_ID, options) + } + + public set(fieldsObject: any): void { + ReactGA.set(fieldsObject) + } + + public outboundLink( + { + label, + }: { + label: string + }, + hitCallback: () => unknown, + ): void { + ReactGA.outboundLink({ label }, hitCallback) + } + + public pageview(path?: string, _?: string[], title?: string): void { + ReactGA.pageview(path, _, title) + } + + public ga(...args: any[]): void { + ReactGA.ga(...args) + } + + public gaCommandSendTiming(timingCategory: any, timingVar: any, timingValue: any, timingLabel: any): void { + ReactGA._gaCommandSendTiming(timingCategory, timingVar, timingValue, timingLabel) + } +} diff --git a/src/components/analytics/NetworkAnalytics.tsx b/src/components/analytics/NetworkAnalytics.tsx deleted file mode 100644 index f7e8d7323..000000000 --- a/src/components/analytics/NetworkAnalytics.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useEffect } from 'react' -import { setDimensions } from 'api/analytics' -import { useNetworkId } from 'state/network' -import { getNetworkFromId } from '@gnosis.pm/dex-js' - -const NETWORK_DIMENSION = 'dimension1' - -export default function NetworkAnalytics(): null { - const networkId = useNetworkId() - - // Update the GA network dimension - useEffect(() => { - const networkInfo = (networkId && getNetworkFromId(networkId)) || 'Not connected' - setDimensions({ - [NETWORK_DIMENSION]: networkInfo, - }) - }, [networkId]) - return null -} diff --git a/src/components/analytics/RouteAnalytics.tsx b/src/components/analytics/RouteAnalytics.tsx deleted file mode 100644 index 5a60ed47c..000000000 --- a/src/components/analytics/RouteAnalytics.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { useEffect } from 'react' -import { viewPage } from 'api/analytics' -import { RouteComponentProps } from 'react-router-dom' - -export default function RouteAnalytics({ location: { pathname, search } }: RouteComponentProps): null { - useEffect(() => { - viewPage(`${pathname}${search}`) - }, [pathname, search]) - return null -} diff --git a/src/components/analytics/index.ts b/src/components/analytics/index.ts new file mode 100644 index 000000000..fe8c4c8e1 --- /dev/null +++ b/src/components/analytics/index.ts @@ -0,0 +1,95 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { useNetworkId } from 'state/network' +import { useEffect } from 'react' +import { UaEventOptions } from 'react-ga4/types/ga4' +import { RouteComponentProps } from 'react-router-dom' +import { isMobile } from 'react-device-detect' +import { getCLS, getFCP, getFID, getLCP, Metric } from 'web-vitals' + +import GoogleAnalyticsProvider from './GoogleAnalyticsProvider' + +const GOOGLE_ANALYTICS_ID: string | undefined = process.env.GOOGLE_ANALYTICS_ID +export const GOOGLE_ANALYTICS_CLIENT_ID_STORAGE_KEY = 'ga_client_id' + +const storedClientId = window.localStorage.getItem(GOOGLE_ANALYTICS_CLIENT_ID_STORAGE_KEY) + +const googleAnalytics = new GoogleAnalyticsProvider() + +export function sendEvent(event: string | UaEventOptions, params?: any): void { + googleAnalytics.sendEvent(event, params) +} + +export function outboundLink( + { + label, + }: { + label: string + }, + hitCallback: () => unknown, +): any { + return googleAnalytics.outboundLink({ label }, hitCallback) +} + +export function sendTiming(timingCategory: any, timingVar: any, timingValue: any, timingLabel: any): any { + return googleAnalytics.gaCommandSendTiming(timingCategory, timingVar, timingValue, timingLabel) +} + +if (typeof GOOGLE_ANALYTICS_ID === 'string') { + googleAnalytics.initialize(GOOGLE_ANALYTICS_ID, { + gaOptions: { + storage: 'none', + storeGac: false, + clientId: storedClientId ?? undefined, + }, + }) + googleAnalytics.set({ + anonymizeIp: true, + customBrowserType: !isMobile + ? 'desktop' + : 'web3' in window || 'ethereum' in window + ? 'mobileWeb3' + : 'mobileRegular', + }) +} else { + googleAnalytics.initialize('test', { gtagOptions: { debug_mode: true } }) +} + +const installed = Boolean(window.navigator.serviceWorker?.controller) +const hit = Boolean((window as any).__isDocumentCached) +const action = installed ? (hit ? 'Cache hit' : 'Cache miss') : 'Not installed' +sendEvent({ category: 'Service Worker', action, nonInteraction: true }) + +function reportWebVitals({ name, delta, id }: Metric): void { + sendTiming('Web Vitals', name, Math.round(name === 'CLS' ? delta * 1000 : delta), id) +} + +// tracks web vitals and pageviews +export function useAnalyticsReporter({ pathname, search }: RouteComponentProps['location'], app: string): void { + useEffect(() => { + getFCP(reportWebVitals) + getFID(reportWebVitals) + getLCP(reportWebVitals) + getCLS(reportWebVitals) + }, []) + + const chainId = useNetworkId() + useEffect(() => { + // cd1 - custom dimension 1 - chainId + googleAnalytics.set({ cd1: chainId ?? 0 }) + }, [chainId]) + + useEffect(() => { + googleAnalytics.pageview(`${pathname}${search}`) + }, [pathname, search, app]) + + useEffect(() => { + // typed as 'any' in react-ga4 -.- + googleAnalytics.ga((tracker: any) => { + if (!tracker) return + + const clientId = tracker.get('clientId') + window.localStorage.setItem(GOOGLE_ANALYTICS_CLIENT_ID_STORAGE_KEY, clientId) + }) + }, []) +} diff --git a/src/components/common/BlockExplorerLink/BlockExplorerLink.tsx b/src/components/common/BlockExplorerLink/BlockExplorerLink.tsx index d0d8d7d29..dd3deb444 100644 --- a/src/components/common/BlockExplorerLink/BlockExplorerLink.tsx +++ b/src/components/common/BlockExplorerLink/BlockExplorerLink.tsx @@ -115,7 +115,7 @@ export const BlockExplorerLink: React.FC = (props: Props) => { const label = labelProp || (useUrlAsLabel && url) || abbreviateString(identifier, 6, 4) return ( - + {label} {showLogo && } diff --git a/src/components/common/MenuDropdown/InternalExternalLink.tsx b/src/components/common/MenuDropdown/InternalExternalLink.tsx index e2294759a..7bb1be17c 100644 --- a/src/components/common/MenuDropdown/InternalExternalLink.tsx +++ b/src/components/common/MenuDropdown/InternalExternalLink.tsx @@ -34,7 +34,7 @@ export default function InternalExternalMenuLink({ if (isExternal) { return ( - + {menuImage} {title} {menuImageExternal} diff --git a/src/components/common/RouteAnalytics.tsx b/src/components/common/RouteAnalytics.tsx deleted file mode 100644 index 5a60ed47c..000000000 --- a/src/components/common/RouteAnalytics.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { useEffect } from 'react' -import { viewPage } from 'api/analytics' -import { RouteComponentProps } from 'react-router-dom' - -export default function RouteAnalytics({ location: { pathname, search } }: RouteComponentProps): null { - useEffect(() => { - viewPage(`${pathname}${search}`) - }, [pathname, search]) - return null -} diff --git a/src/components/orders/DetailsTable/index.tsx b/src/components/orders/DetailsTable/index.tsx index 0048cc315..213b620c3 100644 --- a/src/components/orders/DetailsTable/index.tsx +++ b/src/components/orders/DetailsTable/index.tsx @@ -19,7 +19,7 @@ import { OrderSurplusDisplay } from 'components/orders/OrderSurplusDisplay' import { RowWithCopyButton } from 'components/common/RowWithCopyButton' import { StatusLabel } from 'components/orders/StatusLabel' import { GasFeeDisplay } from 'components/orders/GasFeeDisplay' -import { triggerEvent } from 'api/analytics' +import { sendEvent } from 'components/analytics' import { LinkWithPrefixNetwork } from 'components/common/LinkWithPrefixNetwork' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faProjectDiagram } from '@fortawesome/free-solid-svg-icons' @@ -173,7 +173,7 @@ export function DetailsTable(props: Props): JSX.Element | null { } const onCopy = (label: string): void => - triggerEvent({ + sendEvent({ category: 'Order details', action: 'Copy', label, diff --git a/src/utils/anonymizeLink.ts b/src/utils/anonymizeLink.ts new file mode 100644 index 000000000..2e0d47888 --- /dev/null +++ b/src/utils/anonymizeLink.ts @@ -0,0 +1,32 @@ +const EXPLORER_HOSTNAMES: { [hostname: string]: true } = { + 'etherscan.io': true, + 'ropsten.etherscan.io': true, + 'rinkeby.etherscan.io': true, + 'kovan.etherscan.io': true, + 'goerli.etherscan.io': true, + 'optimistic.etherscan.io': true, + 'kovan-optimistic.etherscan.io': true, + 'rinkeby-explorer.arbitrum.io': true, + 'arbiscan.io': true, +} + +/** + * Returns the anonymized version of the given href, i.e. one that does not leak user information + * @param href the link to anonymize, i.e. remove any personal data from + * @return string anonymized version of the given href + */ +export function anonymizeLink(href: string): string { + try { + const url = new URL(href) + if (EXPLORER_HOSTNAMES[url.hostname]) { + const pathPieces = url.pathname.split('/') + + const anonymizedPath = pathPieces.map((pc) => (/0x[a-fA-F0-9]+/.test(pc) ? '***' : pc)).join('/') + + return `${url.protocol}//${url.hostname}${anonymizedPath}` + } + return href + } catch (error) { + return href + } +} diff --git a/yarn.lock b/yarn.lock index 327d3f228..a264bdb53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4572,6 +4572,13 @@ dependencies: "@types/react" "^16" +"@types/react-helmet@^6.1.5": + version "6.1.5" + resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.5.tgz#35f89a6b1646ee2bc342a33a9a6c8777933f9083" + integrity sha512-/ICuy7OHZxR0YCAZLNg9r7I9aijWUWvxaPR6uTuyxe8tAj5RL4Sw1+R6NhXUtOsarkGYPmaHdBDvuXh2DIN/uA== + dependencies: + "@types/react" "*" + "@types/react-is@^16.7.1": version "16.7.2" resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-16.7.2.tgz#8c2862013d00d741be189ceb71da8e8d21e8fa7d" @@ -17406,7 +17413,7 @@ react-error-overlay@^6.0.9: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew== -react-fast-compare@^3.0.1, react-fast-compare@^3.2.0: +react-fast-compare@^3.0.1, react-fast-compare@^3.1.1, react-fast-compare@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== @@ -17416,10 +17423,10 @@ react-from-dom@^0.6.2: resolved "https://registry.yarnpkg.com/react-from-dom/-/react-from-dom-0.6.2.tgz#9da903a508c91c013b55afcd59348b8b0a39bdb4" integrity sha512-qvWWTL/4xw4k/Dywd41RBpLQUSq97csuv15qrxN+izNeLYlD9wn5W8LspbfYe5CWbaSdkZ72BsaYBPQf2x4VbQ== -react-ga@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-3.3.0.tgz#c91f407198adcb3b49e2bc5c12b3fe460039b3ca" - integrity sha512-o8RScHj6Lb8cwy3GMrVH6NJvL+y0zpJvKtc0+wmH7Bt23rszJmnqEQxRbyrqUzk9DTJIHoP42bfO5rswC9SWBQ== +react-ga4@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/react-ga4/-/react-ga4-1.4.1.tgz#6ee2a2db115ed235b2f2092bc746b4eeeca9e206" + integrity sha512-ioBMEIxd4ePw4YtaloTUgqhQGqz5ebDdC4slEpLgy2sLx1LuZBC9iYCwDymTXzcntw6K1dHX183ulP32nNdG7w== react-helmet-async@^1.0.7: version "1.0.9" @@ -17432,6 +17439,16 @@ react-helmet-async@^1.0.7: react-fast-compare "^3.2.0" shallowequal "^1.1.0" +react-helmet@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" + integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== + dependencies: + object-assign "^4.1.1" + prop-types "^15.7.2" + react-fast-compare "^3.1.1" + react-side-effect "^2.1.0" + react-hook-form@6.9.2: version "6.9.2" resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-6.9.2.tgz#9512cd424e62235fda13c8fc1821f88c352e3d23" @@ -17560,6 +17577,11 @@ react-select@^3.0.8, react-select@^3.2.0: react-input-autosize "^3.0.0" react-transition-group "^4.3.0" +react-side-effect@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a" + integrity sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw== + react-simple-animate@^3.3.8: version "3.3.12" resolved "https://registry.yarnpkg.com/react-simple-animate/-/react-simple-animate-3.3.12.tgz#ddea0f230feb3c1f069fbdb0a26e735e0b233265" @@ -21009,6 +21031,11 @@ web-namespaces@^1.0.0: resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== +web-vitals@^2.1.0: + version "2.1.4" + resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-2.1.4.tgz#76563175a475a5e835264d373704f9dde718290c" + integrity sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg== + web3-bzz@1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.9.tgz#25f8a373bc2dd019f47bf80523546f98b93c8790" From f53ef9c4042fc7ec2fa8ddfa2638df4d9b071b3d Mon Sep 17 00:00:00 2001 From: Leandro Boscariol Date: Wed, 28 Sep 2022 06:53:34 -0700 Subject: [PATCH 3/8] Fetch solvers names (#226) * Added helper function to fetch solver info from Dune GH SQL raw files * Added hook for using solver info useSolversInfo * Temporarily using the hook just for testing. To be reverted before merging --- src/apps/explorer/pages/Home/index.tsx | 7 +++ src/hooks/useSolversInfo.ts | 16 +++++++ src/utils/fetchSolversInfo.ts | 66 ++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 src/hooks/useSolversInfo.ts create mode 100644 src/utils/fetchSolversInfo.ts diff --git a/src/apps/explorer/pages/Home/index.tsx b/src/apps/explorer/pages/Home/index.tsx index 1f0fa1dda..30a938d88 100644 --- a/src/apps/explorer/pages/Home/index.tsx +++ b/src/apps/explorer/pages/Home/index.tsx @@ -8,6 +8,7 @@ import { useNetworkId } from 'state/network' import { TokensTableWidget } from 'apps/explorer/components/TokensTableWidget' import { Helmet } from 'react-helmet' import { APP_TITLE } from 'apps/explorer/const' +import { useSolversInfo } from 'hooks/useSolversInfo' const Wrapper = styled(WrapperMod)` max-width: 140rem; @@ -48,6 +49,12 @@ const SummaryWrapper = styled.section` export const Home: React.FC = () => { const networkId = useNetworkId() || undefined + + // TODO: remove me!!! + const solverInfo = useSolversInfo(networkId) + + console.log('solver info', solverInfo) + return ( diff --git a/src/hooks/useSolversInfo.ts b/src/hooks/useSolversInfo.ts new file mode 100644 index 000000000..aacc71370 --- /dev/null +++ b/src/hooks/useSolversInfo.ts @@ -0,0 +1,16 @@ +import { fetchSolversInfo, SolversInfo } from 'utils/fetchSolversInfo' +import { useEffect, useState } from 'react' + +export function useSolversInfo(network?: number): SolversInfo { + const [info, setInfo] = useState([]) + + useEffect(() => { + if (network) { + fetchSolversInfo(network).then(setInfo) + } else { + setInfo([]) + } + }, [network]) + + return info +} diff --git a/src/utils/fetchSolversInfo.ts b/src/utils/fetchSolversInfo.ts new file mode 100644 index 000000000..d17cc3edf --- /dev/null +++ b/src/utils/fetchSolversInfo.ts @@ -0,0 +1,66 @@ +export type SolverInfo = { + address: string + name: string + environment: string +} + +export type SolversInfo = SolverInfo[] + +const SOLVER_SOURCE_PER_NETWORK = { + 1: 'https://raw.githubusercontent.com/duneanalytics/spellbook/main/models/cow_protocol/ethereum/cow_protocol_ethereum_solvers.sql', + 100: 'https://raw.githubusercontent.com/duneanalytics/spellbook/main/deprecated-dune-v1-abstractions/xdai/gnosis_protocol_v2/view_solvers.sql', +} + +/** + * This is a very dirty temporary solution to get the solver info dynamically + * + * The source file is a SQL with the structure that we care about looking like: + * FROM (VALUES ('0xf2d21ad3c88170d4ae52bbbeba80cb6078d276f4', 'prod', 'MIP'), + * ('0x15f4c337122ec23859ec73bec00ab38445e45304', 'prod', 'Gnosis_ParaSwap'), + * The regex below extracts the 3 fields we are looking for and ignore the rest + */ +const REGEX = /\('(0x[0-9a-fA-F]{40})',\s*'(\w+)',\s*'([\w\s]+)'\)/g + +const SOLVERS_INFO_CACHE: Record = {} + +export async function fetchSolversInfo(network: number): Promise { + const url = SOLVER_SOURCE_PER_NETWORK[network] + + if (!url) { + return [] + } + + const cache = SOLVERS_INFO_CACHE[network] + if (cache) { + return cache + } + + try { + const response = await fetch(url) + const result = _parseSolverInfo(await response.text()) + + SOLVERS_INFO_CACHE[network] = result + + return result + } catch (e) { + console.error(`Failed to fetch solvers info from '${url}'`) + return [] + } +} + +function _parseSolverInfo(body: string): SolversInfo { + const info: SolversInfo = [] + + const matches = body.matchAll(REGEX) + + // Each match has 4 entries: + // 1. The whole capture group + // 2. The address + // 3. The environment + // 4. The name + for (const [, address, environment, name] of matches) { + info.push({ address, environment, name }) + } + + return info +} From d362e02aaee042046bccfc3432997b2e12c8fd90 Mon Sep 17 00:00:00 2001 From: Alfetopito Date: Wed, 28 Sep 2022 15:25:44 +0100 Subject: [PATCH 4/8] Removing test use of `useSolversInfo` that got merged by mistake --- src/apps/explorer/pages/Home/index.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/apps/explorer/pages/Home/index.tsx b/src/apps/explorer/pages/Home/index.tsx index 30a938d88..7f063d72f 100644 --- a/src/apps/explorer/pages/Home/index.tsx +++ b/src/apps/explorer/pages/Home/index.tsx @@ -8,7 +8,6 @@ import { useNetworkId } from 'state/network' import { TokensTableWidget } from 'apps/explorer/components/TokensTableWidget' import { Helmet } from 'react-helmet' import { APP_TITLE } from 'apps/explorer/const' -import { useSolversInfo } from 'hooks/useSolversInfo' const Wrapper = styled(WrapperMod)` max-width: 140rem; @@ -50,11 +49,6 @@ const SummaryWrapper = styled.section` export const Home: React.FC = () => { const networkId = useNetworkId() || undefined - // TODO: remove me!!! - const solverInfo = useSolversInfo(networkId) - - console.log('solver info', solverInfo) - return ( From 7335775124c025608e6fbe36cfdbd0c62056a3f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Longoni?= Date: Fri, 30 Sep 2022 06:34:58 -0300 Subject: [PATCH 5/8] Inter var font improvements (#228) * fix inter var font + add stylistics sets * remove duplicated characters variants * remove Avenir font from network selector * applied fallback font display for all the app * fix font-weight network selector --- src/apps/explorer/pages/AppData/styled.ts | 1 - .../NetworkSelector/NetworkSelector.styled.ts | 1 - src/theme/styles/global.ts | 14 ++++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/apps/explorer/pages/AppData/styled.ts b/src/apps/explorer/pages/AppData/styled.ts index fdbc20a4e..27f103946 100644 --- a/src/apps/explorer/pages/AppData/styled.ts +++ b/src/apps/explorer/pages/AppData/styled.ts @@ -226,7 +226,6 @@ export const Wrapper = styled(WrapperTemplate)` padding: 0.8rem 1.5rem; transition-duration: 0.2s; transition-timing-function: ease-in-out; - font-family: 'Inter var', sans-serif; ${media.mobile} { margin: 1rem 0 0 0; diff --git a/src/components/NetworkSelector/NetworkSelector.styled.ts b/src/components/NetworkSelector/NetworkSelector.styled.ts index bc36d5d50..329bf00c8 100644 --- a/src/components/NetworkSelector/NetworkSelector.styled.ts +++ b/src/components/NetworkSelector/NetworkSelector.styled.ts @@ -34,7 +34,6 @@ export const OptionsContainer = styled.div<{ width: number }>` export const Option = styled.div` display: flex; flex: 1; - font-family: var(--font-avenir); font-weight: 800; font-size: 13px; line-height: 18px; diff --git a/src/theme/styles/global.ts b/src/theme/styles/global.ts index a3b793fa1..cb1e2d578 100644 --- a/src/theme/styles/global.ts +++ b/src/theme/styles/global.ts @@ -67,11 +67,25 @@ export const StaticGlobalStyle = createGlobalStyle` ` export const ThemedGlobalStyle = createGlobalStyle` + input, + textarea, + button { + font-family: ${({ theme }): string => theme.fontDefault}, sans-serif; + } + @supports (font-variation-settings: normal) { + input, + textarea, + button { + font-family: ${({ theme }): string => theme.fontVariable}, sans-serif; + } + } html, body { background: ${({ theme }): string => theme.bg1}; color: ${({ theme }): string => theme.textPrimary1}; /* StyleLint fights you for the sans-serif as it requires a fallback and can't detect it from the theme prop */ font-family: ${({ theme }): string => theme.fontDefault}, sans-serif; + font-feature-settings: 'ss01' on, 'ss02' on; + font-display: fallback; @supports (font-variation-settings: normal) { font-family: ${({ theme }): string => theme.fontVariable}, sans-serif; From 85bfd76c21555f3971e8113eb96ce7921053f1db Mon Sep 17 00:00:00 2001 From: Leandro Boscariol Date: Mon, 3 Oct 2022 05:47:17 -0700 Subject: [PATCH 6/8] Cowswap977/deprecate rinkeby (#232) * Bumping cow-sdk to latest * Removed files already incorporated into the SDK * Removed rinkeby from currently used spots * Updated network selector * removed it from storybook stories * Removed/edited from the legacy code. Should remove it all eventually * Fixed unit test --- package.json | 2 +- src/api/deposit/batchExchangeAddresses.ts | 2 +- src/api/operator/constants.ts | 18 -------- src/api/operator/operatorApi.ts | 5 ++- src/api/operator/signatures.ts | 43 ------------------- src/api/operator/types.ts | 14 +----- src/api/tenderly/tenderlyApi.ts | 1 - src/api/tokenList/index.ts | 2 +- src/api/wallet/WalletApiMock.ts | 14 +----- src/api/web3/index.ts | 1 - src/api/weth/WethApi.ts | 6 +-- src/apps/explorer/ExplorerApp.tsx | 2 +- src/apps/explorer/const.ts | 2 +- .../NetworkSelector/NetworkSelector.styled.ts | 9 ---- src/components/NetworkSelector/index.tsx | 5 --- .../BlockExplorerLink.stories.tsx | 10 ----- src/components/common/TokenImg.stories.tsx | 8 +--- src/components/common/TradeOrderType.tsx | 2 +- .../order-book/OrderBookWidget.stories.tsx | 1 - .../trade/TradesTable/index.stories.tsx | 7 +-- src/components/trade/TradesTable/index.tsx | 11 ++--- src/const.ts | 5 +-- src/data/quoteTokenPriorities.ts | 8 ++-- src/services/factories/tokenList.ts | 10 +---- src/state/network/updater.tsx | 5 +-- src/types/config.ts | 1 - src/types/index.ts | 9 ---- src/utils/walletconnectOptions.ts | 2 +- src/utils/wrap.ts | 6 +-- test/api/ExchangeApi/TokenListApiMock.test.ts | 2 +- .../BlockExplorerLink.components.test.tsx | 5 --- test/config.spec.ts | 15 +------ test/data/operator.ts | 4 +- test/hooks/useDecomposedPath.test.tsx | 4 +- .../services/tryGetOrderOnAllNetworks.test.ts | 12 ++++-- test/utils/url.spec.ts | 8 ++-- webpack.config.js | 2 - yarn.lock | 8 ++-- 38 files changed, 61 insertions(+), 210 deletions(-) delete mode 100644 src/api/operator/constants.ts delete mode 100644 src/api/operator/signatures.ts diff --git a/package.json b/package.json index 27432f062..d642adb13 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "@apollo/client": "^3.1.5", "@cowprotocol/app-data": "0.0.1-RC.5", "@cowprotocol/contracts": "1.3.1", - "@cowprotocol/cow-sdk": "1.0.0-RC.5", + "@cowprotocol/cow-sdk": "^1.0.0-RC.7", "@fortawesome/fontawesome-svg-core": "^6.1.2", "@fortawesome/free-regular-svg-icons": "^6.1.2", "@fortawesome/free-solid-svg-icons": "^6.1.2", diff --git a/src/api/deposit/batchExchangeAddresses.ts b/src/api/deposit/batchExchangeAddresses.ts index b09ddda19..67b3fd39c 100644 --- a/src/api/deposit/batchExchangeAddresses.ts +++ b/src/api/deposit/batchExchangeAddresses.ts @@ -3,7 +3,7 @@ import { Network } from 'types' import { getGpV1ContractAddress } from 'utils/contract' const addressesByNetwork = { - [Network.RINKEBY]: getGpV1ContractAddress(Network.RINKEBY), + [4]: getGpV1ContractAddress(4), [Network.MAINNET]: getGpV1ContractAddress(Network.MAINNET), [Network.GNOSIS_CHAIN]: getGpV1ContractAddress(Network.GNOSIS_CHAIN), } diff --git a/src/api/operator/constants.ts b/src/api/operator/constants.ts deleted file mode 100644 index 7ea9366ef..000000000 --- a/src/api/operator/constants.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { GPv2Settlement, GPv2VaultRelayer } from '@cowprotocol/contracts/networks.json' -import { Network } from 'types' - -// TODO: When contracts are deployed, we can load this from the NPM package -export const GP_SETTLEMENT_CONTRACT_ADDRESS: Partial> = { - [Network.MAINNET]: GPv2Settlement[Network.MAINNET].address, - [Network.RINKEBY]: GPv2Settlement[Network.RINKEBY].address, - [Network.GNOSIS_CHAIN]: GPv2Settlement[Network.GNOSIS_CHAIN].address, -} - -export const GP_VAULT_RELAYER: Partial> = { - [Network.MAINNET]: GPv2VaultRelayer[Network.MAINNET].address, - [Network.RINKEBY]: GPv2VaultRelayer[Network.RINKEBY].address, - [Network.GNOSIS_CHAIN]: GPv2VaultRelayer[Network.GNOSIS_CHAIN].address, -} - -// See https://github.com/gnosis/gp-v2-contracts/commit/821b5a8da213297b0f7f1d8b17c893c5627020af#diff-12bbbe13cd5cf42d639e34a39d8795021ba40d3ee1e1a8282df652eb161a11d6R13 -export const BUY_ETHER_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' diff --git a/src/api/operator/operatorApi.ts b/src/api/operator/operatorApi.ts index 5ff9ba1a0..cf9a7bbfd 100644 --- a/src/api/operator/operatorApi.ts +++ b/src/api/operator/operatorApi.ts @@ -31,17 +31,18 @@ function explorerToApiEnv(explorerEnv?: Envs): ApiEnv { } } +// TODO: should come from the SDK by now, find out where this is used function getOperatorUrl(): Partial> { if (isProd || isStaging) { return { [Network.MAINNET]: process.env.OPERATOR_URL_PROD_MAINNET, - [Network.RINKEBY]: process.env.OPERATOR_URL_PROD_RINKEBY, + [Network.GOERLI]: process.env.OPERATOR_URL_PROD_GOERLI, [Network.GNOSIS_CHAIN]: process.env.OPERATOR_URL_PROD_XDAI, } } else { return { [Network.MAINNET]: process.env.OPERATOR_URL_STAGING_MAINNET, - [Network.RINKEBY]: process.env.OPERATOR_URL_STAGING_RINKEBY, + [Network.GOERLI]: process.env.OPERATOR_URL_STAGING_GOERLI, [Network.GNOSIS_CHAIN]: process.env.OPERATOR_URL_STAGING_XDAI, } } diff --git a/src/api/operator/signatures.ts b/src/api/operator/signatures.ts deleted file mode 100644 index 9a027b737..000000000 --- a/src/api/operator/signatures.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { domain as domainGp, signOrder as signOrderGp, Order } from '@cowprotocol/contracts' - -import { GP_SETTLEMENT_CONTRACT_ADDRESS } from './constants' -import { TypedDataDomain, Signer } from 'ethers' -import { Network } from 'types' - -export { OrderKind } from '@cowprotocol/contracts' -export type UnsignedOrder = Order - -export interface SignOrderParams { - networkId: Network - signer: Signer - order: UnsignedOrder -} - -export interface OrderCreation extends UnsignedOrder { - signature: string // 65 bytes encoded as hex without `0x` prefix. r + s + v from the spec -} - -const TYPED_DATA_SIGNING_SCHEME = 0 - -function _getDomain(networkId: Network): TypedDataDomain { - // Get settlement contract address - const settlementContract = GP_SETTLEMENT_CONTRACT_ADDRESS[networkId] - - if (!settlementContract) { - throw new Error('Unsupported network. Settlement contract is not deployed') - } - - return domainGp(networkId, settlementContract) // TODO: Fix types in NPM package -} - -export async function signOrder(params: SignOrderParams): Promise { - const { networkId, signer, order } = params - - const domain = _getDomain(networkId) - - console.log('[api:operator:signature] signOrder', { domain, order, signer, TYPED_DATA_SIGNING_SCHEME }) - - const { data: signature } = await signOrderGp(domain, order, signer, TYPED_DATA_SIGNING_SCHEME) - - return signature.toString() -} diff --git a/src/api/operator/types.ts b/src/api/operator/types.ts index ed3c4e4ed..83b02ad5b 100644 --- a/src/api/operator/types.ts +++ b/src/api/operator/types.ts @@ -2,6 +2,7 @@ import BigNumber from 'bignumber.js' import { TokenErc20 } from '@gnosis.pm/dex-js' +import { OrderKind } from '@cowprotocol/contracts' import { OrderMetaData, TradeMetaData } from '@cowprotocol/cow-sdk' import { Network } from 'types' @@ -9,19 +10,6 @@ import { Network } from 'types' export type OrderID = string export type TxHash = string -export interface OrderPostError { - errorType: 'MissingOrderData' | 'InvalidSignature' | 'DuplicateOrder' | 'InsufficientFunds' - description: string -} - -export interface FeeInformation { - expirationDate: string - minimalFee: string - feeRatio: number -} - -export type OrderKind = 'sell' | 'buy' - export type OrderStatus = 'open' | 'filled' | 'cancelled' | 'cancelling' | 'expired' | 'signing' export type RawOrderStatusFromAPI = 'presignaturePending' | 'open' | 'fullfilled' | 'cancelled' | 'expired' diff --git a/src/api/tenderly/tenderlyApi.ts b/src/api/tenderly/tenderlyApi.ts index 036a8a799..dd8d71e2f 100644 --- a/src/api/tenderly/tenderlyApi.ts +++ b/src/api/tenderly/tenderlyApi.ts @@ -23,7 +23,6 @@ function _urlAvailableNetwork(): Partial> { return { [Network.MAINNET]: urlNetwork(Network.MAINNET), - [Network.RINKEBY]: urlNetwork(Network.RINKEBY), [Network.GNOSIS_CHAIN]: urlNetwork(Network.GNOSIS_CHAIN), [Network.GOERLI]: urlNetwork(Network.GOERLI), } diff --git a/src/api/tokenList/index.ts b/src/api/tokenList/index.ts index a62840fa9..973672945 100644 --- a/src/api/tokenList/index.ts +++ b/src/api/tokenList/index.ts @@ -6,7 +6,7 @@ import TokenListApiMock from './TokenListApiMock' import { tokenList } from '../../../test/data' export function createTokenListApi(): TokenList { - const networkIds = [Network.MAINNET, Network.RINKEBY, Network.GNOSIS_CHAIN] + const networkIds = [Network.MAINNET, Network.GOERLI, Network.GNOSIS_CHAIN] let tokenListApi: TokenList if (process.env.MOCK_TOKEN_LIST === 'true') { diff --git a/src/api/wallet/WalletApiMock.ts b/src/api/wallet/WalletApiMock.ts index 3b8c0e08f..a6cd57a7c 100644 --- a/src/api/wallet/WalletApiMock.ts +++ b/src/api/wallet/WalletApiMock.ts @@ -4,7 +4,7 @@ import assert from 'assert' import { toWei } from '@gnosis.pm/dex-js' import { logDebug, wait } from 'utils' -import { USER_1, USER_2 } from '../../../test/data' +import { USER_1 } from '../../../test/data' import { WalletApi, WalletInfo, ProviderInfo } from './WalletApi' type OnChangeWalletInfo = (walletInfo: WalletInfo) => void @@ -29,7 +29,7 @@ export class WalletApiMock implements WalletApi { public constructor() { this._connected = process.env.AUTOCONNECT === 'true' this._user = USER_1 - this._networkId = Network.RINKEBY + this._networkId = Network.GOERLI this._balance = toWei(new BN(2.75), 'ether') this._listeners = [] } @@ -107,16 +107,6 @@ export class WalletApiMock implements WalletApi { /* **************** Test functions **************** */ // Functions created just for simulate some cases - public changeUser(): void { - this._user = this._user === USER_1 ? USER_2 : USER_1 - this._notifyListeners() - } - - public changeNetwork(): void { - this._networkId = this._networkId === Network.RINKEBY ? Network.MAINNET : Network.RINKEBY - this._notifyListeners() - } - public async getWalletInfo(): Promise { return { isConnected: this._connected, diff --git a/src/api/web3/index.ts b/src/api/web3/index.ts index 1df7b5753..414277531 100644 --- a/src/api/web3/index.ts +++ b/src/api/web3/index.ts @@ -87,7 +87,6 @@ function isWebsocketConnection(): boolean { export function getProviderByNetwork(networkId: Network | null): string | undefined { switch (networkId) { case Network.MAINNET: - case Network.RINKEBY: case Network.GOERLI: return infuraProvider(networkId) case Network.GNOSIS_CHAIN: diff --git a/src/api/weth/WethApi.ts b/src/api/weth/WethApi.ts index c52c35bd6..32fa12973 100644 --- a/src/api/weth/WethApi.ts +++ b/src/api/weth/WethApi.ts @@ -1,7 +1,7 @@ import Web3 from 'web3' import { Network, WithTxOptionalParams, Receipt } from 'types' -import { WETH_ADDRESS_MAINNET, WETH_ADDRESS_RINKEBY, WXDAI_ADDRESS_XDAI } from 'const' +import { WETH_ADDRESS_GOERLI, WETH_ADDRESS_MAINNET, WXDAI_ADDRESS_XDAI } from 'const' import { wethAbi } from '@gnosis.pm/dex-js' import { logDebug } from 'utils' @@ -29,8 +29,8 @@ function getWethAddressByNetwork(networkId: number): string { switch (networkId) { case Network.MAINNET: return WETH_ADDRESS_MAINNET - case Network.RINKEBY: - return WETH_ADDRESS_RINKEBY + case Network.GOERLI: + return WETH_ADDRESS_GOERLI case Network.GNOSIS_CHAIN: // Is not wxDAI is not WETH, but it has the same approve/withdraw methods // it's just convenient to not rename the API and keep calling it WethApi although it wraps also xDAI diff --git a/src/apps/explorer/ExplorerApp.tsx b/src/apps/explorer/ExplorerApp.tsx index 69280d748..886678d59 100644 --- a/src/apps/explorer/ExplorerApp.tsx +++ b/src/apps/explorer/ExplorerApp.tsx @@ -147,7 +147,7 @@ export const ExplorerApp: React.FC = () => { - + {process.env.NODE_ENV === 'development' && } diff --git a/src/apps/explorer/const.ts b/src/apps/explorer/const.ts index 00f4840bc..e92163862 100644 --- a/src/apps/explorer/const.ts +++ b/src/apps/explorer/const.ts @@ -22,7 +22,7 @@ export const DIMENSION_NAMES = { [AnalyticsDimension.BROWSER_TYPE]: 'dimension2', } -export const NETWORK_ID_SEARCH_LIST = [Network.MAINNET, Network.GNOSIS_CHAIN, Network.RINKEBY, Network.GOERLI] +export const NETWORK_ID_SEARCH_LIST = [Network.MAINNET, Network.GNOSIS_CHAIN, Network.GOERLI] // Estimation heigh of the header + footer space export const HEIGHT_HEADER_FOOTER = 257 diff --git a/src/components/NetworkSelector/NetworkSelector.styled.ts b/src/components/NetworkSelector/NetworkSelector.styled.ts index 329bf00c8..27ef8159e 100644 --- a/src/components/NetworkSelector/NetworkSelector.styled.ts +++ b/src/components/NetworkSelector/NetworkSelector.styled.ts @@ -22,7 +22,6 @@ export const OptionsContainer = styled.div<{ width: number }>` z-index: 1; margin: 0 auto; width: ${(props: { width: number }): string => `${184 + props.width}px`}; - height: 170px; left: 15px; top: 54px; background: ${({ theme }): string => theme.bg1}; @@ -60,9 +59,6 @@ export const Option = styled.div` &.görli { background: ${(): string => goerliColor}; } - &.rinkeby { - background: ${({ theme }): string => theme.yellow4}; - } &.gnosischain { background: ${(): string => gnosisChainColor}; } @@ -88,11 +84,6 @@ export const NetworkLabel = styled.span` color: ${({ theme }): string => theme.black}; } - &.rinkeby { - background: ${({ theme }): string => theme.yellow4}; - color: ${({ theme }): string => theme.black}; - } - &.ethereum { background: ${({ theme }): string => theme.blue4}; color: ${({ theme }): string => theme.textSecondary1}; diff --git a/src/components/NetworkSelector/index.tsx b/src/components/NetworkSelector/index.tsx index 61bfdb7b8..5e4383627 100644 --- a/src/components/NetworkSelector/index.tsx +++ b/src/components/NetworkSelector/index.tsx @@ -29,11 +29,6 @@ export const networkOptions: NetworkOptions[] = [ name: 'Gnosis Chain', url: 'gc', }, - { - id: Network.RINKEBY, - name: 'Rinkeby', - url: 'rinkeby', - }, { id: Network.GOERLI, name: 'Görli', diff --git a/src/components/common/BlockExplorerLink/BlockExplorerLink.stories.tsx b/src/components/common/BlockExplorerLink/BlockExplorerLink.stories.tsx index 4b3edad9e..32f6abf07 100644 --- a/src/components/common/BlockExplorerLink/BlockExplorerLink.stories.tsx +++ b/src/components/common/BlockExplorerLink/BlockExplorerLink.stories.tsx @@ -10,7 +10,6 @@ import { ADDRESS_GNO_XDAI, ADDRESS_GNO, ADDRESS_GNOSIS_PROTOCOL, - ADDRESS_GNOSIS_PROTOCOL_RINKEBY, TX_EXAMPLE, TX_XDAI, } from 'storybook/data' @@ -45,15 +44,6 @@ Mainnet.args = { ...defaultParams, } -export const Rinkeby = Template.bind({}) -Rinkeby.args = { - ...defaultParams, - networkId: Network.RINKEBY, - type: 'contract', - label: 'Gnosis Protocol (Rinkeby)', - identifier: ADDRESS_GNOSIS_PROTOCOL_RINKEBY, -} - export const Labeled = Template.bind({}) Labeled.args = { ...defaultParams, diff --git a/src/components/common/TokenImg.stories.tsx b/src/components/common/TokenImg.stories.tsx index 1fe7dfe40..542ba1087 100644 --- a/src/components/common/TokenImg.stories.tsx +++ b/src/components/common/TokenImg.stories.tsx @@ -2,7 +2,7 @@ import React from 'react' // also exported from '@storybook/react' if you can deal with breaking changes in 6.1 import { Story, Meta } from '@storybook/react/types-6-0' import { TokenImg, Props } from './TokenImg' -import { WETH_ADDRESS_MAINNET, WETH_ADDRESS_RINKEBY } from 'const' +import { WETH_ADDRESS_MAINNET } from 'const' import { Frame } from './Frame' import styled from 'styled-components' import { ADDRESS_GNO, ADDRESS_WXDAI } from 'storybook/data' @@ -27,12 +27,6 @@ WethMainnet.args = { address: WETH_ADDRESS_MAINNET, } -export const WethRinkeby = Template.bind({}) -WethRinkeby.args = { - address: WETH_ADDRESS_RINKEBY, - addressMainnet: WETH_ADDRESS_MAINNET, -} - export const WrappedXdai = Template.bind({}) WrappedXdai.args = { address: ADDRESS_WXDAI, diff --git a/src/components/common/TradeOrderType.tsx b/src/components/common/TradeOrderType.tsx index 2f93b8fc1..68f25f38d 100644 --- a/src/components/common/TradeOrderType.tsx +++ b/src/components/common/TradeOrderType.tsx @@ -1,7 +1,7 @@ import React from 'react' import styled from 'styled-components' +import { OrderKind } from '@cowprotocol/contracts' -import { OrderKind } from 'api/operator' import { capitalize } from 'utils' const TradeTypeWrapper = styled.div` diff --git a/src/components/order-book/OrderBookWidget.stories.tsx b/src/components/order-book/OrderBookWidget.stories.tsx index 632a5cc6b..bf630667d 100644 --- a/src/components/order-book/OrderBookWidget.stories.tsx +++ b/src/components/order-book/OrderBookWidget.stories.tsx @@ -23,7 +23,6 @@ const defaultParams: OrderBookProps = { // with price-estimation endpoints const availableNetworks = { Mainnet: networkMap.MAINNET, - Rinkeby: networkMap.RINKEBY, } export default { diff --git a/src/components/trade/TradesTable/index.stories.tsx b/src/components/trade/TradesTable/index.stories.tsx index 78a87b5d2..f85016cba 100644 --- a/src/components/trade/TradesTable/index.stories.tsx +++ b/src/components/trade/TradesTable/index.stories.tsx @@ -1,13 +1,14 @@ import React from 'react' // also exported from '@storybook/react' if you can deal with breaking changes in 6.1 -import { Story, Meta } from '@storybook/react/types-6-0' +import { Meta, Story } from '@storybook/react/types-6-0' import TradesTable, { Props as TradesUserTableProps } from '.' import { sub } from 'date-fns' import BigNumber from 'bignumber.js' -import { GlobalStyles, ThemeToggler, Router, NetworkDecorator } from 'storybook/decorators' +import { GlobalStyles, NetworkDecorator, Router, ThemeToggler } from 'storybook/decorators' import { Trade } from 'api/operator' import { RICH_TRADE, TUSD, WETH } from '../../../../test/data' +import { OrderKind } from '@cowprotocol/contracts' export default { title: 'trade/TradesTable', @@ -17,7 +18,7 @@ export default { const tradeBuy: Trade = { ...RICH_TRADE, - kind: 'buy', + kind: OrderKind.BUY, orderId: 'bdef89ac', buyToken: WETH, sellToken: TUSD, diff --git a/src/components/trade/TradesTable/index.tsx b/src/components/trade/TradesTable/index.tsx index 1b118e482..faf800495 100644 --- a/src/components/trade/TradesTable/index.tsx +++ b/src/components/trade/TradesTable/index.tsx @@ -2,28 +2,29 @@ import React, { useState } from 'react' import styled from 'styled-components' import { faExchangeAlt } from '@fortawesome/free-solid-svg-icons' -import { Trade, RawOrder } from 'api/operator' +import { RawOrder, Trade } from 'api/operator' import { DateDisplay } from 'components/common/DateDisplay' import { RowWithCopyButton } from 'components/common/RowWithCopyButton' import { BlockExplorerLink } from 'components/common/BlockExplorerLink' import { - getOrderLimitPrice, - getOrderExecutedPrice, formatCalculatedPriceToDisplay, formatExecutedPriceToDisplay, formattedAmount, + getOrderExecutedPrice, + getOrderLimitPrice, } from 'utils' import { getShortOrderId } from 'utils/operator' import { HelpTooltip } from 'components/Tooltip' import StyledUserDetailsTable, { - StyledUserDetailsTableProps, EmptyItemWrapper, + StyledUserDetailsTableProps, } from '../../common/StyledUserDetailsTable' import Icon from 'components/Icon' import TradeOrderType from 'components/common/TradeOrderType' import { Surplus } from './Surplus' import { LinkWithPrefixNetwork } from 'components/common/LinkWithPrefixNetwork' +import { OrderKind } from '@cowprotocol/contracts' const Wrapper = styled(StyledUserDetailsTable)` > thead > tr, @@ -116,7 +117,7 @@ const RowOrder: React.FC = ({ trade, isPriceInverted }) => { /> - + {formattedAmount(sellToken, sellAmount)} {sellToken?.symbol} diff --git a/src/const.ts b/src/const.ts index f32b97ce9..316516b45 100644 --- a/src/const.ts +++ b/src/const.ts @@ -8,7 +8,6 @@ export { BATCH_TIME, MAX_BATCH_ID, FEE_PERCENTAGE, - DEFAULT_DECIMALS as OLD_DEFAULT_DECIMALS, DEFAULT_PRECISION, ZERO, ONE, @@ -172,7 +171,7 @@ export const LIQUIDITY_TOKEN_LIST = new Set(LIQUIDITY_TOKEN_LIST_VALUES.split(', export const INPUT_PRECISION_SIZE = 6 export const WETH_ADDRESS_MAINNET = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' -export const WETH_ADDRESS_RINKEBY = '0xc778417E063141139Fce010982780140Aa0cD5Ab' +export const WETH_ADDRESS_GOERLI = '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6' export const WXDAI_ADDRESS_XDAI = '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d' export const WETH_ADDRESS_XDAI = '0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1' export const NATIVE_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' @@ -222,7 +221,6 @@ export const DISABLED_TOKEN_MAPS = Object.keys(disabledTokens).reduce = { '1': ETH, - '4': ETH, '5': ETH, '100': XDAI, } diff --git a/src/data/quoteTokenPriorities.ts b/src/data/quoteTokenPriorities.ts index f2eb2d81d..51d2eaa67 100644 --- a/src/data/quoteTokenPriorities.ts +++ b/src/data/quoteTokenPriorities.ts @@ -31,7 +31,7 @@ const quoteTokenPriorityList: QuoteTokenPriorityList = [ '0x9a48bd0ec040ea4f1d3147c025cd4076a2e71e3e', // USD++ ], // Rinkeby - [Network.RINKEBY]: [ + [4]: [ '0xa9881E6459CA05d7D7C95374463928369cD7a90C', // USDT '0x4DBCdF9B62e891a7cec5A2568C3F4FAF9E8Abe2b', // USDC '0x5592EC0cfb4dbc12D3aB100b257153436a1f0FEa', // DAI @@ -58,7 +58,7 @@ const quoteTokenPriorityList: QuoteTokenPriorityList = [ addresses: { [Network.MAINNET]: ['0x1a5f9352af8af974bfc03399e3767df6370d82e4'], // OWL // Rinkeby - [Network.RINKEBY]: ['0xa7d1c04faf998f9161fc9f800a99a809b84cfc9d'], // OWL + [4]: ['0xa7d1c04faf998f9161fc9f800a99a809b84cfc9d'], // OWL // xDAI [Network.GNOSIS_CHAIN]: ['0x0905ab807f8fd040255f0cf8fa14756c1d824931'], // xOWL }, @@ -76,7 +76,7 @@ const quoteTokenPriorityList: QuoteTokenPriorityList = [ '0x1fc31488f28ac846588ffa201cde0669168471bd', // UAX ], // Rinkeby - [Network.RINKEBY]: [], + [4]: [], // xDAI [Network.GNOSIS_CHAIN]: [ '0x0e0293b766e89011abd9bea5612d978c3a13cbb8', // dCHF @@ -91,7 +91,7 @@ const quoteTokenPriorityList: QuoteTokenPriorityList = [ '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH ], // Rinkeby - [Network.RINKEBY]: [ + [4]: [ '0xc778417E063141139Fce010982780140Aa0cD5Ab', // WETH ], // xDAI diff --git a/src/services/factories/tokenList.ts b/src/services/factories/tokenList.ts index faaf903fc..cbba24f6f 100644 --- a/src/services/factories/tokenList.ts +++ b/src/services/factories/tokenList.ts @@ -7,7 +7,7 @@ import { logDebug, notEmpty, retry } from 'utils' import { TokenFromErc20Params } from './' import { safeTokenName, TokenErc20 } from '@gnosis.pm/dex-js' -import { WETH_ADDRESS_MAINNET, WETH_ADDRESS_RINKEBY, WETH_ADDRESS_XDAI, WXDAI_ADDRESS_XDAI } from 'const' +import { WETH_ADDRESS_MAINNET, WETH_ADDRESS_XDAI, WXDAI_ADDRESS_XDAI } from 'const' export function getTokensFactory(factoryParams: { tokenListApi: TokenList @@ -211,14 +211,6 @@ export function getTokensFactory(factoryParams: { return compareByLabel(a, b) } break - case Network.RINKEBY: - comparator = (a, b): number => { - // WETH first - if (a.address === WETH_ADDRESS_RINKEBY) return -1 - if (b.address === WETH_ADDRESS_RINKEBY) return 1 - return compareByLabel(a, b) - } - break case Network.GNOSIS_CHAIN: comparator = (a, b): number => { // WXDAI before WETH diff --git a/src/state/network/updater.tsx b/src/state/network/updater.tsx index 06d2b23ba..13a38fc69 100644 --- a/src/state/network/updater.tsx +++ b/src/state/network/updater.tsx @@ -13,7 +13,6 @@ const MAINNET_PREFIX = '' const NETWORK_PREFIXES_RAW: [Network, string][] = [ [Network.MAINNET, ''], [Network.GNOSIS_CHAIN, 'gc'], - [Network.RINKEBY, 'rinkeby'], [Network.GOERLI, 'goerli'], ] export const PREFIX_BY_NETWORK_ID: Map = new Map(NETWORK_PREFIXES_RAW) @@ -36,7 +35,7 @@ function getNetworkPrefix(network: Network): string { */ export const useDecomposedPath = (): [string, string] | [] => { const { pathname } = useLocation() - const pathMatchArray = pathname.match('/(rinkeby|xdai|mainnet|gc|goerli)?/?(.*)') + const pathMatchArray = pathname.match('/(xdai|mainnet|gc|goerli)?/?(.*)') return pathMatchArray == null ? [] : [pathMatchArray[1], pathMatchArray[2]] } @@ -90,7 +89,7 @@ export const NetworkUpdater: React.FC = () => { const location = useLocation() useEffect(() => { - const networkMatchArray = location.pathname.match('^/(rinkeby|gc|goerli)') + const networkMatchArray = location.pathname.match('^/(gc|goerli)') const network = networkMatchArray && networkMatchArray.length > 0 ? networkMatchArray[1] : undefined const networkId = getNetworkId(network) diff --git a/src/types/config.ts b/src/types/config.ts index 484ba0389..31db3dc6c 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -65,7 +65,6 @@ export interface TokenOverride { export interface DisabledTokens { [Network.MAINNET]: TokenOverride[] - [Network.RINKEBY]: TokenOverride[] [Network.GNOSIS_CHAIN]: TokenOverride[] } diff --git a/src/types/index.ts b/src/types/index.ts index 7f22a1b89..69d230ae8 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -10,15 +10,6 @@ export type AnyFunction = (...args: unknown[]) => unknown export type Mutation = (original: T) => T export type Unpromise = T extends Promise ? U : T -//This Enum here is temporary until SupportedChainId is exported -/* -export enum Network { - MAINNET = 1, - RINKEBY = 4, - GNOSIS_CHAIN = 100, -} -*/ - export const Network = SupportedChainId export type Network = SupportedChainId diff --git a/src/utils/walletconnectOptions.ts b/src/utils/walletconnectOptions.ts index e201ccb2b..17b6d2861 100644 --- a/src/utils/walletconnectOptions.ts +++ b/src/utils/walletconnectOptions.ts @@ -41,7 +41,7 @@ const mapStoredRpc = (rpc?: WCOptions['rpc']): IRPCMap | undefined => { const rpcMap = {} if (mainnet) rpcMap[Network.MAINNET] = mainnet - if (rinkeby) rpcMap[Network.RINKEBY] = rinkeby + if (rinkeby) rpcMap[4] = rinkeby if (xDAI) rpcMap[Network.GNOSIS_CHAIN] = xDAI return rpcMap diff --git a/src/utils/wrap.ts b/src/utils/wrap.ts index e3b26fe7c..0f121a55b 100644 --- a/src/utils/wrap.ts +++ b/src/utils/wrap.ts @@ -1,4 +1,4 @@ -import { WETH_ADDRESS_MAINNET, WETH_ADDRESS_RINKEBY, WXDAI_ADDRESS_XDAI } from 'const' +import { WETH_ADDRESS_GOERLI, WETH_ADDRESS_MAINNET, WXDAI_ADDRESS_XDAI } from 'const' import { Network } from 'types' export interface NativeTokenInfo { @@ -10,8 +10,8 @@ export function getIsWrappable(networkId: number, address: string): boolean { switch (networkId) { case Network.MAINNET: return address === WETH_ADDRESS_MAINNET - case Network.RINKEBY: - return address === WETH_ADDRESS_RINKEBY + case Network.GOERLI: + return address === WETH_ADDRESS_GOERLI case Network.GNOSIS_CHAIN: return address === WXDAI_ADDRESS_XDAI default: diff --git a/test/api/ExchangeApi/TokenListApiMock.test.ts b/test/api/ExchangeApi/TokenListApiMock.test.ts index 11425a3b5..bc72ad21f 100644 --- a/test/api/ExchangeApi/TokenListApiMock.test.ts +++ b/test/api/ExchangeApi/TokenListApiMock.test.ts @@ -18,7 +18,7 @@ let instanceReal: TokenList beforeEach(() => { instanceMock = new TokenListApiMock(testTokenList) - instanceReal = new TokenListApiImpl({ networkIds: [Network.MAINNET, Network.RINKEBY], initialTokenList }) + instanceReal = new TokenListApiImpl({ networkIds: [Network.MAINNET], initialTokenList }) }) const NEW_TOKEN = { diff --git a/test/components/BlockExplorerLink.components.test.tsx b/test/components/BlockExplorerLink.components.test.tsx index cb1fbc8ca..2e8c77502 100644 --- a/test/components/BlockExplorerLink.components.test.tsx +++ b/test/components/BlockExplorerLink.components.test.tsx @@ -75,11 +75,6 @@ describe('', () => { expect(wrapper.prop('href')).toMatch(`https://etherscan.io/tx/${TX_HASH}`) }) - it('renders link to rinkeby', () => { - networkId = Network.RINKEBY - const wrapper = render() - expect(wrapper.prop('href')).toMatch(`https://rinkeby.etherscan.io/tx/${TX_HASH}`) - }) it('renders link to xDai', () => { networkId = Network.GNOSIS_CHAIN const wrapper = render() diff --git a/test/config.spec.ts b/test/config.spec.ts index c5326f363..f95255cc9 100644 --- a/test/config.spec.ts +++ b/test/config.spec.ts @@ -55,23 +55,17 @@ describe('Test config defaults', () => { config: [ { networkId: 1, - // eslint-disable-next-line @typescript-eslint/camelcase url_production: 'https://dex-price-estimator.gnosis.io', - // eslint-disable-next-line @typescript-eslint/camelcase url_develop: 'https://price-estimate-mainnet.dev.gnosisdev.com', }, { networkId: 4, - // eslint-disable-next-line @typescript-eslint/camelcase url_production: 'https://dex-price-estimator.rinkeby.gnosis.io', - // eslint-disable-next-line @typescript-eslint/camelcase url_develop: 'https://price-estimate-rinkeby.dev.gnosisdev.com', }, { networkId: 100, - // eslint-disable-next-line @typescript-eslint/camelcase url_production: 'https://dex-price-estimator.xdai.gnosis.io', - // eslint-disable-next-line @typescript-eslint/camelcase url_develop: 'https://price-estimate-xdai.dev.gnosisdev.com', }, ], @@ -132,15 +126,10 @@ describe('Test config defaults', () => { }), ]) - const { - [Network.MAINNET]: disabledOnMainnet, - [Network.RINKEBY]: disabledOnRinkeby, - [Network.GNOSIS_CHAIN]: disabledOnXdai, - } = CONFIG.disabledTokens + const { [Network.MAINNET]: disabledOnMainnet, [Network.GNOSIS_CHAIN]: disabledOnXdai } = CONFIG.disabledTokens if (disabledOnMainnet.length) expect(disabledOnMainnet).toEqual(disabledTokensArray) - if (disabledOnRinkeby.length) expect(disabledOnRinkeby).toEqual(disabledTokensArray) - if (disabledOnRinkeby.length) expect(disabledOnXdai).toEqual(disabledTokensArray) + if (disabledOnXdai.length) expect(disabledOnXdai).toEqual(disabledTokensArray) }) it('initialTokenSelection', () => { diff --git a/test/data/operator.ts b/test/data/operator.ts index 7d18ba710..4047b8dee 100644 --- a/test/data/operator.ts +++ b/test/data/operator.ts @@ -1,11 +1,11 @@ import BigNumber from 'bignumber.js' +import { OrderKind } from '@cowprotocol/contracts' import { Order, RawOrder, RawTrade, Trade } from 'api/operator' import { ZERO_BIG_NUMBER } from 'const' import { USDT, WETH } from './erc20s' -import { OrderKind } from '@cowprotocol/contracts' export const RAW_ORDER = { creationDate: '2021-01-20T23:15:07.892538607Z', @@ -71,7 +71,7 @@ export const RAW_TRADE: RawTrade = { export const RICH_TRADE: Trade = { ...RAW_TRADE, orderId: RAW_TRADE.orderUid, - kind: 'sell', + kind: OrderKind.SELL, executionTime: new Date('2021-01-20T23:15:07.892538607Z'), buyAmount: new BigNumber(RAW_TRADE.buyAmount), executedBuyAmount: new BigNumber('50000000000000000'), diff --git a/test/hooks/useDecomposedPath.test.tsx b/test/hooks/useDecomposedPath.test.tsx index 03b8df036..4408f4030 100644 --- a/test/hooks/useDecomposedPath.test.tsx +++ b/test/hooks/useDecomposedPath.test.tsx @@ -20,7 +20,7 @@ function wrapperMemoryRouter(props: Props): JSX.Element { describe('useDecomposedPath', () => { it('get prefix network and suffix of pathname on react-router', () => { // given - const networkPrefix = '/rinkeby' + const networkPrefix = '/gc' const pathSuffix = '/address/123' const mockLocation = networkPrefix + pathSuffix @@ -29,7 +29,7 @@ describe('useDecomposedPath', () => { wrapper: ({ children }) => wrapperMemoryRouter({ children, mockLocation }), }) - expect(result.current[0]).toBe(networkPrefix.substr(1)) // rinkeby + expect(result.current[0]).toBe(networkPrefix.substr(1)) // gchain expect(result.current[1]).toBe(pathSuffix.substr(1)) // addrres... }) diff --git a/test/services/tryGetOrderOnAllNetworks.test.ts b/test/services/tryGetOrderOnAllNetworks.test.ts index 915a67d63..32462db2a 100644 --- a/test/services/tryGetOrderOnAllNetworks.test.ts +++ b/test/services/tryGetOrderOnAllNetworks.test.ts @@ -1,13 +1,17 @@ import { GetTxOrdersParams } from 'api/operator/types' -import { GetOrderApi, MultipleOrders, tryGetOrderOnAllNetworksAndEnvironments } from 'services/helpers/tryGetOrderOnAllNetworks' +import { + GetOrderApi, + MultipleOrders, + tryGetOrderOnAllNetworksAndEnvironments, +} from 'services/helpers/tryGetOrderOnAllNetworks' import { RAW_ORDER } from '../data' import { Network } from 'types' -const networkIdSearchListRemaining = [Network.MAINNET, Network.RINKEBY] +const networkIdSearchListRemaining = [Network.MAINNET, Network.GOERLI] describe('tryGetOrderOnAllNetworks', () => { test('Should consult other networks when the order is empty', async () => { - const network = Network.RINKEBY + const network = Network.GOERLI const txHash = '0xTest_txHash' const defaultParams: GetTxOrdersParams = { networkId: network, txHash } const mockedApi = jest.fn().mockImplementation(() => Promise.resolve(null)) @@ -22,7 +26,7 @@ describe('tryGetOrderOnAllNetworks', () => { expect(result).toEqual({ order: null }) }) test('Should return and not call other networks when encountered', async () => { - const network = Network.RINKEBY + const network = Network.GOERLI const txHash = '0xTest_txHash' const ordersResult = [RAW_ORDER] const defaultParams: GetTxOrdersParams = { networkId: network, txHash } diff --git a/test/utils/url.spec.ts b/test/utils/url.spec.ts index 18aee1326..524b9f080 100644 --- a/test/utils/url.spec.ts +++ b/test/utils/url.spec.ts @@ -10,15 +10,15 @@ describe('replace URL path according to network', () => { }) test('path is replaced to xdai', () => { - const currentPath = '/rinkeby/address/0xb6bad41ae76a11d10f7b0e664c5007b908bc77c9' - expect(replaceURL(currentPath, 'xdai', Network.RINKEBY)).toBe( + const currentPath = '/goerli/address/0xb6bad41ae76a11d10f7b0e664c5007b908bc77c9' + expect(replaceURL(currentPath, 'xdai', Network.GOERLI)).toBe( '/xdai/address/0xb6bad41ae76a11d10f7b0e664c5007b908bc77c9', ) }) test('url is replaced to mainnet', () => { - const currentPath = '/rinkeby/address/0xb6bad41ae76a11d10f7b0e664c5007b908bc77c9' - expect(replaceURL(currentPath, '', Network.RINKEBY)).toBe('/address/0xb6bad41ae76a11d10f7b0e664c5007b908bc77c9') + const currentPath = '/goerli/address/0xb6bad41ae76a11d10f7b0e664c5007b908bc77c9' + expect(replaceURL(currentPath, '', Network.GOERLI)).toBe('/address/0xb6bad41ae76a11d10f7b0e664c5007b908bc77c9') }) }) diff --git a/webpack.config.js b/webpack.config.js index 8cf762675..54808f053 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -26,11 +26,9 @@ const EXPLORER_APP = { EXPLORER_APP_DOMAIN_REGEX_BARN: '^barn\\.explorer\\.cow\\.fi|^barn\\.gnosis-protocol\\.io', OPERATOR_URL_STAGING_MAINNET: 'https://barn.api.cow.fi/mainnet/api', - OPERATOR_URL_STAGING_RINKEBY: 'https://barn.api.cow.fi/rinkeby/api', OPERATOR_URL_STAGING_GOERLI: 'https://barn.api.cow.fi/goerli/api', OPERATOR_URL_STAGING_XDAI: 'https://barn.api.cow.fi/xdai/api', OPERATOR_URL_PROD_MAINNET: 'https://api.cow.fi/mainnet/api', - OPERATOR_URL_PROD_RINKEBY: 'https://api.cow.fi/rinkeby/api', OPERATOR_URL_PROD_GOERLI: 'https://api.cow.fi/goerli/api', OPERATOR_URL_PROD_XDAI: 'https://api.cow.fi/xdai/api', diff --git a/yarn.lock b/yarn.lock index a264bdb53..99212e955 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1286,10 +1286,10 @@ resolved "https://registry.yarnpkg.com/@cowprotocol/contracts/-/contracts-1.3.1.tgz#a812b27bdd0c046ab730eb24911ef7c641ed0df8" integrity sha512-p93xODog3XG5eSDU5od1ERvYf7YIyXd7USknKcsWnka5VN5mDDjqjCKTLw8EoZdrCIEpf6fGd8At2nv+MsvNqA== -"@cowprotocol/cow-sdk@1.0.0-RC.5": - version "1.0.0-RC.5" - resolved "https://registry.yarnpkg.com/@cowprotocol/cow-sdk/-/cow-sdk-1.0.0-RC.5.tgz#2ab136f47d1ce8b36aac28b775150c11bbd26444" - integrity sha512-UQZLNc+FBT/XTFkKqmHagttDi0o62bVc2woycL/gxjmoi99iLmvYGi1TVF/V+CvxwE8evRR1Kgm1q2NdnGNmqg== +"@cowprotocol/cow-sdk@^1.0.0-RC.7": + version "1.0.0-RC.7" + resolved "https://registry.yarnpkg.com/@cowprotocol/cow-sdk/-/cow-sdk-1.0.0-RC.7.tgz#c365495ce1b2c1eaf8c755ec5535ff4e95344bab" + integrity sha512-oJg3yzlCcrrz/1SFI7UaMYoPvFntM8/K/UI05sXkzGjxTjj+LDWimEjHTdYsACceHj+Ds84ORZEuB+Y3nGzyGg== dependencies: "@cowprotocol/app-data" "^0.0.1-RC.3" "@cowprotocol/contracts" "^1.3.1" From cad3bd010768b68b41c10d44a13562d7dc909168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Longoni?= Date: Tue, 4 Oct 2022 11:21:18 -0300 Subject: [PATCH 7/8] [Rebranding] Updates with the CoW Protocol new brand on Batch Viewer (#225) * update Cow protocol logo and Trader icon * update cowLoading component * change cow trader image --- .../TransanctionBatchGraph/styled.ts | 6 +- src/assets/img/CoW-loading.svg | 16 +++++ src/assets/img/CoW-protocol.svg | 18 ++++++ src/assets/img/Trader-variant.svg | 62 +++++++++++++++++++ src/assets/img/Trader.svg | 7 +++ src/assets/img/TraderMe.svg | 16 ----- src/assets/img/TraderOther.svg | 23 ------- src/assets/img/cowLoading.svg | 11 ---- src/components/common/CowLoading.tsx | 32 ++-------- 9 files changed, 110 insertions(+), 81 deletions(-) create mode 100644 src/assets/img/CoW-loading.svg create mode 100644 src/assets/img/CoW-protocol.svg create mode 100644 src/assets/img/Trader-variant.svg create mode 100644 src/assets/img/Trader.svg delete mode 100644 src/assets/img/TraderMe.svg delete mode 100644 src/assets/img/TraderOther.svg delete mode 100644 src/assets/img/cowLoading.svg diff --git a/src/apps/explorer/components/TransanctionBatchGraph/styled.ts b/src/apps/explorer/components/TransanctionBatchGraph/styled.ts index 9cd5ac67d..9d1347b20 100644 --- a/src/apps/explorer/components/TransanctionBatchGraph/styled.ts +++ b/src/apps/explorer/components/TransanctionBatchGraph/styled.ts @@ -1,8 +1,8 @@ import { Stylesheet } from 'cytoscape' import styled, { DefaultTheme, css } from 'styled-components' -import TraderOtherIcon from 'assets/img/TraderOther.svg' -import CowProtocolIcon from 'assets/img/CoW.svg' +import TraderIcon from 'assets/img/Trader.svg' +import CowProtocolIcon from 'assets/img/CoW-protocol.svg' import DexIcon from 'assets/img/Dex.svg' import { MEDIA } from 'const' import { Dropdown } from 'apps/explorer/components/common/Dropdown' @@ -150,7 +150,7 @@ export function STYLESHEET(theme: DefaultTheme): Stylesheet[] { { selector: 'node[type="trader"]', style: { - 'background-image': `url(${TraderOtherIcon})`, + 'background-image': `url(${TraderIcon})`, 'text-valign': 'bottom', 'text-margin-y': 8, }, diff --git a/src/assets/img/CoW-loading.svg b/src/assets/img/CoW-loading.svg new file mode 100644 index 000000000..f1ebe1e89 --- /dev/null +++ b/src/assets/img/CoW-loading.svg @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/src/assets/img/CoW-protocol.svg b/src/assets/img/CoW-protocol.svg new file mode 100644 index 000000000..1e0a63dea --- /dev/null +++ b/src/assets/img/CoW-protocol.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/Trader-variant.svg b/src/assets/img/Trader-variant.svg new file mode 100644 index 000000000..19c4cbfcf --- /dev/null +++ b/src/assets/img/Trader-variant.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/Trader.svg b/src/assets/img/Trader.svg new file mode 100644 index 000000000..f98a1d2ff --- /dev/null +++ b/src/assets/img/Trader.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/img/TraderMe.svg b/src/assets/img/TraderMe.svg deleted file mode 100644 index 7cf20e1d6..000000000 --- a/src/assets/img/TraderMe.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/assets/img/TraderOther.svg b/src/assets/img/TraderOther.svg deleted file mode 100644 index fc96622b3..000000000 --- a/src/assets/img/TraderOther.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/cowLoading.svg b/src/assets/img/cowLoading.svg deleted file mode 100644 index 871da0902..000000000 --- a/src/assets/img/cowLoading.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/components/common/CowLoading.tsx b/src/components/common/CowLoading.tsx index 74288129f..55480eda5 100644 --- a/src/components/common/CowLoading.tsx +++ b/src/components/common/CowLoading.tsx @@ -1,7 +1,7 @@ import React from 'react' import styled, { keyframes } from 'styled-components' import SVG from 'react-inlinesvg' -import CowLoadingSVG from 'assets/img/cowLoading.svg' +import CowLoadingSVG from 'assets/img/CoW-loading.svg' export const WrapperCenter = styled.div` display: flex; @@ -10,46 +10,22 @@ export const WrapperCenter = styled.div` height: 100%; ` -const CowBounce = keyframes` +const CowAnimation = keyframes` 0%, 100% { transform: scale(0.95) translateX(1px); + opacity: 0.4; } 50% { transform: scale(1); - } -` - -const EyesOpacity = keyframes` - from { - opacity: 1; - } - 30% { - opacity: 0.3; - } - to { opacity: 1; } ` - export const StyledCowLoading = styled(SVG)` .cowLoading { - animation: ${CowBounce} 1.5s infinite ease-in-out; + animation: ${CowAnimation} 1.4s infinite ease-in-out; animation-delay: -1s; } - .cowHead { - fill: ${({ theme }): string => theme.white}; - opacity: 0.4; - } - .eyesBg { - fill: ${({ theme }): string => theme.bg1}; - opacity: 1; - } - .eyes { - fill: ${({ theme }): string => theme.orange}; - animation: ${EyesOpacity} 1.5s ease-in-out infinite; - filter: blur(1px); - } ` export const CowLoading: React.FC = () => ( From 5c18d52e6963b5a04ab17e6988e82c0089df5052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Longoni?= Date: Thu, 6 Oct 2022 15:30:08 -0300 Subject: [PATCH 8/8] layout improvement (#233) --- src/apps/explorer/styled.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/apps/explorer/styled.ts b/src/apps/explorer/styled.ts index fc57e542c..70e9e544a 100644 --- a/src/apps/explorer/styled.ts +++ b/src/apps/explorer/styled.ts @@ -62,6 +62,7 @@ export const MainWrapper = styled.div` display: flex; flex-direction: column; flex-grow: 1; + justify-content: space-between; } footer { flex-direction: row;