Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(explorer): support Sepolia network #3580

Merged
merged 53 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
6dadb0c
chore: setup blank react project
shoom3301 Jan 3, 2024
7734964
chore: copy-paste explorer files
shoom3301 Jan 3, 2024
86b3c1b
chore: copy-paste test files
shoom3301 Jan 3, 2024
18741d1
feat(explorer): update project and migrate to vite
shoom3301 Jan 8, 2024
8496103
feat(explorer): support Sepolia network
shoom3301 Jan 8, 2024
7ecfc22
chore: update yarn lock
shoom3301 Jan 8, 2024
01696c0
chore: revert orderBookSDK env
shoom3301 Jan 8, 2024
60db855
chore: hide charts for Sepolia
shoom3301 Jan 8, 2024
f3de188
chore: remove appId
shoom3301 Jan 8, 2024
e42f873
chore: remove unnecessary code
shoom3301 Jan 8, 2024
1451406
chore: update yarn lock
shoom3301 Jan 8, 2024
fec0e13
chore: remove subgraph for Sepolia
shoom3301 Jan 9, 2024
bdd4d27
chore: tenderly url for Sepolia
shoom3301 Jan 9, 2024
59d5ffd
refactor: use Record for network configs
shoom3301 Jan 9, 2024
2d9a492
chore: remove todos and fix 404 page
shoom3301 Jan 9, 2024
35a8474
chore: fix test and lint
shoom3301 Jan 9, 2024
3280aba
Merge branch 'develop' of https://github.com/cowprotocol/cowswap into…
shoom3301 Jan 9, 2024
4589465
Merge branch 'feat/explorer-3' of https://github.com/cowprotocol/cows…
shoom3301 Jan 9, 2024
c0ab231
chore: fix network config
shoom3301 Jan 9, 2024
a9a2f8d
chore: remove irrelevant test
shoom3301 Jan 9, 2024
9020d56
chore: fix lint
shoom3301 Jan 9, 2024
f0228b3
chore: remove outdated files
shoom3301 Jan 9, 2024
5016ff5
Merge branch 'feat/explorer-3' of https://github.com/cowprotocol/cows…
shoom3301 Jan 9, 2024
cd9ed01
chore: fix networkId
shoom3301 Jan 9, 2024
a789b58
chore: test commit
shoom3301 Jan 9, 2024
3048211
chore: test commit
shoom3301 Jan 9, 2024
53c3578
chore: fix dynamic import
shoom3301 Jan 9, 2024
6ddbe9a
Merge branch 'feat/explorer-3' of https://github.com/cowprotocol/cows…
shoom3301 Jan 9, 2024
b65bb36
chore: fix error
shoom3301 Jan 9, 2024
f9734a9
chore: fix main page position
shoom3301 Jan 9, 2024
ef2a10e
chore: add back redirects
shoom3301 Jan 9, 2024
f8244c8
chore: define SUBGRAPH_URLS
shoom3301 Jan 9, 2024
a2d0e42
Merge branch 'develop' into feat/explorer-3
shoom3301 Jan 9, 2024
5ebd311
chore: fix vite build
shoom3301 Jan 9, 2024
6238d49
Merge branch 'feat/explorer-3' of https://github.com/cowprotocol/cows…
shoom3301 Jan 9, 2024
9a35b36
chore: update SUBGRAPH_URLS
shoom3301 Jan 9, 2024
73634c2
Merge branch 'develop' into feat/explorer-3
shoom3301 Jan 9, 2024
74de1c9
Merge branch 'feat/explorer-3' of https://github.com/cowprotocol/cows…
shoom3301 Jan 9, 2024
6079237
Merge branch 'develop' into feat/explorer-3
shoom3301 Jan 9, 2024
095d41e
Merge branch 'develop' into feat/explorer-3
shoom3301 Jan 10, 2024
39fa5ac
Merge branch 'feat/explorer-3' of https://github.com/cowprotocol/cows…
shoom3301 Jan 10, 2024
a101a76
chore: fix vite build
shoom3301 Jan 10, 2024
d715050
fix(explorer): use relevant setllement and vault relayer addresses
shoom3301 Jan 10, 2024
b675754
fix(explorer): fix search redirects
shoom3301 Jan 10, 2024
d3e28ec
fix(explorer): fix multi-network search
shoom3301 Jan 10, 2024
ac77442
Merge branch 'develop' into feat/explorer-3
shoom3301 Jan 10, 2024
300ac60
Merge branch 'feat/explorer-3' of https://github.com/cowprotocol/cows…
shoom3301 Jan 10, 2024
5c47952
chore: fix build
shoom3301 Jan 10, 2024
4083eb0
Merge branch 'develop' of https://github.com/cowprotocol/cowswap into…
shoom3301 Jan 10, 2024
649b606
chore: fix lint
shoom3301 Jan 10, 2024
c092474
Merge branch 'develop' of https://github.com/cowprotocol/cowswap into…
shoom3301 Jan 10, 2024
bb504fa
Update apps/explorer/src/consts/subgraphUrls.ts
shoom3301 Jan 10, 2024
f73e1ad
Merge branch 'develop' into feat/explorer-sepolia-1
shoom3301 Jan 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 16 additions & 20 deletions apps/explorer/src/api/tenderly/tenderlyApi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { APP_NAME, NATIVE_TOKEN_ADDRESS_LOWERCASE, TENDERLY_API_URL } from 'const'
import { Network } from 'types'
import { fetchQuery } from 'api/baseApi'
import {
Account,
Expand All @@ -14,59 +13,56 @@ import {
} from './types'
import { abbreviateString } from 'utils'
import { SPECIAL_ADDRESSES } from '../../explorer/const'
import { mapSupportedNetworks, SupportedChainId } from '@cowprotocol/cow-sdk'

export const ALIAS_TRADER_NAME = 'Trader'
const COW_PROTOCOL_CONTRACT_NAME = 'GPv2Settlement'
const API_BASE_URLs = _urlAvailableNetwork()

function _urlAvailableNetwork(): Partial<Record<Network, string>> {
const urlNetwork = (_networkId: Network): string => `${TENDERLY_API_URL}/${_networkId}`
const API_BASE_URLs: Record<SupportedChainId, string> = mapSupportedNetworks(
(_networkId: SupportedChainId): string => `${TENDERLY_API_URL}/${_networkId}`
alfetopito marked this conversation as resolved.
Show resolved Hide resolved
)

return {
[Network.MAINNET]: urlNetwork(Network.MAINNET),
[Network.GNOSIS_CHAIN]: urlNetwork(Network.GNOSIS_CHAIN),
[Network.GOERLI]: urlNetwork(Network.GOERLI),
}
}

function _getApiBaseUrl(networkId: Network): string {
function _getApiBaseUrl(networkId: SupportedChainId): string {
const baseUrl = API_BASE_URLs[networkId]

if (!baseUrl) {
throw new Error('Unsupported Network. The tenderly API is not available in the Network ' + networkId)
throw new Error('Unsupported Network. The tenderly API is not available in the SupportedChainId ' + networkId)
} else {
return baseUrl
}
}

function _get(networkId: Network, url: string): Promise<Response> {
function _get(networkId: SupportedChainId, url: string): Promise<Response> {
const baseUrl = _getApiBaseUrl(networkId)
return fetch(baseUrl + url)
}

function _fetchTrace(networkId: Network, txHash: string): Promise<Trace> {
function _fetchTrace(networkId: SupportedChainId, txHash: string): Promise<Trace> {
const queryString = `/trace/${txHash}`
console.log(`[tenderlyApi:fetchTrace] Fetching trace tx ${txHash} on network ${networkId}`)

return fetchQuery<Trace>({ get: () => _get(networkId, queryString) }, queryString)
}

function _fetchTradesAccounts(networkId: Network, txHash: string): Promise<Contract[]> {
function _fetchTradesAccounts(networkId: SupportedChainId, txHash: string): Promise<Contract[]> {
const queryString = `/tx/${txHash}/contracts`
console.log(`[tenderlyApi:fetchTradesAccounts] Fetching tx trades account on network ${networkId}`)

return fetchQuery<Array<Contract>>({ get: () => _get(networkId, queryString) }, queryString)
}

export async function getTransactionTrace(networkId: Network, txHash: string): Promise<Trace> {
export async function getTransactionTrace(networkId: SupportedChainId, txHash: string): Promise<Trace> {
return _fetchTrace(networkId, txHash)
}

export async function getTransactionContracts(networkId: Network, txHash: string): Promise<Contract[]> {
export async function getTransactionContracts(networkId: SupportedChainId, txHash: string): Promise<Contract[]> {
return _fetchTradesAccounts(networkId, txHash)
}

export async function getTradesAndTransfers(networkId: Network, txHash: string): Promise<TxTradesAndTransfers> {
export async function getTradesAndTransfers(
networkId: SupportedChainId,
txHash: string
): Promise<TxTradesAndTransfers> {
const trace = await _fetchTrace(networkId, txHash)

return traceToTransfersAndTrades(trace)
Expand Down Expand Up @@ -129,7 +125,7 @@ export function traceToTransfersAndTrades(trace: Trace): TxTradesAndTransfers {
}

export async function getTradesAccount(
networkId: Network,
networkId: SupportedChainId,
txHash: string,
trades: Array<Trade>,
transfers: Array<Transfer>
Expand Down
20 changes: 0 additions & 20 deletions apps/explorer/src/api/web3/hooks.ts

This file was deleted.

31 changes: 16 additions & 15 deletions apps/explorer/src/api/web3/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import Web3 from 'web3'
import { getNetworkFromId } from '@gnosis.pm/dex-js'
import { parseUserAgent } from 'detect-browser'

import { Network } from 'types'

import { ETH_NODE_URL, INFURA_ID } from 'const'
import { SupportedChainId } from '@cowprotocol/cow-sdk'

// TODO connect to mainnet if we need AUTOCONNECT at all
export const getDefaultProvider = (): string | null => (process.env.NODE_ENV === 'test' ? null : ETH_NODE_URL)
Expand Down Expand Up @@ -44,14 +42,21 @@ export function createWeb3Api(provider?: string): Web3 {
return web3
}

function infuraProvider(networkId: Network): string {
const INFURA_NETWORK_NAME_MAP: Record<SupportedChainId, string> = {
[SupportedChainId.MAINNET]: 'Mainnet',
[SupportedChainId.GNOSIS_CHAIN]: 'xDai',
[SupportedChainId.GOERLI]: 'Goerli',
[SupportedChainId.SEPOLIA]: 'Sepolia',
}

function infuraProvider(networkId: SupportedChainId): string {
// INFURA_ID relies on mesa `config` file logic.
// We can be independent of that config by relying on the env var directly
if (!INFURA_ID) {
throw new Error(`INFURA_ID not set`)
}

const network = getNetworkFromId(networkId).toLowerCase()
const network = INFURA_NETWORK_NAME_MAP[networkId]

if (isWebsocketConnection()) {
return `wss://${network}.infura.io/ws/v3/${INFURA_ID}`
Expand Down Expand Up @@ -84,23 +89,19 @@ function isWebsocketConnection(): boolean {
}

// For now only infura provider is available
export function getProviderByNetwork(networkId: Network | null): string | undefined {
switch (networkId) {
case Network.MAINNET:
case Network.GOERLI:
return infuraProvider(networkId)
case Network.GNOSIS_CHAIN:
return 'https://rpc.gnosis.gateway.fm/'
default:
return undefined
export function getProviderByNetwork(networkId: SupportedChainId): string | undefined {
if (networkId === SupportedChainId.GNOSIS_CHAIN) {
return 'https://rpc.gnosis.gateway.fm/'
}

return infuraProvider(networkId)
}

// Approach 2: update the provider in a single web3 instance
// Advantage is that regular APIs that require web3 instance should work without any changes
// Also, there's no change to consumers currently importing from <app>/api module
// Side effect is applied at reducer level (state/network/updater module)
export function updateWeb3Provider(web3: Web3, networkId?: Network | null): void {
export function updateWeb3Provider(web3: Web3, networkId?: SupportedChainId | null): void {
if (!networkId) {
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { COLOURS } from 'styles'
import { media } from 'theme/styles/media'
import { ArrowIconCSS } from 'components/icons/cssIcons'

const { fadedGreyishWhiteOpacity, white, gnosisChainColor, goerliColor } = COLOURS
const { fadedGreyishWhiteOpacity, white, gnosisChainColor, goerliColor, sepoliaColor } = COLOURS

export const SelectorContainer = styled.div`
display: flex;
Expand Down Expand Up @@ -59,6 +59,9 @@ export const Option = styled.div`
&.görli {
background: ${(): string => goerliColor};
}
&.sepolia {
background: ${(): string => sepoliaColor};
}
&.gnosischain {
background: ${(): string => gnosisChainColor};
}
Expand Down Expand Up @@ -90,8 +93,13 @@ export const NetworkLabel = styled.span`
}

&.gnosischain {
background: ${(): string => `rgba(7,121,91,1.00);`};
color: ${(): string => white};
background: rgba(7, 121, 91, 1);
color: white;
}

&.sepolia {
background: rgb(136, 51, 196);
color: white;
}
`

Expand Down
30 changes: 3 additions & 27 deletions apps/explorer/src/components/NetworkSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,18 @@ import { faCheck } from '@fortawesome/free-solid-svg-icons'
import { SelectorContainer, OptionsContainer, Option, NetworkLabel, StyledFAIcon } from './NetworkSelector.styled'
import { replaceURL } from 'utils/url'
import { NO_REDIRECT_HOME_ROUTES } from 'const'
import { Network } from 'types'
import { cleanNetworkName } from 'utils'
import { NETWORK_OPTIONS } from '../../consts/network'

type networkSelectorProps = {
networkId: number
}

type NetworkOptions = {
id: Network
name: string
url: string
}

export const networkOptions: NetworkOptions[] = [
{
id: Network.MAINNET,
name: 'Ethereum',
url: '',
},
{
id: Network.GNOSIS_CHAIN,
name: 'Gnosis Chain',
url: 'gc',
},
{
id: Network.GOERLI,
name: 'Görli',
url: 'goerli',
},
]

export const NetworkSelector: React.FC<networkSelectorProps> = ({ networkId }) => {
const selectContainer = useRef<HTMLInputElement>(null)
const navigate = useNavigate()
const location = useLocation()
const name = networkOptions.find((network) => network.id === networkId)?.name.toLowerCase()
const name = NETWORK_OPTIONS.find((network) => network.id === networkId)?.name.toLowerCase()
const [open, setOpen] = useState(false)

useEffect(() => {
Expand Down Expand Up @@ -77,7 +53,7 @@ export const NetworkSelector: React.FC<networkSelectorProps> = ({ networkId }) =
<span className={`arrow ${open && 'open'}`} />
{open && (
<OptionsContainer width={selectContainer.current?.offsetWidth || 0}>
{networkOptions.map((network) => (
{NETWORK_OPTIONS.map((network) => (
<Option onClick={(): void => redirectToNetwork(network.url, networkId)} key={network.id}>
<div className={`dot ${cleanNetworkName(network.name)} `} />
<div className={`name ${network.id === networkId && 'selected'}`}>{network.name}</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/explorer/src/components/RedirectToSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const RedirectToSearch: React.FC<RedirectToSearchParams> = ({ from, data }) => {
const newPath =
pathMatchArray && pathMatchArray.length > 0 ? `${prefixPath}/search${pathMatchArray[1]}` : `${prefixPath}${suffix}`

return <Navigate to={newPath} />
return <Navigate to={newPath} state={{ referrer: from, data }} />
}

export default RedirectToSearch
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react'
import styled from 'styled-components'
import { media } from 'theme/styles/media'
import { getGpV2ContractAddress } from 'utils/contract'

// Components
import LogoWrapper, { LOGO_MAP } from 'components/common/LogoWrapper'
Expand All @@ -12,6 +11,7 @@ import { useNetworkId } from 'state/network'
import { footerConfig } from '../Footer/config'
import { Network } from 'types'
import { BlockExplorerLink } from 'components/common/BlockExplorerLink'
import { COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS, COW_PROTOCOL_VAULT_RELAYER_ADDRESS } from '@cowprotocol/cow-sdk'

const FooterStyled = styled.footer`
display: flex;
Expand Down Expand Up @@ -120,8 +120,9 @@ export interface FooterType {
export const Footer: React.FC<FooterType> = (props) => {
const { isBeta = footerConfig.isBeta, url = footerConfig.url } = props
const networkId = useNetworkId() || Network.MAINNET
const settlementContractAddress = getGpV2ContractAddress(networkId, 'GPv2Settlement')
const vaultRelayerContractAddress = getGpV2ContractAddress(networkId, 'GPv2VaultRelayer')
const settlementContractAddress = COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS[networkId]
const vaultRelayerContractAddress = COW_PROTOCOL_VAULT_RELAYER_ADDRESS[networkId]

return (
<FooterStyled>
{isBeta && <BetaWrapper>This project is in beta. Use at your own risk.</BetaWrapper>}
Expand Down
29 changes: 29 additions & 0 deletions apps/explorer/src/consts/network.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from '@cowprotocol/cow-sdk'

export type NetworkOptions = {
id: SupportedChainId
name: string
url: string
}

export const CHAIN_ID_TO_NAME: Record<SupportedChainId, string> = {
[SupportedChainId.MAINNET]: 'Ethereum',
[SupportedChainId.GNOSIS_CHAIN]: 'Gnosis Chain',
[SupportedChainId.GOERLI]: 'Görli',
[SupportedChainId.SEPOLIA]: 'Sepolia',
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this a duplication?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find other similar configs


export const CHAIN_ID_TO_URL_PREFIX: Record<SupportedChainId, string> = {
[SupportedChainId.MAINNET]: '',
[SupportedChainId.GNOSIS_CHAIN]: 'gc',
[SupportedChainId.GOERLI]: 'goerli',
[SupportedChainId.SEPOLIA]: 'sepolia',
}

export const NETWORK_PREFIXES = Object.values(CHAIN_ID_TO_URL_PREFIX).filter(Boolean).join('|')

export const NETWORK_OPTIONS: NetworkOptions[] = ALL_SUPPORTED_CHAIN_IDS.map((chainId) => ({
id: chainId,
name: CHAIN_ID_TO_NAME[chainId],
url: CHAIN_ID_TO_URL_PREFIX[chainId],
}))
15 changes: 15 additions & 0 deletions apps/explorer/src/consts/subgraphUrls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { SUBGRAPH_PROD_CONFIG, SupportedChainId } from '@cowprotocol/cow-sdk'

function getSubgraphUrl(chainId: SupportedChainId, suffix: string): string | null {
return process.env[`REACT_APP_SUBGRAPH_URL_${suffix}`] || SUBGRAPH_PROD_CONFIG[chainId] || null
}

/**
* When value is null, then Subgraph is not supported for the network
*/
export const SUBGRAPH_URLS: Record<SupportedChainId, string | null> = {
[SupportedChainId.MAINNET]: getSubgraphUrl(SupportedChainId.MAINNET, 'MAINNET'),
[SupportedChainId.GNOSIS_CHAIN]: getSubgraphUrl(SupportedChainId.GNOSIS_CHAIN, 'GNOSIS_CHAIN'),
[SupportedChainId.GOERLI]: getSubgraphUrl(SupportedChainId.GOERLI, 'GOERLI'),
[SupportedChainId.SEPOLIA]: getSubgraphUrl(SupportedChainId.SEPOLIA, 'SEPOLIA'),
}
24 changes: 5 additions & 19 deletions apps/explorer/src/cowSdk.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,9 @@
import { OrderBookApi, SupportedChainId } from '@cowprotocol/cow-sdk'
import { SubgraphApi } from '@cowprotocol/cow-sdk'
import { OrderBookApi, SubgraphApi } from '@cowprotocol/cow-sdk'
import { MetadataApi } from '@cowprotocol/app-data'
import { SUBGRAPH_PROD_CONFIG } from '@cowprotocol/cow-sdk'

function getSubgraphUrls(): Record<SupportedChainId, string> {
const [mainnetUrl, gcUrl, goerliUrl] = [
process.env.REACT_APP_SUBGRAPH_URL_MAINNET || undefined,
process.env.REACT_APP_SUBGRAPH_URL_GNOSIS_CHAIN || undefined,
process.env.REACT_APP_SUBGRAPH_URL_GOERLI || undefined,
]

return {
...SUBGRAPH_PROD_CONFIG,
...(mainnetUrl ? { [SupportedChainId.MAINNET]: mainnetUrl } : undefined),
...(gcUrl ? { [SupportedChainId.GNOSIS_CHAIN]: gcUrl } : undefined),
...(goerliUrl ? { [SupportedChainId.GOERLI]: goerliUrl } : undefined),
}
}
import { SUBGRAPH_URLS } from './consts/subgraphUrls'

export const orderBookSDK = new OrderBookApi({ env: 'prod' })
export const subgraphApiSDK = new SubgraphApi({ baseUrls: getSubgraphUrls() })
export const subgraphApiSDK = new SubgraphApi({
baseUrls: SUBGRAPH_URLS,
})
export const metadataApiSDK = new MetadataApi()
Loading
Loading