diff --git a/nginx/conf.d/default.conf b/nginx/conf.d/default.conf index 4fdf9a1..2866389 100644 --- a/nginx/conf.d/default.conf +++ b/nginx/conf.d/default.conf @@ -113,6 +113,40 @@ server { } } + location /chains { + root /usr/share/nginx/html; + expires 30d; + + # Base on: https://michielkalkman.com/snippets/nginx-cors-open-configuration/ + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' '*'; + # + # Om nom nom cookies + # + add_header 'Access-Control-Allow-Credentials' 'true'; + add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS'; + + # + # Custom headers and headers various browsers *should* be OK with but aren't + # + add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; + + # + # Tell client that this pre-flight info is valid for 20 days + # + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } + if ($request_method = 'GET') { + add_header 'Access-Control-Allow-Origin' '*' always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always; + add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always; + } + } + location / { root /usr/share/nginx/html; index index.html; diff --git a/package.json b/package.json index 505e22a..6ba5274 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,8 @@ "test": "craco test", "eject": "react-scripts eject", "source-map-explorer": "source-map-explorer build/static/js/*.js", - "assets-start": "docker run --rm -p 3001:80 --name otterscan-assets -d -v$(pwd)/4bytes/signatures:/usr/share/nginx/html/signatures/ -v$(pwd)/trustwallet/blockchains/ethereum/assets:/usr/share/nginx/html/assets/1 -v$(pwd)/topic0/with_parameter_names:/usr/share/nginx/html/topic0/ -v$(pwd)/nginx/nginx.conf:/etc/nginx/nginx.conf -v$(pwd)/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf nginx:1.21.1-alpine", - "assets-start-with-param-names": "docker run --rm -p 3001:80 --name otterscan-assets -d -v$(pwd)/4bytes/with_parameter_names:/usr/share/nginx/html/signatures/ -v$(pwd)/trustwallet/blockchains/ethereum/assets:/usr/share/nginx/html/assets/1 -v$(pwd)/topic0/with_parameter_names:/usr/share/nginx/html/topic0/ -v$(pwd)/nginx/nginx.conf:/etc/nginx/nginx.conf -v$(pwd)/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf nginx:1.21.1-alpine", + "assets-start": "docker run --rm -p 3001:80 --name otterscan-assets -d -v$(pwd)/4bytes/signatures:/usr/share/nginx/html/signatures/ -v$(pwd)/trustwallet/blockchains/ethereum/assets:/usr/share/nginx/html/assets/1 -v$(pwd)/topic0/with_parameter_names:/usr/share/nginx/html/topic0/ -v$(pwd)/chains/_data/chains:/usr/share/nginx/html/chains/ -v$(pwd)/nginx/nginx.conf:/etc/nginx/nginx.conf -v$(pwd)/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf nginx:1.21.1-alpine", + "assets-start-with-param-names": "docker run --rm -p 3001:80 --name otterscan-assets -d -v$(pwd)/4bytes/with_parameter_names:/usr/share/nginx/html/signatures/ -v$(pwd)/trustwallet/blockchains/ethereum/assets:/usr/share/nginx/html/assets/1 -v$(pwd)/topic0/with_parameter_names:/usr/share/nginx/html/topic0/ -v$(pwd)/chains/_data/chains:/usr/share/nginx/html/chains/ -v$(pwd)/nginx/nginx.conf:/etc/nginx/nginx.conf -v$(pwd)/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf nginx:1.21.1-alpine", "assets-stop": "docker stop otterscan-assets", "docker-build": "DOCKER_BUILDKIT=1 docker build -t otterscan -f Dockerfile .", "docker-start": "docker run --rm -p 5000:80 --name otterscan -d otterscan", diff --git a/src/App.tsx b/src/App.tsx index e9c3f20..81116d7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,7 +7,7 @@ import ConnectionErrorPanel from "./ConnectionErrorPanel"; import Footer from "./Footer"; import { ConnectionStatus } from "./types"; import { RuntimeContext, useRuntime } from "./useRuntime"; -import { ChainInfoContext, defaultChainInfo } from "./useChainInfo"; +import { ChainInfoContext, useChainInfoFromMetadataFile } from "./useChainInfo"; const Block = React.lazy( () => import(/* webpackChunkName: "block", webpackPrefetch: true */ "./Block") @@ -41,17 +41,19 @@ const PageNotFound = React.lazy( const App = () => { const runtime = useRuntime(); + const chainInfo = useChainInfoFromMetadataFile(runtime); return ( - {runtime.connStatus !== ConnectionStatus.CONNECTED ? ( + {runtime.connStatus !== ConnectionStatus.CONNECTED || + chainInfo === undefined ? ( ) : ( - +
diff --git a/src/url.ts b/src/url.ts index a67b708..6a7a411 100644 --- a/src/url.ts +++ b/src/url.ts @@ -15,6 +15,11 @@ export const tokenLogoURL = ( address: string ): string => `${assetsURLPrefix}/assets/${chainId}/${address}/logo.png`; +export const chainInfoURL = ( + assetsURLPrefix: string, + chainId: number +): string => `${assetsURLPrefix}/chains/eip155-${chainId}.json`; + export const blockURL = (blockNum: BlockTag) => `/block/${blockNum}`; export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`; diff --git a/src/useChainInfo.ts b/src/useChainInfo.ts index f1de917..0d494b9 100644 --- a/src/useChainInfo.ts +++ b/src/useChainInfo.ts @@ -1,4 +1,6 @@ -import { createContext, useContext } from "react"; +import { createContext, useContext, useEffect, useState } from "react"; +import { chainInfoURL } from "./url"; +import { OtterscanRuntime } from "./useRuntime"; export type ChainInfo = { nativeName: string; @@ -14,6 +16,40 @@ export const defaultChainInfo: ChainInfo = { export const ChainInfoContext = createContext(undefined); +export const useChainInfoFromMetadataFile = ( + runtime: OtterscanRuntime | undefined +): ChainInfo | undefined => { + const assetsURLPrefix = runtime?.config?.assetsURLPrefix; + const chainId = runtime?.provider?.network.chainId; + + const [chainInfo, setChainInfo] = useState(undefined); + + useEffect(() => { + if (chainId === undefined) { + setChainInfo(undefined); + return; + } + + const readChainInfo = async () => { + const res = await fetch(chainInfoURL(assetsURLPrefix!, chainId)); + if (!res.ok) { + setChainInfo(defaultChainInfo); + return; + } + const info = await res.json(); + + setChainInfo({ + nativeName: info.nativeCurrency.name, + nativeDecimals: info.nativeCurrency.decimals, + nativeSymbol: info.nativeCurrency.symbol, + }); + }; + readChainInfo(); + }, [assetsURLPrefix, chainId]); + + return chainInfo; +}; + export const useChainInfo = (): ChainInfo => { const chainInfo = useContext(ChainInfoContext); if (chainInfo === undefined) {