mirror of
https://github.com/oceanprotocol/status
synced 2024-11-21 17:26:59 +01:00
get more network info from RPC
This commit is contained in:
parent
9c129af275
commit
2bb562dd7a
783
package-lock.json
generated
783
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@ -8,7 +8,7 @@
|
||||
"build": "next build",
|
||||
"serve": "next start",
|
||||
"analyze": "ANALYZE=true next build",
|
||||
"test": "npm run lint && NODE_ENV=test jest",
|
||||
"test": "npm run lint && NODE_ENV=test jest --silent",
|
||||
"test:watch": "npm run lint && NODE_ENV=test jest --watch",
|
||||
"lint": "eslint --ignore-path .gitignore --ext .js .",
|
||||
"format": "prettier --ignore-path .gitignore **/**/*.{css,yml,js,jsx,ts,tsx,json} --write"
|
||||
@ -19,22 +19,24 @@
|
||||
"@oceanprotocol/typographies": "^0.1.0",
|
||||
"@zeit/next-css": "^1.0.1",
|
||||
"axios": "^0.19.0",
|
||||
"next": "9.1.4",
|
||||
"ethereumjs-units": "^0.2.0",
|
||||
"next": "^9.1.5",
|
||||
"next-seo": "^3.1.0",
|
||||
"next-svgr": "^0.0.2",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "^9.1.4",
|
||||
"@next/bundle-analyzer": "^9.1.5",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/react": "^9.3.3",
|
||||
"@types/bn.js": "^4.11.5",
|
||||
"@types/jest": "^24.0.23",
|
||||
"@types/next-seo": "^1.10.0",
|
||||
"@types/node": "^12.12.14",
|
||||
"@types/react": "^16.9.15",
|
||||
"@typescript-eslint/eslint-plugin": "^2.10.0",
|
||||
"@typescript-eslint/parser": "^2.10.0",
|
||||
"@types/node": "^12.12.17",
|
||||
"@types/react": "^16.9.16",
|
||||
"@typescript-eslint/eslint-plugin": "^2.11.0",
|
||||
"@typescript-eslint/parser": "^2.11.0",
|
||||
"cssnano": "^4.1.10",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-config-oceanprotocol": "^1.5.0",
|
||||
|
1
src/@types/node_modules.d.ts
vendored
Normal file
1
src/@types/node_modules.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module 'ethereumjs-units'
|
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
import Layout from '../Layout'
|
||||
import Layout from './Layout'
|
||||
|
||||
describe('Layout', () => {
|
||||
it('renders without crashing', () => {
|
@ -1,68 +0,0 @@
|
||||
import React from 'react'
|
||||
import { render, wait, waitForElement } from '@testing-library/react'
|
||||
import axios from 'axios'
|
||||
import Network from '../components/Network'
|
||||
|
||||
import { mocked } from 'ts-jest/dist/util/testing'
|
||||
|
||||
jest.mock('axios')
|
||||
const axiosMock: any = mocked(axios)
|
||||
|
||||
const mockResponse = {
|
||||
status: 200,
|
||||
duration: 1000,
|
||||
data: { result: '0x345' }
|
||||
}
|
||||
|
||||
const network = {
|
||||
name: 'Pacific',
|
||||
project: 'Ocean Protocol',
|
||||
type: 'mainnet',
|
||||
networkId: '0xCEA11',
|
||||
rpcUrl: 'https://pacific.oceanprotocol.com',
|
||||
explorerUrl: 'https://submarine.oceanprotocol.com'
|
||||
}
|
||||
|
||||
const networkNoRpc = {
|
||||
name: 'Pacific',
|
||||
project: 'Ocean Protocol',
|
||||
type: 'mainnet',
|
||||
networkId: '0xCEA11',
|
||||
explorerUrl: 'https://submarine.oceanprotocol.com'
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllTimers()
|
||||
})
|
||||
|
||||
describe('Network', () => {
|
||||
it('renders without crashing', async () => {
|
||||
axiosMock.post.mockResolvedValue(mockResponse)
|
||||
const { container, rerender, getByText } = render(
|
||||
<Network network={network} />
|
||||
)
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
await waitForElement(() => getByText('Online'))
|
||||
expect(axiosMock.post).toHaveBeenCalledTimes(2)
|
||||
|
||||
rerender(<Network network={networkNoRpc} />)
|
||||
await waitForElement(() => getByText('Online'))
|
||||
expect(axiosMock.post).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('renders without response', async () => {
|
||||
axiosMock.post.mockResolvedValue(undefined)
|
||||
const { container } = render(<Network network={network} />)
|
||||
await wait()
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('re-fetches after 5 sec.', async () => {
|
||||
jest.useFakeTimers()
|
||||
axiosMock.post.mockResolvedValue(mockResponse)
|
||||
const { getByText } = render(<Network network={network} />)
|
||||
jest.advanceTimersByTime(6000)
|
||||
await waitForElement(() => getByText('Online'))
|
||||
// expect(setInterval).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
18
src/components/Network/Info.module.css
Normal file
18
src/components/Network/Info.module.css
Normal file
@ -0,0 +1,18 @@
|
||||
.moreInfo {
|
||||
font-size: var(--font-size-mini);
|
||||
}
|
||||
|
||||
.moreInfo p {
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.moreInfo span {
|
||||
color: var(--brand-grey-lighter);
|
||||
}
|
||||
|
||||
.clientVersion {
|
||||
margin-top: var(--spacer);
|
||||
font-size: var(--font-size-mini);
|
||||
}
|
35
src/components/Network/Info.tsx
Normal file
35
src/components/Network/Info.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import React from 'react'
|
||||
import styles from './Info.module.css'
|
||||
|
||||
export default function Info({
|
||||
gasPrice,
|
||||
gasLimit,
|
||||
clientVersion,
|
||||
peers
|
||||
}: {
|
||||
gasPrice: string
|
||||
gasLimit: string
|
||||
clientVersion: string
|
||||
peers: number
|
||||
}) {
|
||||
return (
|
||||
<div className={styles.moreInfo}>
|
||||
{peers && (
|
||||
<p>
|
||||
Connected Peers <span>{peers}</span>
|
||||
</p>
|
||||
)}
|
||||
{gasLimit && (
|
||||
<p>
|
||||
Gas Limit <span>{gasLimit} gas</span>
|
||||
</p>
|
||||
)}
|
||||
{gasPrice && (
|
||||
<p>
|
||||
Gas Prize <span>{gasPrice} Gwei</span>
|
||||
</p>
|
||||
)}
|
||||
{clientVersion && <p className={styles.clientVersion}>{clientVersion}</p>}
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,14 +1,22 @@
|
||||
.networkData {
|
||||
min-height: 66px;
|
||||
min-height: 105px;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: var(--font-size-large);
|
||||
margin-bottom: calc(var(--spacer) / 8);
|
||||
}
|
||||
|
||||
.block {
|
||||
margin-bottom: var(--spacer);
|
||||
}
|
||||
|
||||
.block a {
|
||||
color: var(--brand-grey-lighter);
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: var(--font-size-large);
|
||||
margin-bottom: 0;
|
||||
.block a:hover {
|
||||
color: var(--brand-pink);
|
||||
}
|
||||
|
||||
.success {
|
||||
@ -24,7 +32,3 @@
|
||||
margin-left: calc(var(--spacer) / 4);
|
||||
font-size: var(--font-size-mini);
|
||||
}
|
||||
|
||||
.clientVersion {
|
||||
font-size: var(--font-size-mini);
|
||||
}
|
@ -1,25 +1,37 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { fetchRpc, AxiosResponseCustom } from '../../rpc'
|
||||
import { fetchRpc } from '../../rpc'
|
||||
import Spinner from '../Spinner'
|
||||
import { NetworkProps } from '.'
|
||||
import styles from './Data.module.css'
|
||||
import styles from './Status.module.css'
|
||||
import Info from './Info'
|
||||
import { getClientVersion, getGasPrize, getPeers } from './utils'
|
||||
|
||||
export default function Data({ network }: { network: NetworkProps }) {
|
||||
export default function Status({ network }: { network: NetworkProps }) {
|
||||
const { rpcUrl, explorerUrl } = network
|
||||
const [status, setStatus] = useState('')
|
||||
const [block, setBlock] = useState(0)
|
||||
const [latency, setLatency] = useState(0)
|
||||
const [gasLimit, setGasLimit] = useState()
|
||||
const [clientVersion, setClientVersion] = useState('')
|
||||
const [gasPrice, setGasPrice] = useState()
|
||||
const [peers, setPeers] = useState()
|
||||
|
||||
async function getStatusAndBlock() {
|
||||
if (!rpcUrl) return
|
||||
|
||||
const response: AxiosResponseCustom = await fetchRpc(
|
||||
rpcUrl,
|
||||
'eth_blockNumber'
|
||||
)
|
||||
const response = await fetchRpc(rpcUrl, 'eth_getBlockByNumber', [
|
||||
'latest',
|
||||
true
|
||||
])
|
||||
|
||||
if (!response || response.status !== 200) {
|
||||
const responseParity = await fetchRpc(rpcUrl, 'parity_mode')
|
||||
|
||||
if (
|
||||
!response ||
|
||||
!response.data ||
|
||||
response.status !== 200 ||
|
||||
responseParity.data.result !== 'active'
|
||||
) {
|
||||
setStatus('Offline')
|
||||
return
|
||||
}
|
||||
@ -27,29 +39,26 @@ export default function Data({ network }: { network: NetworkProps }) {
|
||||
setStatus('Online')
|
||||
response.duration && setLatency(response.duration)
|
||||
|
||||
const blockNumber =
|
||||
response && response.data && parseInt(response.data.result, 16)
|
||||
setBlock(blockNumber)
|
||||
const { number, gasLimit } = response.data.result
|
||||
setBlock(parseInt(number, 16))
|
||||
setGasLimit(parseInt(gasLimit, 16))
|
||||
}
|
||||
|
||||
async function getClientVersion() {
|
||||
async function getData() {
|
||||
getStatusAndBlock()
|
||||
|
||||
if (!rpcUrl) return
|
||||
|
||||
const response: AxiosResponseCustom = await fetchRpc(
|
||||
rpcUrl,
|
||||
'web3_clientVersion'
|
||||
)
|
||||
|
||||
response && response.data && setClientVersion(response.data.result)
|
||||
setClientVersion(await getClientVersion(rpcUrl))
|
||||
setGasPrice(await getGasPrize(rpcUrl))
|
||||
setPeers(await getPeers(rpcUrl))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getStatusAndBlock()
|
||||
getClientVersion()
|
||||
getData()
|
||||
|
||||
const timer = setInterval(() => {
|
||||
getStatusAndBlock()
|
||||
getClientVersion()
|
||||
getData()
|
||||
}, 5000) // run every 5 sec.
|
||||
return () => {
|
||||
clearInterval(timer)
|
||||
@ -77,9 +86,13 @@ export default function Data({ network }: { network: NetworkProps }) {
|
||||
At block #<a href={`${explorerUrl}/blocks/${block}`}>{block}</a>
|
||||
</p>
|
||||
)}
|
||||
{clientVersion && (
|
||||
<p className={styles.clientVersion}>{clientVersion}</p>
|
||||
)}
|
||||
|
||||
<Info
|
||||
gasPrice={gasPrice}
|
||||
gasLimit={gasLimit}
|
||||
clientVersion={clientVersion}
|
||||
peers={peers}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Spinner />
|
33
src/components/Network/index.test.tsx
Normal file
33
src/components/Network/index.test.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import React from 'react'
|
||||
import { render, waitForElement } from '@testing-library/react'
|
||||
import Network from '.'
|
||||
|
||||
const network = {
|
||||
name: 'Pacific',
|
||||
project: 'Ocean Protocol',
|
||||
type: 'mainnet',
|
||||
networkId: '0xCEA11',
|
||||
rpcUrl: 'https://pacific.oceanprotocol.com',
|
||||
explorerUrl: 'https://submarine.oceanprotocol.com'
|
||||
}
|
||||
|
||||
const networkNoRpc = {
|
||||
name: 'Pacific',
|
||||
project: 'Ocean Protocol',
|
||||
type: 'mainnet',
|
||||
networkId: '0xCEA11',
|
||||
explorerUrl: 'https://submarine.oceanprotocol.com'
|
||||
}
|
||||
|
||||
describe('Network', () => {
|
||||
it('renders without crashing', async () => {
|
||||
const { container, rerender, getByText } = render(
|
||||
<Network network={network} />
|
||||
)
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
await waitForElement(() => getByText('Online' || 'Offline'))
|
||||
|
||||
rerender(<Network network={networkNoRpc} />)
|
||||
await waitForElement(() => getByText('Online' || 'Offline'))
|
||||
})
|
||||
})
|
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import styles from './index.module.css'
|
||||
import Data from './Data'
|
||||
import Status from './Status'
|
||||
|
||||
export interface NetworkProps {
|
||||
name: string
|
||||
@ -26,7 +26,7 @@ export default function Network({ network }: { network: NetworkProps }) {
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<Data network={network} />
|
||||
<Status network={network} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
63
src/components/Network/utils.test.ts
Normal file
63
src/components/Network/utils.test.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import axios from 'axios'
|
||||
import { convert } from 'ethereumjs-units'
|
||||
import { mocked } from 'ts-jest/dist/util/testing'
|
||||
import { getGasPrize, getClientVersion, getPeers } from './utils'
|
||||
|
||||
jest.mock('axios')
|
||||
const axiosMock: any = mocked(axios)
|
||||
|
||||
describe('Network Utils', () => {
|
||||
it('getGasPrize', async () => {
|
||||
const response = {
|
||||
status: 200,
|
||||
duration: 1000,
|
||||
data: { result: '0x345' }
|
||||
}
|
||||
|
||||
axiosMock.post.mockImplementationOnce(() => Promise.resolve(response))
|
||||
await expect(getGasPrize('http://rpc.com')).resolves.toEqual(
|
||||
convert(parseInt(response.data.result, 16), 'wei', 'gwei')
|
||||
)
|
||||
})
|
||||
|
||||
it('getGasPrize: Error', async () => {
|
||||
axiosMock.post.mockImplementationOnce(() => Promise.resolve(null))
|
||||
await expect(getGasPrize('http://rpc.com')).resolves.toEqual(null)
|
||||
})
|
||||
|
||||
it('getClientVersion', async () => {
|
||||
const response = {
|
||||
status: 200,
|
||||
duration: 1000,
|
||||
data: { result: '0x345' }
|
||||
}
|
||||
|
||||
axiosMock.post.mockImplementationOnce(() => Promise.resolve(response))
|
||||
await expect(getClientVersion('http://rpc.com')).resolves.toEqual(
|
||||
response.data.result
|
||||
)
|
||||
})
|
||||
|
||||
it('getClientVersion: Error', async () => {
|
||||
axiosMock.post.mockImplementationOnce(() => Promise.resolve(null))
|
||||
await expect(getClientVersion('http://rpc.com')).resolves.toEqual(null)
|
||||
})
|
||||
|
||||
it('getPeers', async () => {
|
||||
const response = {
|
||||
status: 200,
|
||||
duration: 1000,
|
||||
data: { result: '0x24' }
|
||||
}
|
||||
|
||||
axiosMock.post.mockImplementationOnce(() => Promise.resolve(response))
|
||||
await expect(getPeers('http://rpc.com')).resolves.toEqual(
|
||||
parseInt(response.data.result, 16)
|
||||
)
|
||||
})
|
||||
|
||||
it('getPeers: Error', async () => {
|
||||
axiosMock.post.mockImplementationOnce(() => Promise.resolve(null))
|
||||
await expect(getPeers('http://rpc.com')).resolves.toEqual(null)
|
||||
})
|
||||
})
|
40
src/components/Network/utils.ts
Normal file
40
src/components/Network/utils.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { convert } from 'ethereumjs-units'
|
||||
import { fetchRpc, AxiosResponseCustom } from '../../rpc'
|
||||
|
||||
export async function getGasPrize(rpcUrl: string) {
|
||||
try {
|
||||
const response: AxiosResponseCustom = await fetchRpc(rpcUrl, 'eth_gasPrice')
|
||||
return convert(parseInt(response.data.result, 16), 'wei', 'gwei')
|
||||
} catch (error) {
|
||||
console.log(error.message)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export async function getClientVersion(rpcUrl: string) {
|
||||
try {
|
||||
const response: AxiosResponseCustom = await fetchRpc(
|
||||
rpcUrl,
|
||||
'web3_clientVersion'
|
||||
)
|
||||
|
||||
return response.data.result
|
||||
} catch (error) {
|
||||
console.log(error.message)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPeers(rpcUrl: string) {
|
||||
try {
|
||||
const response: AxiosResponseCustom = await fetchRpc(
|
||||
rpcUrl,
|
||||
'net_peerCount'
|
||||
)
|
||||
|
||||
return parseInt(response.data.result, 16)
|
||||
} catch (error) {
|
||||
console.log(error.message)
|
||||
return null
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
.spinner {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
margin-top: calc(var(--spacer) * 2);
|
||||
margin-top: calc(var(--spacer) * 3);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
import Spinner from '../components/Spinner'
|
||||
import Spinner from './Spinner'
|
||||
|
||||
describe('Spinner', () => {
|
||||
it('renders without crashing', () => {
|
@ -12,11 +12,11 @@ export interface AxiosResponseCustom extends AxiosResponse {
|
||||
config: AxiosRequestConfigCustom
|
||||
}
|
||||
|
||||
async function fetchRpc(url: string, method: string) {
|
||||
async function fetchRpc(url: string, method: string, params?: any[]) {
|
||||
try {
|
||||
const response = await axios.post(url, {
|
||||
const response: AxiosResponseCustom = await axios.post(url, {
|
||||
method,
|
||||
params: [],
|
||||
params,
|
||||
id: 1,
|
||||
jsonrpc: '2.0'
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user