add block explorer

This commit is contained in:
Dexter 2022-02-15 09:01:36 +00:00 committed by Dexter Edwards
parent 726bcaf579
commit b41b97556f
78 changed files with 14306 additions and 0 deletions

25
apps/block-explorer/.env Normal file
View File

@ -0,0 +1,25 @@
# React Environment Variables
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
# Netlify Environment Variables
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
REACT_APP_VERSION=$npm_package_version
REACT_APP_REPOSITORY_URL=$REPOSITORY_URL
REACT_APP_BRANCH=$BRANCH
REACT_APP_PULL_REQUEST=$PULL_REQUEST
REACT_APP_HEAD=$HEAD
REACT_APP_COMMIT_REF=$COMMIT_REF
REACT_APP_CONTEXT=$CONTEXT
REACT_APP_REVIEW_ID=$REVIEW_ID
REACT_APP_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
REACT_APP_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
REACT_APP_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
REACT_APP_URL=$URL
REACT_APP_DEPLOY_URL=$DEPLOY_URL
REACT_APP_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
# App configuration variables
REACT_APP_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
REACT_APP_TENDERMINT_URL = "https://lb.testnet.vega.xyz/tm"
REACT_APP_TENDERMINT_WEBSOCKET_URL = "wss://lb.testnet.vega.xyz/tm/websocket"
REACT_APP_VEGA_URL = "https://lb.testnet.vega.xyz/query"

23
apps/block-explorer/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@ -0,0 +1,46 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.\
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).

View File

@ -0,0 +1,8 @@
module.exports = {
client: {
service: {
name: "vega",
url: process.env.REACT_APP_VEGA_URL,
},
},
};

View File

@ -0,0 +1,4 @@
[[redirects]]
from = "/*"
to = "/index.html"
status = 200

View File

@ -0,0 +1,57 @@
{
"name": "vega-explorer",
"version": "0.1.0",
"private": true,
"dependencies": {
"@apollo/client": "^3.5.8",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.4.0",
"@types/node": "^16.11.22",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"graphql": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.1",
"react-scripts": "5.0.0",
"react-use-websocket": "^3.0.0",
"sass": "^1.49.7",
"typescript": "^4.5.5",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"generate": "env-cmd yarn apollo codegen:generate --target=typescript --globalTypesFile=src/__generated__/globalTypes.ts"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/react-router-dom": "^5.3.3",
"apollo": "^2.33.9",
"env-cmd": "^10.1.0"
},
"resolutions": {
"graphql": "15.8.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@ -0,0 +1,73 @@
@import "./styles/colors";
@import "./styles/fonts";
@import "./styles/reset";
html,
body,
#root {
background-color: $black;
color: $white;
font-family: $font-main;
height: 100%;
margin: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.app {
max-width: 1300px;
margin: 0 auto;
display: grid;
grid-template-rows: 1fr min-content;
min-height: 100%;
@media (min-width: 960px) {
border-left: 1px solid $white;
border-right: 1px solid $white;
}
}
.template-sidebar {
border-bottom: 1px solid $white;
border-top: 1px solid $white;
display: grid;
grid-template-rows: auto minmax(700px, 1fr);
grid-template-columns: 300px minmax(auto, 1fr);
nav {
border-right: 1px solid $white;
padding: 20px;
grid-column-start: 1;
grid-column-end: 1;
grid-row-start: 1;
grid-row-end: 3;
overflow: hidden;
}
header {
padding: 20px;
border-bottom: 1px solid $white;
grid-column-start: 2;
grid-column-end: 2;
grid-row-start: 1;
grid-row-end: 2;
}
main {
padding: 20px;
grid-column-start: 2;
grid-column-end: 2;
grid-row-start: 2;
grid-row-end: 3;
overflow: hidden;
}
footer {
border-top: 1px solid $white;
padding: 20px;
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 3;
grid-row-end: 4;
}
}

View File

@ -0,0 +1,9 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@ -0,0 +1,35 @@
import "./App.scss";
import { ApolloProvider } from "@apollo/client";
import { createClient } from "./lib/apollo-client";
import { BrowserRouter as Router } from "react-router-dom";
import { Nav } from "./components/nav";
import { Footer } from "./components/footer";
import { Header } from "./components/header";
import { Main } from "./components/main";
import React from "react";
import { DATA_SOURCES } from "./config";
import { TendermintWebsocketProvider } from "./contexts/websocket/tendermint-websocket-provider";
function App() {
const [client] = React.useState(createClient(DATA_SOURCES.dataNodeUrl));
return (
<Router>
<TendermintWebsocketProvider>
<ApolloProvider client={client}>
<div className="app">
<div className="template-sidebar">
<Nav />
<Header />
<Main />
<Footer />
</div>
</div>
</ApolloProvider>
</TendermintWebsocketProvider>
</Router>
);
}
export default App;

View File

@ -0,0 +1,126 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
//==============================================================
// START Enums and Input Objects
//==============================================================
/**
* The various account types we have (used by collateral)
*/
export enum AccountType {
Bond = "Bond",
FeeInfrastructure = "FeeInfrastructure",
FeeLiquidity = "FeeLiquidity",
General = "General",
GlobalInsurance = "GlobalInsurance",
Insurance = "Insurance",
LockWithdraw = "LockWithdraw",
Margin = "Margin",
Settlement = "Settlement",
}
export enum AuctionTrigger {
Batch = "Batch",
Liquidity = "Liquidity",
Opening = "Opening",
Price = "Price",
Unspecified = "Unspecified",
}
/**
* The current state of a market
*/
export enum MarketState {
Active = "Active",
Cancelled = "Cancelled",
Closed = "Closed",
Pending = "Pending",
Proposed = "Proposed",
Rejected = "Rejected",
Settled = "Settled",
Suspended = "Suspended",
TradingTerminated = "TradingTerminated",
}
/**
* What market trading mode are we in
*/
export enum MarketTradingMode {
BatchAuction = "BatchAuction",
Continuous = "Continuous",
MonitoringAuction = "MonitoringAuction",
OpeningAuction = "OpeningAuction",
}
export enum NodeStatus {
NonValidator = "NonValidator",
Validator = "Validator",
}
/**
* Reason for the proposal being rejected by the core node
*/
export enum ProposalRejectionReason {
CloseTimeTooLate = "CloseTimeTooLate",
CloseTimeTooSoon = "CloseTimeTooSoon",
CouldNotInstantiateMarket = "CouldNotInstantiateMarket",
EnactTimeTooLate = "EnactTimeTooLate",
EnactTimeTooSoon = "EnactTimeTooSoon",
IncompatibleTimestamps = "IncompatibleTimestamps",
InsufficientTokens = "InsufficientTokens",
InvalidAsset = "InvalidAsset",
InvalidAssetDetails = "InvalidAssetDetails",
InvalidFeeAmount = "InvalidFeeAmount",
InvalidFutureMaturityTimestamp = "InvalidFutureMaturityTimestamp",
InvalidFutureProduct = "InvalidFutureProduct",
InvalidInstrumentSecurity = "InvalidInstrumentSecurity",
InvalidRiskParameter = "InvalidRiskParameter",
InvalidShape = "InvalidShape",
MajorityThresholdNotReached = "MajorityThresholdNotReached",
MarketMissingLiquidityCommitment = "MarketMissingLiquidityCommitment",
MissingBuiltinAssetField = "MissingBuiltinAssetField",
MissingCommitmentAmount = "MissingCommitmentAmount",
MissingERC20ContractAddress = "MissingERC20ContractAddress",
NetworkParameterInvalidKey = "NetworkParameterInvalidKey",
NetworkParameterInvalidValue = "NetworkParameterInvalidValue",
NetworkParameterValidationFailed = "NetworkParameterValidationFailed",
NoProduct = "NoProduct",
NoRiskParameters = "NoRiskParameters",
NoTradingMode = "NoTradingMode",
NodeValidationFailed = "NodeValidationFailed",
OpeningAuctionDurationTooLarge = "OpeningAuctionDurationTooLarge",
OpeningAuctionDurationTooSmall = "OpeningAuctionDurationTooSmall",
ParticipationThresholdNotReached = "ParticipationThresholdNotReached",
ProductMaturityIsPassed = "ProductMaturityIsPassed",
UnsupportedProduct = "UnsupportedProduct",
UnsupportedTradingMode = "UnsupportedTradingMode",
}
/**
* Various states a proposal can transition through:
* Open ->
* - Passed -> Enacted.
* - Rejected.
* Proposal can enter Failed state from any other state.
*/
export enum ProposalState {
Declined = "Declined",
Enacted = "Enacted",
Failed = "Failed",
Open = "Open",
Passed = "Passed",
Rejected = "Rejected",
WaitingForNodeVote = "WaitingForNodeVote",
}
export enum VoteValue {
No = "No",
Yes = "Yes",
}
//==============================================================
// END Enums and Input Objects
//==============================================================

View File

@ -0,0 +1,15 @@
import packageJson from "../../../package.json";
export const Footer = () => {
return (
<footer>
<section>
<div>Reading Vega Fairground data from </div>
<div>
Version/commit hash: {packageJson.version} /
{process.env.REACT_APP_COMMIT_REF || "dev"}
</div>
</section>
</footer>
);
};

View File

@ -0,0 +1,9 @@
import Search from "../search";
export const Header = () => {
return (
<header>
<Search />
</header>
);
};

View File

@ -0,0 +1,9 @@
import { AppRouter } from "../../routes";
export const Main = () => {
return (
<main>
<AppRouter />
</main>
);
};

View File

@ -0,0 +1,15 @@
import { Link } from "react-router-dom";
import routerConfig from "../../routes/router-config";
export const Nav = () => {
return (
<nav>
{routerConfig.map((r) => (
<div key={r.name}>
<Link to={r.path}>{r.name}</Link>
<br />
</div>
))}
</nav>
);
};

View File

@ -0,0 +1,31 @@
import React from "react";
interface RouteErrorBoundaryProps {
children: React.ReactElement;
}
export class RouteErrorBoundary extends React.Component<
RouteErrorBoundaryProps,
{ hasError: boolean }
> {
constructor(props: RouteErrorBoundaryProps) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error: Error) {
console.log(`Error caught in App error boundary ${error.message}`, error);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong</h1>;
}
return this.props.children;
}
}

View File

@ -0,0 +1,68 @@
import React from "react";
import { useState } from "react";
const PARTY_ID_LENGTH = 64;
enum PossibleIdTypes {
Block = "Block",
Tx = "Tx",
Party = "Party",
Unknown = "Unknown",
}
const useGuess = () => {
const [search, setSearch] = useState<string>("");
const [possibleTypes, setPossibleTypes] = useState<PossibleIdTypes[]>();
const getPossibleIds = React.useCallback((search: string) => {
if (!search.length) {
return [];
} else if (
search.startsWith("0x") &&
search.length === PARTY_ID_LENGTH + 2
) {
return [PossibleIdTypes.Tx, PossibleIdTypes.Party];
} else if (!search.startsWith("0x") && search.length === PARTY_ID_LENGTH) {
return [PossibleIdTypes.Tx, PossibleIdTypes.Party];
} else if (!isNaN(Number(search))) {
return [PossibleIdTypes.Block];
}
return [];
}, []);
const onChange = React.useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const search = event.target.value;
setSearch(search);
setPossibleTypes(getPossibleIds(search));
},
[getPossibleIds]
);
return {
onChange,
possibleTypes,
search,
};
};
const Search = () => {
const { search, onChange, possibleTypes } = useGuess();
return (
<section>
<h1>Vega Block Explorer</h1>
<fieldset>
<label htmlFor="search">Search: </label>
<input
name="search"
value={search}
onChange={(e) => onChange(e)}
></input>
</fieldset>
{/* TODO implement links */}
<div>{JSON.stringify(possibleTypes)}</div>
</section>
);
};
export default Search;

View File

@ -0,0 +1 @@
export * from "./splash-loader";

View File

@ -0,0 +1,22 @@
@import "../../styles/colors";
.loading {
display: flex;
flex-direction: column;
align-items: center;
&__animation {
display: flex;
flex-wrap: wrap;
width: 50px;
height: 50px;
margin-bottom: 20px;
div {
width: 10px;
height: 10px;
background: white;
opacity: 0;
}
}
}

View File

@ -0,0 +1,32 @@
import "./splash-loader.scss";
import React from "react";
export const SplashLoader = ({ text = "Loading" }: { text?: string }) => {
const [, forceRender] = React.useState(false);
React.useEffect(() => {
const interval = setInterval(() => {
forceRender((x) => !x);
}, 100);
return () => clearInterval(interval);
}, []);
return (
<div className="loading" data-testid="splash-loader">
<div className="loading__animation">
{new Array(25).fill(null).map((_, i) => {
return (
<div
key={i}
style={{
opacity: Math.random() > 0.75 ? 1 : 0,
}}
/>
);
})}
</div>
<div>{text}</div>
</div>
);
};

View File

@ -0,0 +1 @@
export * from "./splash-screen";

View File

@ -0,0 +1,12 @@
@import "../../styles/colors";
.splash-screen {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
width: 100%;
height: 100%;
font-size: 20px;
color: $white;
}

View File

@ -0,0 +1,7 @@
import "./splash-screen.scss";
import React from "react";
export const SplashScreen = ({ children }: { children: React.ReactNode }) => {
return <div className="splash-screen">{children}</div>;
};

View File

@ -0,0 +1,7 @@
export const DATA_SOURCES = {
chainExplorerUrl: process.env.REACT_APP_CHAIN_EXPLORER_URL as string,
tendermintUrl: process.env.REACT_APP_TENDERMINT_URL as string,
tendermintWebsocketUrl: process.env
.REACT_APP_TENDERMINT_WEBSOCKET_URL as string,
dataNodeUrl: process.env.REACT_APP_VEGA_URL as string,
};

View File

@ -0,0 +1,15 @@
import React from "react";
import { WebSocketHook } from "react-use-websocket/dist/lib/types";
export type WebsocketContextShape = WebSocketHook;
export const TendermintWebsocketContext =
React.createContext<WebsocketContextShape | null>(null);
export function useTendermintWebsocketContext() {
const context = React.useContext(TendermintWebsocketContext);
if (context === null) {
throw new Error("useWebsocket must be used within WebsocketContext");
}
return context;
}

View File

@ -0,0 +1,33 @@
import React, { useState } from "react";
import useWebSocket from "react-use-websocket";
import { SplashLoader } from "../../components/splash-loader";
import { SplashScreen } from "../../components/splash-screen";
import { DATA_SOURCES } from "../../config";
import { TendermintWebsocketContext } from "./tendermint-websocket-context";
/**
* Provides a single, shared, websocket instance to the entire app to prevent recreation on every render
*/
export const TendermintWebsocketProvider = ({
children,
}: {
children: JSX.Element;
}) => {
const [socketUrl] = useState(DATA_SOURCES.tendermintWebsocketUrl);
const contextShape = useWebSocket(socketUrl);
if (!contextShape) {
return (
<SplashScreen>
<SplashLoader />
</SplashScreen>
);
}
return (
<TendermintWebsocketContext.Provider value={{ ...contextShape }}>
{children}
</TendermintWebsocketContext.Provider>
);
};

View File

@ -0,0 +1,86 @@
import { useEffect, useReducer, useRef } from "react";
interface State<T> {
data?: T;
error?: Error;
loading?: boolean;
}
enum ActionType {
LOADING = "LOADING",
ERROR = "ERROR",
FETCHED = "FETCHED",
}
// discriminated union type
type Action<T> =
| { type: ActionType.LOADING }
| { type: ActionType.FETCHED; payload: T }
| { type: ActionType.ERROR; error: Error };
function useFetch<T = unknown>(url?: string, options?: RequestInit): State<T> {
// Used to prevent state update if the component is unmounted
const cancelRequest = useRef<boolean>(false);
const initialState: State<T> = {
error: undefined,
data: undefined,
loading: false,
};
// Keep state logic separated
const fetchReducer = (state: State<T>, action: Action<T>): State<T> => {
switch (action.type) {
case ActionType.LOADING:
return { ...initialState, loading: true };
case ActionType.FETCHED:
return { ...initialState, data: action.payload, loading: false };
case ActionType.ERROR:
return { ...initialState, error: action.error, loading: false };
}
};
const [state, dispatch] = useReducer(fetchReducer, initialState);
useEffect(() => {
// Do nothing if the url is not given
if (!url) return;
const fetchData = async () => {
dispatch({ type: ActionType.LOADING });
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(response.statusText);
}
const data = (await response.json()) as T;
if ("error" in data) {
// @ts-ignore
throw new Error(data.error);
}
if (cancelRequest.current) return;
dispatch({ type: ActionType.FETCHED, payload: data });
} catch (error) {
if (cancelRequest.current) return;
dispatch({ type: ActionType.ERROR, error: error as Error });
}
};
void fetchData();
// Use the cleanup function for avoiding a possibly...
// ...state update after the component was unmounted
return () => {
cancelRequest.current = true;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [url]);
return state;
}
export default useFetch;

View File

@ -0,0 +1,68 @@
import React from "react";
import { useTendermintWebsocketContext } from "../contexts/websocket/tendermint-websocket-context";
export interface TendermintWebsocketQuery {
query: string;
}
/**
* Handles the ID generation, subscription and unsubscription logic for tendermint websocket queries
*
* Subscribes when called and cleans up the subscription on destroy.
*
* Creates IDs for messages uniquely so only the current subscription is updated
*
* If a bufferSize is passed in then a circular buffer is created, with the oldest messages being discarded to prevent memory leeks
*
* @param message The query to be sent to tendermint see: https://docs.tendermint.com/master/rpc/#/Websocket/subscribe for syntax for query parameter
* @param bufferSize
* @returns
*/
export const useTendermintWebsocket = function <T>(
message: TendermintWebsocketQuery,
bufferSize?: number
) {
const { sendMessage, lastMessage } = useTendermintWebsocketContext();
// @ts-ignore
const [id] = React.useState(crypto.randomUUID());
const [messages, setMessages] = React.useState<MessageEvent<T>[]>([]);
const [subMsg] = React.useState({
jsonrpc: "2.0",
method: "subscribe",
id,
params: {
...message,
},
});
const [unsubMsg] = React.useState({
...subMsg,
method: "unsubscribe",
});
React.useEffect(() => {
if (lastMessage && lastMessage.data) {
const data = JSON.parse(lastMessage.data);
if (data.id === id) {
setMessages((prev) => {
if (bufferSize && prev.length >= bufferSize) {
return [data, ...prev.slice(0, bufferSize - 1)];
} else {
return [data, ...prev];
}
});
}
}
}, [bufferSize, id, lastMessage, setMessages]);
React.useEffect(() => {
sendMessage(JSON.stringify(subMsg));
return () => {
sendMessage(JSON.stringify(unsubMsg));
};
}, [subMsg, sendMessage, unsubMsg]);
return {
messages,
};
};

View File

@ -0,0 +1,16 @@
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View File

@ -0,0 +1,53 @@
import { ApolloClient, from, HttpLink, InMemoryCache } from "@apollo/client";
import { onError } from "@apollo/client/link/error";
import { RetryLink } from "@apollo/client/link/retry";
export function createClient(base?: string) {
if (!base) {
throw new Error("Base must be passed into createClient!");
}
const gqlPath = "query";
const urlHTTP = new URL(gqlPath, base);
const urlWS = new URL(gqlPath, base);
// Replace http with ws, preserving if its a secure connection eg. https => wss
urlWS.protocol = urlWS.protocol.replace("http", "ws");
const cache = new InMemoryCache({
typePolicies: {
Query: {},
Account: {
keyFields: false,
fields: {
balanceFormatted: {},
},
},
Node: {
keyFields: false,
},
},
});
const retryLink = new RetryLink({
delay: {
initial: 300,
max: 10000,
jitter: true,
},
});
const httpLink = new HttpLink({
uri: urlHTTP.href,
credentials: "same-origin",
});
const errorLink = onError(({ graphQLErrors, networkError }) => {
console.log(graphQLErrors);
console.log(networkError);
});
return new ApolloClient({
connectToDevTools: process.env.NODE_ENV === "development",
link: from([errorLink, retryLink, httpLink]),
cache,
});
}

View File

@ -0,0 +1 @@
/// <reference types="react-scripts" />

View File

@ -0,0 +1,15 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@ -0,0 +1,95 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { AccountType } from "./../../../__generated__/globalTypes";
// ====================================================
// GraphQL query operation: AssetsQuery
// ====================================================
export interface AssetsQuery_assets_source_ERC20 {
__typename: "ERC20";
/**
* The address of the erc20 contract
*/
contractAddress: string;
}
export interface AssetsQuery_assets_source_BuiltinAsset {
__typename: "BuiltinAsset";
/**
* Maximum amount that can be requested by a party through the built-in asset faucet at a time
*/
maxFaucetAmountMint: string;
}
export type AssetsQuery_assets_source = AssetsQuery_assets_source_ERC20 | AssetsQuery_assets_source_BuiltinAsset;
export interface AssetsQuery_assets_infrastructureFeeAccount_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
}
export interface AssetsQuery_assets_infrastructureFeeAccount {
__typename: "Account";
/**
* Account type (General, Margin, etc)
*/
type: AccountType;
/**
* Balance as string - current account balance (approx. as balances can be updated several times per second)
*/
balance: string;
/**
* Market (only relevant to margin accounts)
*/
market: AssetsQuery_assets_infrastructureFeeAccount_market | null;
}
export interface AssetsQuery_assets {
__typename: "Asset";
/**
* The id of the asset
*/
id: string;
/**
* The full name of the asset (e.g: Great British Pound)
*/
name: string;
/**
* The symbol of the asset (e.g: GBP)
*/
symbol: string;
/**
* The total supply of the market
*/
totalSupply: string;
/**
* The precision of the asset
*/
decimals: number;
/**
* The min stake to become an lp for any market using this asset for settlement
*/
minLpStake: string;
/**
* The origin source of the asset (e.g: an erc20 asset)
*/
source: AssetsQuery_assets_source;
/**
* The infrastructure fee account for this asset
*/
infrastructureFeeAccount: AssetsQuery_assets_infrastructureFeeAccount;
}
export interface AssetsQuery {
/**
* The list of all assets in use in the vega network
*/
assets: AssetsQuery_assets[] | null;
}

View File

@ -0,0 +1,42 @@
import { gql, useQuery } from "@apollo/client";
import { AssetsQuery } from "./__generated__/AssetsQuery";
export const ASSETS_QUERY = gql`
query AssetsQuery {
assets {
id
name
symbol
totalSupply
decimals
minLpStake
source {
... on ERC20 {
contractAddress
}
... on BuiltinAsset {
maxFaucetAmountMint
}
}
infrastructureFeeAccount {
type
balance
market {
id
}
}
}
}
`;
const Assets = () => {
const { data } = useQuery<AssetsQuery>(ASSETS_QUERY);
return (
<section>
<h1>Assets</h1>
<pre>{JSON.stringify(data, null, " ")}</pre>
</section>
);
};
export default Assets;

View File

@ -0,0 +1,41 @@
import { DATA_SOURCES } from "../../../config";
import useFetch from "../../../hooks/use-fetch";
import { NewBlockMessage } from "../tendermint-new-block";
import { TendermintBlockchainResponse } from "../tendermint-blockchain-response";
import { useTendermintWebsocket } from "../../../hooks/use-tendermint-websocket";
const MAX_BLOCKS = 10;
const Blocks = () => {
const { messages: blocks } = useTendermintWebsocket<NewBlockMessage>(
{
query: "tm.event = 'NewBlock'",
},
MAX_BLOCKS
);
const { data } = useFetch<TendermintBlockchainResponse>(
`${DATA_SOURCES.tendermintUrl}/blockchain`
);
return (
<section>
<h1>Blocks</h1>
<h2>Blocks from blockchain</h2>
{`${DATA_SOURCES.tendermintUrl}/blockchain`}
<br />
<div>Height: {data?.result?.last_height || 0}</div>
<br />
<div>
<br />
<pre>{JSON.stringify(data, null, " ")}</pre>
</div>
<h2>Blocks streamed in</h2>
<div>
<br />
<pre>{JSON.stringify(blocks, null, " ")}</pre>
</div>
</section>
);
};
export { Blocks };

View File

@ -0,0 +1,40 @@
import React from "react";
import { useParams } from "react-router-dom";
import { DATA_SOURCES } from "../../../config";
import useFetch from "../../../hooks/use-fetch";
import { ChainExplorerTxResponse } from "../../types/chain-explorer-response";
import { TendermintBlocksResponse } from "../tendermint-blocks-response";
const Block = () => {
const { block } = useParams<{ block: string }>();
const { data: decodedBlockData } = useFetch<ChainExplorerTxResponse[]>(
DATA_SOURCES.chainExplorerUrl,
{
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
block_height: parseInt(block),
node_url: `${DATA_SOURCES.tendermintUrl}/`,
}),
}
);
const { data: blockData } = useFetch<TendermintBlocksResponse>(
`${DATA_SOURCES.tendermintUrl}/block?height=${block}`
);
return (
<section>
<h1>block</h1>
<h2>Tendermint Data</h2>
<pre>{JSON.stringify(blockData, null, " ")}</pre>
<h2>Decoded data</h2>
<pre>{JSON.stringify(decodedBlockData, null, " ")}</pre>
</section>
);
};
export { Block };

View File

@ -0,0 +1,21 @@
import React from "react";
import { Route, Switch, useRouteMatch } from "react-router-dom";
import { Blocks } from "./home";
import { Block } from "./id";
const BlockPage = () => {
const match = useRouteMatch();
return (
<Switch>
<Route path={match.path} exact={true}>
<Blocks />
</Route>
<Route path={`${match.path}/:block`}>
<Block />
</Route>
</Switch>
);
};
export default BlockPage;

View File

@ -0,0 +1,53 @@
export interface Parts {
total: number;
hash: string;
}
export interface BlockId {
hash: string;
parts: Parts;
}
export interface Version {
block: string;
}
export interface LastBlockId {
hash: string;
parts: Parts;
}
export interface Header {
version: Version;
chain_id: string;
height: string;
time: string;
last_block_id: LastBlockId;
last_commit_hash: string;
data_hash: string;
validators_hash: string;
next_validators_hash: string;
consensus_hash: string;
app_hash: string;
last_results_hash: string;
evidence_hash: string;
proposer_address: string;
}
export interface BlockMeta {
block_id: BlockId;
block_size: string;
header: Header;
num_txs: string;
}
export interface Result {
last_height: string;
block_metas: BlockMeta[];
}
export interface TendermintBlockchainResponse {
jsonrpc: string;
id: number;
result: Result;
}

View File

@ -0,0 +1,70 @@
export interface Parts {
total: number;
hash: string;
}
export interface BlockId {
hash: string;
parts: Parts;
}
export interface Version {
block: string;
}
export interface Header {
version: Version;
chain_id: string;
height: string;
time: string;
last_block_id: BlockId;
last_commit_hash: string;
data_hash: string;
validators_hash: string;
next_validators_hash: string;
consensus_hash: string;
app_hash: string;
last_results_hash: string;
evidence_hash: string;
proposer_address: string;
}
export interface Data {
txs: string[];
}
export interface Evidence {
evidence: any[];
}
export interface Signature {
block_id_flag: number;
validator_address: string;
timestamp: string;
signature: string;
}
export interface LastCommit {
height: string;
round: number;
block_id: BlockId;
signatures: Signature[];
}
export interface Block {
header: Header;
data: Data;
evidence: Evidence;
last_commit: LastCommit;
}
export interface Result {
block_id: BlockId;
block: Block;
}
export interface TendermintBlocksResponse {
jsonrpc: string;
id: number;
result: Result;
}

View File

@ -0,0 +1,97 @@
export interface Version {
block: string;
}
export interface Parts {
total: number;
hash: string;
}
export interface LastBlockId {
hash: string;
parts: Parts;
}
export interface Header {
version: Version;
chain_id: string;
height: string;
time: string;
last_block_id: LastBlockId;
last_commit_hash: string;
data_hash: string;
validators_hash: string;
next_validators_hash: string;
consensus_hash: string;
app_hash: string;
last_results_hash: string;
evidence_hash: string;
proposer_address: string;
}
export interface TransactionData {
txs: string[];
}
export interface Evidence {
evidence: any[];
}
export interface BlockId {
hash: string;
parts: Parts;
}
export interface Signature {
block_id_flag: number;
validator_address: string;
timestamp: string;
signature: string;
}
export interface LastCommit {
height: string;
round: number;
block_id: BlockId;
signatures: Signature[];
}
export interface Block {
header: Header;
data: TransactionData;
evidence: Evidence;
last_commit: LastCommit;
}
export interface ResultBeginBlock {}
export interface ResultEndBlock {
validator_updates?: any;
}
export interface Value {
block: Block;
result_begin_block: ResultBeginBlock;
result_end_block: ResultEndBlock;
}
export interface Data {
type: string;
value: Value;
}
export interface Events {
"tm.event": string[];
}
export interface Result {
query: string;
data: Data;
events: Events;
}
export interface NewBlockMessage {
jsonrpc: string;
id: string;
result: Result;
}

View File

@ -0,0 +1,17 @@
import { DATA_SOURCES } from "../../config";
import useFetch from "../../hooks/use-fetch";
import { TendermintGenesisResponse } from "./tendermint-genesis-response";
const Genesis = () => {
const { data: genesis } = useFetch<TendermintGenesisResponse>(
`${DATA_SOURCES.tendermintUrl}/genesis`
);
return (
<section>
<h1>Genesis</h1>
<pre>{JSON.stringify(genesis, null, " ")}</pre>
</section>
);
};
export default Genesis;

View File

@ -0,0 +1,112 @@
export interface Block {
max_bytes: string;
max_gas: string;
time_iota_ms: string;
}
export interface Evidence {
max_age_num_blocks: string;
max_age_duration: string;
}
export interface ValidatorAddresses {
pub_key_types: string[];
}
export interface Version {}
export interface ConsensusParams {
block: Block;
evidence: Evidence;
validator: ValidatorAddresses;
version: Version;
}
export interface PubKey {
type: string;
value: string;
}
export interface Validator {
address: string;
pub_key: PubKey;
power: string;
name: string;
}
export interface Erc20 {
contract_address: string;
}
export interface Erc20Source {
erc20: Erc20;
}
export interface Asset {
decimals: number;
min_lp_stake: string;
name: string;
source: Erc20Source | BuiltinAssetSource;
symbol: string;
total_supply: string;
}
export interface BuiltinAsset {
max_faucet_amount_mint: string;
}
export interface BuiltinAssetSource {
builtin_asset: BuiltinAsset;
}
export interface Assets {
[key: string]: Asset;
}
export interface Network {
replay_attack_threshold: number;
}
export interface NetworkLimits {
bootstrap_block_count: number;
propose_asset_enabled: boolean;
propose_asset_enabled_from: Date;
propose_market_enabled: boolean;
propose_market_enabled_from: Date;
}
export interface NetworkParameters {
[key: string]: string;
}
export interface Validators {
[key: string]: Validator;
}
export interface AppState {
assets: Assets;
network: Network;
network_limits: NetworkLimits;
network_parameters: NetworkParameters;
validators: Validators;
}
export interface Genesis {
genesis_time: Date;
chain_id: string;
initial_height: string;
consensus_params: ConsensusParams;
validators: Validator[];
app_hash: string;
app_state: AppState;
}
export interface Result {
genesis: Genesis;
}
export interface TendermintGenesisResponse {
jsonrpc: string;
id: number;
result: Result;
}

View File

@ -0,0 +1,265 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { ProposalState, ProposalRejectionReason, VoteValue } from "./../../../__generated__/globalTypes";
// ====================================================
// GraphQL query operation: ProposalsQuery
// ====================================================
export interface ProposalsQuery_proposals_party {
__typename: "Party";
/**
* Party identifier
*/
id: string;
}
export interface ProposalsQuery_proposals_terms_change_NewMarket_instrument {
__typename: "InstrumentConfiguration";
/**
* Full and fairly descriptive name for the instrument
*/
name: string;
}
export interface ProposalsQuery_proposals_terms_change_NewMarket {
__typename: "NewMarket";
/**
* New market instrument configuration
*/
instrument: ProposalsQuery_proposals_terms_change_NewMarket_instrument;
}
export interface ProposalsQuery_proposals_terms_change_UpdateMarket {
__typename: "UpdateMarket";
marketId: string;
}
export interface ProposalsQuery_proposals_terms_change_NewAsset_source_BuiltinAsset {
__typename: "BuiltinAsset";
/**
* Maximum amount that can be requested by a party through the built-in asset faucet at a time
*/
maxFaucetAmountMint: string;
}
export interface ProposalsQuery_proposals_terms_change_NewAsset_source_ERC20 {
__typename: "ERC20";
/**
* The address of the erc20 contract
*/
contractAddress: string;
}
export type ProposalsQuery_proposals_terms_change_NewAsset_source = ProposalsQuery_proposals_terms_change_NewAsset_source_BuiltinAsset | ProposalsQuery_proposals_terms_change_NewAsset_source_ERC20;
export interface ProposalsQuery_proposals_terms_change_NewAsset {
__typename: "NewAsset";
/**
* The symbol of the asset (e.g: GBP)
*/
symbol: string;
/**
* the source of the new Asset
*/
source: ProposalsQuery_proposals_terms_change_NewAsset_source;
}
export interface ProposalsQuery_proposals_terms_change_UpdateNetworkParameter_networkParameter {
__typename: "NetworkParameter";
/**
* The name of the network parameter
*/
key: string;
/**
* The value of the network parameter
*/
value: string;
}
export interface ProposalsQuery_proposals_terms_change_UpdateNetworkParameter {
__typename: "UpdateNetworkParameter";
networkParameter: ProposalsQuery_proposals_terms_change_UpdateNetworkParameter_networkParameter;
}
export type ProposalsQuery_proposals_terms_change = ProposalsQuery_proposals_terms_change_NewMarket | ProposalsQuery_proposals_terms_change_UpdateMarket | ProposalsQuery_proposals_terms_change_NewAsset | ProposalsQuery_proposals_terms_change_UpdateNetworkParameter;
export interface ProposalsQuery_proposals_terms {
__typename: "ProposalTerms";
/**
* RFC3339Nano time and date when voting closes for this proposal.
* Constrained by "minClose" and "maxClose" network parameters.
*/
closingDatetime: string;
/**
* RFC3339Nano time and date when this proposal is executed (if passed). Note that it has to be after closing date time.
* Constrained by "minEnactInSeconds" and "maxEnactInSeconds" network parameters.
*/
enactmentDatetime: string;
/**
* Actual change being introduced by the proposal - action the proposal triggers if passed and enacted.
*/
change: ProposalsQuery_proposals_terms_change;
}
export interface ProposalsQuery_proposals_votes_yes_votes_party_stake {
__typename: "PartyStake";
/**
* The stake currently available for the party
*/
currentStakeAvailable: string;
}
export interface ProposalsQuery_proposals_votes_yes_votes_party {
__typename: "Party";
/**
* Party identifier
*/
id: string;
/**
* The staking informations for this Party
*/
stake: ProposalsQuery_proposals_votes_yes_votes_party_stake;
}
export interface ProposalsQuery_proposals_votes_yes_votes {
__typename: "Vote";
/**
* The vote value cast
*/
value: VoteValue;
/**
* The party casting the vote
*/
party: ProposalsQuery_proposals_votes_yes_votes_party;
/**
* RFC3339Nano time and date when the vote reached Vega network
*/
datetime: string;
}
export interface ProposalsQuery_proposals_votes_yes {
__typename: "ProposalVoteSide";
/**
* Total tokens of governance token from the votes casted for this side
*/
totalTokens: string;
/**
* Total number of votes casted for this side
*/
totalNumber: string;
/**
* All votes casted for this side
*/
votes: ProposalsQuery_proposals_votes_yes_votes[] | null;
}
export interface ProposalsQuery_proposals_votes_no_votes_party_stake {
__typename: "PartyStake";
/**
* The stake currently available for the party
*/
currentStakeAvailable: string;
}
export interface ProposalsQuery_proposals_votes_no_votes_party {
__typename: "Party";
/**
* Party identifier
*/
id: string;
/**
* The staking informations for this Party
*/
stake: ProposalsQuery_proposals_votes_no_votes_party_stake;
}
export interface ProposalsQuery_proposals_votes_no_votes {
__typename: "Vote";
/**
* The vote value cast
*/
value: VoteValue;
/**
* The party casting the vote
*/
party: ProposalsQuery_proposals_votes_no_votes_party;
/**
* RFC3339Nano time and date when the vote reached Vega network
*/
datetime: string;
}
export interface ProposalsQuery_proposals_votes_no {
__typename: "ProposalVoteSide";
/**
* Total tokens of governance token from the votes casted for this side
*/
totalTokens: string;
/**
* Total number of votes casted for this side
*/
totalNumber: string;
/**
* All votes casted for this side
*/
votes: ProposalsQuery_proposals_votes_no_votes[] | null;
}
export interface ProposalsQuery_proposals_votes {
__typename: "ProposalVotes";
/**
* Yes votes cast for this proposal
*/
yes: ProposalsQuery_proposals_votes_yes;
/**
* No votes cast for this proposal
*/
no: ProposalsQuery_proposals_votes_no;
}
export interface ProposalsQuery_proposals {
__typename: "Proposal";
/**
* Proposal ID that is filled by VEGA once proposal reaches the network
*/
id: string | null;
/**
* A UUID reference to aid tracking proposals on VEGA
*/
reference: string;
/**
* State of the proposal
*/
state: ProposalState;
/**
* RFC3339Nano time and date when the proposal reached Vega network
*/
datetime: string;
/**
* Reason for the proposal to be rejected by the core
*/
rejectionReason: ProposalRejectionReason | null;
/**
* Party that prepared the proposal
*/
party: ProposalsQuery_proposals_party;
/**
* Terms of the proposal
*/
terms: ProposalsQuery_proposals_terms;
/**
* Votes cast for this proposal
*/
votes: ProposalsQuery_proposals_votes;
}
export interface ProposalsQuery {
/**
* All governance proposals in the VEGA network
*/
proposals: ProposalsQuery_proposals[] | null;
}

View File

@ -0,0 +1,91 @@
import { gql, useQuery } from "@apollo/client";
import { ProposalsQuery } from "./__generated__/ProposalsQuery";
const PROPOSAL_QUERY = gql`
query ProposalsQuery {
proposals {
id
reference
state
datetime
rejectionReason
party {
id
}
terms {
closingDatetime
enactmentDatetime
change {
... on NewMarket {
instrument {
name
}
}
... on UpdateMarket {
marketId
}
... on NewAsset {
__typename
symbol
source {
... on BuiltinAsset {
maxFaucetAmountMint
}
... on ERC20 {
contractAddress
}
}
}
... on UpdateNetworkParameter {
networkParameter {
key
value
}
}
}
}
votes {
yes {
totalTokens
totalNumber
votes {
value
party {
id
stake {
currentStakeAvailable
}
}
datetime
}
}
no {
totalTokens
totalNumber
votes {
value
party {
id
stake {
currentStakeAvailable
}
}
datetime
}
}
}
}
}
`;
const Governance = () => {
const { data } = useQuery<ProposalsQuery>(PROPOSAL_QUERY);
return (
<section>
<h1>Governance</h1>
<pre>{JSON.stringify(data, null, " ")}</pre>
</section>
);
};
export default Governance;

View File

@ -0,0 +1,9 @@
const Home = () => {
return (
<section>
<h1>Home page content</h1>
</section>
);
};
export default Home;

View File

@ -0,0 +1,35 @@
import React from "react";
import { Route, Switch } from "react-router-dom";
import { RouteErrorBoundary } from "../components/router-error-boundary";
import { SplashLoader } from "../components/splash-loader";
import { SplashScreen } from "../components/splash-screen";
import routerConfig from "./router-config";
export interface RouteChildProps {
name: string;
}
export const AppRouter = () => {
const splashLoading = (
<SplashScreen>
<SplashLoader />
</SplashScreen>
);
return (
<RouteErrorBoundary>
<React.Suspense fallback={splashLoading}>
<Switch>
{routerConfig.map(
({ path, component: Component, exact = false, name }) => (
<Route key={name} path={path} exact={exact}>
<Component />
</Route>
)
)}
</Switch>
</React.Suspense>
</RouteErrorBoundary>
);
};

View File

@ -0,0 +1,549 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { MarketTradingMode, MarketState, AccountType, AuctionTrigger } from "./../../../__generated__/globalTypes";
// ====================================================
// GraphQL query operation: MarketsQuery
// ====================================================
export interface MarketsQuery_markets_fees_factors {
__typename: "FeeFactors";
/**
* The factor applied to calculate MakerFees, a non-negative float
*/
makerFee: string;
/**
* The factor applied to calculate InfrastructureFees, a non-negative float
*/
infrastructureFee: string;
/**
* The factor applied to calculate LiquidityFees, a non-negative float
*/
liquidityFee: string;
}
export interface MarketsQuery_markets_fees {
__typename: "Fees";
/**
* The factors used to calculate the different fees
*/
factors: MarketsQuery_markets_fees_factors;
}
export interface MarketsQuery_markets_tradableInstrument_instrument_metadata {
__typename: "InstrumentMetadata";
/**
* An arbitrary list of tags to associated to associate to the Instrument (string list)
*/
tags: string[] | null;
}
export interface MarketsQuery_markets_tradableInstrument_instrument_product_settlementAsset_globalRewardPoolAccount {
__typename: "Account";
/**
* Balance as string - current account balance (approx. as balances can be updated several times per second)
*/
balance: string;
}
export interface MarketsQuery_markets_tradableInstrument_instrument_product_settlementAsset {
__typename: "Asset";
/**
* The id of the asset
*/
id: string;
/**
* The full name of the asset (e.g: Great British Pound)
*/
name: string;
/**
* The precision of the asset
*/
decimals: number;
/**
* The total supply of the market
*/
totalSupply: string;
/**
* The global reward pool account for this asset
*/
globalRewardPoolAccount: MarketsQuery_markets_tradableInstrument_instrument_product_settlementAsset_globalRewardPoolAccount | null;
}
export interface MarketsQuery_markets_tradableInstrument_instrument_product {
__typename: "Future";
/**
* RFC3339Nano maturity date of the product
*/
maturity: string;
/**
* The name of the asset (string)
*/
settlementAsset: MarketsQuery_markets_tradableInstrument_instrument_product_settlementAsset;
}
export interface MarketsQuery_markets_tradableInstrument_instrument {
__typename: "Instrument";
/**
* Full and fairly descriptive name for the instrument
*/
name: string;
/**
* Metadata for this instrument
*/
metadata: MarketsQuery_markets_tradableInstrument_instrument_metadata;
/**
* Uniquely identify an instrument accrods all instruments available on Vega (string)
*/
id: string;
/**
* A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
*/
code: string;
/**
* A reference to or instance of a fully specified product, including all required product parameters for that product (Product union)
*/
product: MarketsQuery_markets_tradableInstrument_instrument_product;
}
export interface MarketsQuery_markets_tradableInstrument_riskModel_LogNormalRiskModel_params {
__typename: "LogNormalModelParams";
/**
* r parameter
*/
r: number;
/**
* sigma parameter
*/
sigma: number;
/**
* mu parameter
*/
mu: number;
}
export interface MarketsQuery_markets_tradableInstrument_riskModel_LogNormalRiskModel {
__typename: "LogNormalRiskModel";
/**
* Tau parameter of the risk model
*/
tau: number;
/**
* Lambda parameter of the risk model
*/
riskAversionParameter: number;
/**
* Params for the log normal risk model
*/
params: MarketsQuery_markets_tradableInstrument_riskModel_LogNormalRiskModel_params;
}
export interface MarketsQuery_markets_tradableInstrument_riskModel_SimpleRiskModel_params {
__typename: "SimpleRiskModelParams";
/**
* Risk factor for long
*/
factorLong: number;
/**
* Risk factor for short
*/
factorShort: number;
}
export interface MarketsQuery_markets_tradableInstrument_riskModel_SimpleRiskModel {
__typename: "SimpleRiskModel";
/**
* Params for the simple risk model
*/
params: MarketsQuery_markets_tradableInstrument_riskModel_SimpleRiskModel_params;
}
export type MarketsQuery_markets_tradableInstrument_riskModel = MarketsQuery_markets_tradableInstrument_riskModel_LogNormalRiskModel | MarketsQuery_markets_tradableInstrument_riskModel_SimpleRiskModel;
export interface MarketsQuery_markets_tradableInstrument_marginCalculator_scalingFactors {
__typename: "ScalingFactors";
/**
* the scaling factor that determines the margin level at which Vega has to search for more money
*/
searchLevel: number;
/**
* the scaling factor that determines the optimal margin level
*/
initialMargin: number;
/**
* The scaling factor that determines the overflow margin level
*/
collateralRelease: number;
}
export interface MarketsQuery_markets_tradableInstrument_marginCalculator {
__typename: "MarginCalculator";
/**
* The scaling factors that will be used for margin calculation
*/
scalingFactors: MarketsQuery_markets_tradableInstrument_marginCalculator_scalingFactors;
}
export interface MarketsQuery_markets_tradableInstrument {
__typename: "TradableInstrument";
/**
* An instance of or reference to a fully specified instrument.
*/
instrument: MarketsQuery_markets_tradableInstrument_instrument;
/**
* A reference to a risk model that is valid for the instrument
*/
riskModel: MarketsQuery_markets_tradableInstrument_riskModel;
/**
* Margin calculation info, currently only the scaling factors (search, initial, release) for this tradable instrument
*/
marginCalculator: MarketsQuery_markets_tradableInstrument_marginCalculator | null;
}
export interface MarketsQuery_markets_openingAuction {
__typename: "AuctionDuration";
/**
* Duration of the auction in seconds
*/
durationSecs: number;
/**
* Target uncrossing trading volume
*/
volume: number;
}
export interface MarketsQuery_markets_priceMonitoringSettings_parameters_triggers {
__typename: "PriceMonitoringTrigger";
/**
* Price monitoring projection horizon τ in seconds (> 0).
*/
horizonSecs: number;
/**
* Price monitoring probability level p. (>0 and < 1)
*/
probability: number;
/**
* Price monitoring auction extension duration in seconds should the price
* breach it's theoretical level over the specified horizon at the specified
* probability level (> 0)
*/
auctionExtensionSecs: number;
}
export interface MarketsQuery_markets_priceMonitoringSettings_parameters {
__typename: "PriceMonitoringParameters";
/**
* The list of triggers for this price monitoring
*/
triggers: MarketsQuery_markets_priceMonitoringSettings_parameters_triggers[] | null;
}
export interface MarketsQuery_markets_priceMonitoringSettings {
__typename: "PriceMonitoringSettings";
/**
* Specified a set of PriceMonitoringParameters to be use for price monitoring purposes
*/
parameters: MarketsQuery_markets_priceMonitoringSettings_parameters | null;
/**
* How often (in seconds) the price monitoring bounds should be updated
*/
updateFrequencySecs: number;
}
export interface MarketsQuery_markets_liquidityMonitoringParameters_targetStakeParameters {
__typename: "TargetStakeParameters";
/**
* Specifies length of time window expressed in seconds for target stake calculation
*/
timeWindow: number;
/**
* Specifies scaling factors used in target stake calculation
*/
scalingFactor: number;
}
export interface MarketsQuery_markets_liquidityMonitoringParameters {
__typename: "LiquidityMonitoringParameters";
/**
* Specifies the triggering ratio for entering liquidity auction
*/
triggeringRatio: number;
/**
* Specifies parameters related to target stake calculation
*/
targetStakeParameters: MarketsQuery_markets_liquidityMonitoringParameters_targetStakeParameters;
}
export interface MarketsQuery_markets_proposal {
__typename: "Proposal";
/**
* Proposal ID that is filled by VEGA once proposal reaches the network
*/
id: string | null;
}
export interface MarketsQuery_markets_accounts_asset {
__typename: "Asset";
/**
* The id of the asset
*/
id: string;
/**
* The full name of the asset (e.g: Great British Pound)
*/
name: string;
}
export interface MarketsQuery_markets_accounts {
__typename: "Account";
/**
* Asset, the 'currency'
*/
asset: MarketsQuery_markets_accounts_asset;
/**
* Balance as string - current account balance (approx. as balances can be updated several times per second)
*/
balance: string;
/**
* Account type (General, Margin, etc)
*/
type: AccountType;
}
export interface MarketsQuery_markets_data_priceMonitoringBounds_trigger {
__typename: "PriceMonitoringTrigger";
/**
* Price monitoring auction extension duration in seconds should the price
* breach it's theoretical level over the specified horizon at the specified
* probability level (> 0)
*/
auctionExtensionSecs: number;
/**
* Price monitoring probability level p. (>0 and < 1)
*/
probability: number;
}
export interface MarketsQuery_markets_data_priceMonitoringBounds {
__typename: "PriceMonitoringBounds";
/**
* Minimum price that isn't currently breaching the specified price monitoring trigger
*/
minValidPrice: string;
/**
* Maximum price that isn't currently breaching the specified price monitoring trigger
*/
maxValidPrice: string;
/**
* Price monitoring trigger associated with the bounds
*/
trigger: MarketsQuery_markets_data_priceMonitoringBounds_trigger;
/**
* Reference price used to calculate the valid price range
*/
referencePrice: string;
}
export interface MarketsQuery_markets_data_liquidityProviderFeeShare_party {
__typename: "Party";
/**
* Party identifier
*/
id: string;
}
export interface MarketsQuery_markets_data_liquidityProviderFeeShare {
__typename: "LiquidityProviderFeeShare";
/**
* The liquidity provider party id
*/
party: MarketsQuery_markets_data_liquidityProviderFeeShare_party;
/**
* The share own by this liquidity provider (float)
*/
equityLikeShare: string;
/**
* the average entry valuation of the liqidity provider for the market
*/
averageEntryValuation: string;
}
export interface MarketsQuery_markets_data {
__typename: "MarketData";
/**
* the mark price (actually an unsgined int)
*/
markPrice: string;
/**
* the highest price level on an order book for buy orders.
*/
bestBidPrice: string;
/**
* the aggregated volume being bid at the best bid price.
*/
bestBidVolume: string;
/**
* the lowest price level on an order book for offer orders.
*/
bestOfferPrice: string;
/**
* the aggregated volume being offered at the best offer price.
*/
bestOfferVolume: string;
/**
* the highest price level on an order book for buy orders not including pegged orders.
*/
bestStaticBidPrice: string;
/**
* the aggregated volume being offered at the best static bid price, excluding pegged orders
*/
bestStaticBidVolume: string;
/**
* the lowest price level on an order book for offer orders not including pegged orders.
*/
bestStaticOfferPrice: string;
/**
* the aggregated volume being offered at the best static offer price, excluding pegged orders.
*/
bestStaticOfferVolume: string;
/**
* the arithmetic average of the best bid price and best offer price.
*/
midPrice: string;
/**
* the arithmetic average of the best static bid price and best static offer price
*/
staticMidPrice: string;
/**
* RFC3339Nano time at which this market price was releavant
*/
timestamp: string;
/**
* the sum of the size of all positions greater than 0.
*/
openInterest: string;
/**
* RFC3339Nano time at which the auction will stop (null if not in auction mode)
*/
auctionEnd: string | null;
/**
* RFC3339Nano time at which the next auction will start (null if none is scheduled)
*/
auctionStart: string | null;
/**
* indicative price if the auction ended now, 0 if not in auction mode
*/
indicativePrice: string;
/**
* indicative volume if the auction ended now, 0 if not in auction mode
*/
indicativeVolume: string;
/**
* what triggered an auction (if an auction was started)
*/
trigger: AuctionTrigger;
/**
* what extended the ongoing auction (if an auction was extended)
*/
extensionTrigger: AuctionTrigger;
/**
* the amount of stake targeted for this market
*/
targetStake: string | null;
/**
* the supplied stake for the market
*/
suppliedStake: string | null;
/**
* A list of valid price ranges per associated trigger
*/
priceMonitoringBounds: MarketsQuery_markets_data_priceMonitoringBounds[] | null;
/**
* the market value proxy
*/
marketValueProxy: string;
/**
* the equity like share of liquidity fee for each liquidity provider
*/
liquidityProviderFeeShare: MarketsQuery_markets_data_liquidityProviderFeeShare[] | null;
}
export interface MarketsQuery_markets {
__typename: "Market";
/**
* Market ID
*/
id: string;
/**
* Market full name
*/
name: string;
/**
* Fees related data
*/
fees: MarketsQuery_markets_fees;
/**
* An instance of or reference to a tradable instrument.
*/
tradableInstrument: MarketsQuery_markets_tradableInstrument;
/**
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
* number denominated in the currency of the Market. (uint64)
*
* Examples:
* Currency Balance decimalPlaces Real Balance
* GBP 100 0 GBP 100
* GBP 100 2 GBP 1.00
* GBP 100 4 GBP 0.01
* GBP 1 4 GBP 0.0001 ( 0.01p )
*
* GBX (pence) 100 0 GBP 1.00 (100p )
* GBX (pence) 100 2 GBP 0.01 ( 1p )
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/
decimalPlaces: number;
/**
* Auction duration specifies how long the opening auction will run (minimum
* duration and optionally a minimum traded volume).
*/
openingAuction: MarketsQuery_markets_openingAuction;
/**
* Price monitoring settings for the market
*/
priceMonitoringSettings: MarketsQuery_markets_priceMonitoringSettings;
/**
* Liquidity monitoring parameters for the market
*/
liquidityMonitoringParameters: MarketsQuery_markets_liquidityMonitoringParameters;
/**
* Current mode of execution of the market
*/
tradingMode: MarketTradingMode;
/**
* Current state of the market
*/
state: MarketState;
/**
* The proposal which initiated this market
*/
proposal: MarketsQuery_markets_proposal | null;
/**
* Get account for a party or market
*/
accounts: MarketsQuery_markets_accounts[] | null;
/**
* marketData for the given market
*/
data: MarketsQuery_markets_data | null;
}
export interface MarketsQuery {
/**
* One or more instruments that are trading on the VEGA network
*/
markets: MarketsQuery_markets[] | null;
}

View File

@ -0,0 +1,154 @@
import { gql, useQuery } from "@apollo/client";
import { MarketsQuery } from "./__generated__/MarketsQuery";
const MARKETS_QUERY = gql`
query MarketsQuery {
markets {
id
name
fees {
factors {
makerFee
infrastructureFee
liquidityFee
}
}
tradableInstrument {
instrument {
name
metadata {
tags
}
id
code
product {
... on Future {
maturity
settlementAsset {
id
name
decimals
totalSupply
globalRewardPoolAccount {
balance
}
}
}
}
}
riskModel {
... on LogNormalRiskModel {
tau
riskAversionParameter
params {
r
sigma
mu
}
}
... on SimpleRiskModel {
params {
factorLong
factorShort
}
}
}
marginCalculator {
scalingFactors {
searchLevel
initialMargin
collateralRelease
}
}
}
decimalPlaces
openingAuction {
durationSecs
volume
}
priceMonitoringSettings {
parameters {
triggers {
horizonSecs
probability
auctionExtensionSecs
}
}
updateFrequencySecs
}
liquidityMonitoringParameters {
triggeringRatio
targetStakeParameters {
timeWindow
scalingFactor
}
}
tradingMode
state
proposal {
id
}
state
accounts {
asset {
id
name
}
balance
type
}
data {
markPrice
bestBidPrice
bestBidVolume
bestOfferPrice
bestOfferVolume
bestStaticBidPrice
bestStaticBidVolume
bestStaticOfferPrice
bestStaticOfferVolume
midPrice
staticMidPrice
timestamp
openInterest
auctionEnd
auctionStart
indicativePrice
indicativeVolume
trigger
extensionTrigger
targetStake
suppliedStake
priceMonitoringBounds {
minValidPrice
maxValidPrice
trigger {
auctionExtensionSecs
probability
}
referencePrice
}
marketValueProxy
liquidityProviderFeeShare {
party {
id
}
equityLikeShare
averageEntryValuation
}
}
}
}
`;
const Markets = () => {
const { data } = useQuery<MarketsQuery>(MARKETS_QUERY);
return (
<section>
<h1>Markets</h1>
<pre>{JSON.stringify(data, null, " ")}</pre>
</section>
);
};
export default Markets;

View File

@ -0,0 +1,27 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL query operation: NetworkParametersQuery
// ====================================================
export interface NetworkParametersQuery_networkParameters {
__typename: "NetworkParameter";
/**
* The name of the network parameter
*/
key: string;
/**
* The value of the network parameter
*/
value: string;
}
export interface NetworkParametersQuery {
/**
* return the full list of network parameters
*/
networkParameters: NetworkParametersQuery_networkParameters[] | null;
}

View File

@ -0,0 +1,23 @@
import { gql, useQuery } from "@apollo/client";
import { NetworkParametersQuery } from "./__generated__/NetworkParametersQuery";
export const NETWORK_PARAMETERS_QUERY = gql`
query NetworkParametersQuery {
networkParameters {
key
value
}
}
`;
const NetworkParameters = () => {
const { data } = useQuery<NetworkParametersQuery>(NETWORK_PARAMETERS_QUERY);
return (
<section>
<h1>NetworkParameters</h1>
<pre>{JSON.stringify(data, null, " ")}</pre>
</section>
);
};
export default NetworkParameters;

View File

@ -0,0 +1,15 @@
import React from "react";
const Parties = () => {
return (
<section>
<h1>Parties</h1>
<h2>
Not sure what to do with this page? Could show all parties but would
eventually need to be rewritten. But that's not very useful either
</h2>
</section>
);
};
export { Parties };

View File

@ -0,0 +1,125 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { AccountType } from "./../../../../__generated__/globalTypes";
// ====================================================
// GraphQL query operation: PartyAssetsQuery
// ====================================================
export interface PartyAssetsQuery_party_delegations_node {
__typename: "Node";
/**
* The node url eg n01.vega.xyz
*/
id: string;
name: string;
}
export interface PartyAssetsQuery_party_delegations {
__typename: "Delegation";
/**
* Amount delegated
*/
amount: string;
/**
* URL of node you are delegating to
*/
node: PartyAssetsQuery_party_delegations_node;
/**
* Epoch of delegation
*/
epoch: number;
}
export interface PartyAssetsQuery_party_stake {
__typename: "PartyStake";
/**
* The stake currently available for the party
*/
currentStakeAvailable: string;
}
export interface PartyAssetsQuery_party_accounts_asset_source_BuiltinAsset {
__typename: "BuiltinAsset";
}
export interface PartyAssetsQuery_party_accounts_asset_source_ERC20 {
__typename: "ERC20";
/**
* The address of the erc20 contract
*/
contractAddress: string;
}
export type PartyAssetsQuery_party_accounts_asset_source = PartyAssetsQuery_party_accounts_asset_source_BuiltinAsset | PartyAssetsQuery_party_accounts_asset_source_ERC20;
export interface PartyAssetsQuery_party_accounts_asset {
__typename: "Asset";
/**
* The full name of the asset (e.g: Great British Pound)
*/
name: string;
/**
* The id of the asset
*/
id: string;
/**
* The precision of the asset
*/
decimals: number;
/**
* The symbol of the asset (e.g: GBP)
*/
symbol: string;
/**
* The origin source of the asset (e.g: an erc20 asset)
*/
source: PartyAssetsQuery_party_accounts_asset_source;
}
export interface PartyAssetsQuery_party_accounts {
__typename: "Account";
/**
* Asset, the 'currency'
*/
asset: PartyAssetsQuery_party_accounts_asset;
/**
* Account type (General, Margin, etc)
*/
type: AccountType;
/**
* Balance as string - current account balance (approx. as balances can be updated several times per second)
*/
balance: string;
}
export interface PartyAssetsQuery_party {
__typename: "Party";
/**
* Party identifier
*/
id: string;
delegations: PartyAssetsQuery_party_delegations[] | null;
/**
* The staking informations for this Party
*/
stake: PartyAssetsQuery_party_stake;
/**
* Collateral accounts relating to a party
*/
accounts: PartyAssetsQuery_party_accounts[] | null;
}
export interface PartyAssetsQuery {
/**
* An entity that is trading on the VEGA network
*/
party: PartyAssetsQuery_party | null;
}
export interface PartyAssetsQueryVariables {
partyId: string;
}

View File

@ -0,0 +1,74 @@
import { useQuery } from "@apollo/client";
import { gql } from "@apollo/client";
import React from "react";
import { useParams } from "react-router-dom";
import { DATA_SOURCES } from "../../../config";
import useFetch from "../../../hooks/use-fetch";
import { TendermintSearchTransactionResponse } from "../tendermint-transaction-response";
import {
PartyAssetsQuery,
PartyAssetsQueryVariables,
} from "./__generated__/PartyAssetsQuery";
const PARTY_ASSETS_QUERY = gql`
query PartyAssetsQuery($partyId: ID!) {
party(id: $partyId) {
id
delegations {
amount
node {
id
name
}
epoch
}
stake {
currentStakeAvailable
}
accounts {
asset {
name
id
decimals
symbol
source {
__typename
... on ERC20 {
contractAddress
}
}
}
type
balance
}
}
}
`;
const Party = () => {
const { party } = useParams<{ party: string }>();
const { data: partyData } = useFetch<TendermintSearchTransactionResponse>(
`${DATA_SOURCES.tendermintWebsocketUrl}/tx_search?query="tx.submitter=%27${party}%27"`
);
const { data } = useQuery<PartyAssetsQuery, PartyAssetsQueryVariables>(
PARTY_ASSETS_QUERY,
{
// Don't cache data for this query, party information can move quite quickly
fetchPolicy: "network-only",
variables: { partyId: party.replace("0x", "") },
}
);
return (
<section>
<h1>Party</h1>
<h2>Tendermint Data</h2>
<pre>{JSON.stringify(partyData, null, " ")}</pre>
<h2>Asset data</h2>
<pre>{JSON.stringify(data, null, " ")}</pre>
</section>
);
};
export { Party };

View File

@ -0,0 +1,21 @@
import React from "react";
import { Route, Switch, useRouteMatch } from "react-router-dom";
import { Parties } from "./home";
import { Party } from "./id";
const PartiesPage = () => {
const match = useRouteMatch();
return (
<Switch>
<Route path={match.path} exact={true}>
<Parties />
</Route>
<Route path={`${match.path}/:party`}>
<Party />
</Route>
</Switch>
);
};
export default PartiesPage;

View File

@ -0,0 +1,40 @@
export interface Attribute {
key: string;
value: string;
index: boolean;
}
export interface Event {
type: string;
attributes: Attribute[];
}
export interface TxResult {
code: number;
data?: any;
log: string;
info: string;
gas_wanted: string;
gas_used: string;
events: Event[];
codespace: string;
}
export interface Tx {
hash: string;
height: string;
index: number;
tx_result: TxResult;
tx: string;
}
export interface Result {
txs: Tx[];
total_count: string;
}
export interface TendermintSearchTransactionResponse {
jsonrpc: string;
id: number;
result: Result;
}

View File

@ -0,0 +1,79 @@
import Assets from "./assets";
import Blocks from "./blocks";
import Governance from "./governance";
import Home from "./home";
import Markets from "./markets";
import Party from "./parties";
import Txs from "./txs";
import Validators from "./validators";
import Genesis from "./genesis";
import NetworkParameters from "./network-parameters";
export const Routes = {
HOME: "/",
TX: "/txs",
BLOCKS: "/blocks",
PARTIES: "/parties",
VALIDATORS: "/validators",
ASSETS: "/assets",
GENESIS: "/genesis",
GOVERNANCE: "/governance",
MARKETS: "/markets",
NETWORK_PARAMETERS: "/network-parameters",
};
const routerConfig = [
{
path: Routes.HOME,
name: "Home",
component: Home,
exact: true,
},
{
path: Routes.TX,
name: "Txs",
component: Txs,
},
{
path: Routes.BLOCKS,
name: "Blocks",
component: Blocks,
},
{
path: Routes.PARTIES,
name: "Parties",
component: Party,
},
{
path: Routes.ASSETS,
name: "Assets",
component: Assets,
},
{
path: Routes.GENESIS,
name: "Genesis",
component: Genesis,
},
{
path: Routes.GOVERNANCE,
name: "Governance",
component: Governance,
},
{
path: Routes.MARKETS,
name: "Markets",
component: Markets,
},
{
path: Routes.NETWORK_PARAMETERS,
name: "NetworkParameters",
component: NetworkParameters,
},
{
path: Routes.VALIDATORS,
name: "Validators",
component: Validators,
},
];
export default routerConfig;

View File

@ -0,0 +1,28 @@
import React from "react";
import { DATA_SOURCES } from "../../../config";
import useFetch from "../../../hooks/use-fetch";
import { TendermintUnconfirmedTransactionsResponse } from "../tendermint-unconfirmed-transactions-response.d";
const Txs = () => {
const { data: unconfirmedTransactions } =
useFetch<TendermintUnconfirmedTransactionsResponse>(
`${DATA_SOURCES.tendermintUrl}/unconfirmed_txs`
);
return (
<section>
<h1>Tx</h1>
<h2>Unconfirmed transactions</h2>
https://lb.testnet.vega.xyz/tm/unconfirmed_txs
<br />
<div>Number: {unconfirmedTransactions?.result?.n_txs || 0}</div>
<br />
<div>
<br />
{JSON.stringify(unconfirmedTransactions, null, " ")}
</div>
</section>
);
};
export { Txs };

View File

@ -0,0 +1,35 @@
import React from "react";
import { useParams } from "react-router-dom";
import { DATA_SOURCES } from "../../../config";
import useFetch from "../../../hooks/use-fetch";
import { ChainExplorerTxResponse } from "../../types/chain-explorer-response";
import { TendermintTransactionResponse } from "../tendermint-transaction-response.d";
const Tx = () => {
const { txHash } = useParams<{ txHash: string }>();
const { data: transactionData } = useFetch<TendermintTransactionResponse>(
`${DATA_SOURCES.tendermintUrl}/tx?hash=${txHash}`
);
const { data: decodedData } = useFetch<ChainExplorerTxResponse>(
DATA_SOURCES.chainExplorerUrl,
{
method: "POST",
body: JSON.stringify({
tx_hash: txHash,
node_url: `${DATA_SOURCES.tendermintUrl}/`,
}),
}
);
return (
<section>
<h1>Tx</h1>
<h2>Tendermint Data</h2>
<pre>{JSON.stringify(transactionData, null, " ")}</pre>
<h2>Decoded data</h2>
<pre>{JSON.stringify(decodedData, null, " ")}</pre>
</section>
);
};
export { Tx };

View File

@ -0,0 +1,21 @@
import React from "react";
import { Route, Switch, useRouteMatch } from "react-router-dom";
import { Txs } from "./home";
import { Tx } from "./id";
const TxPage = () => {
const match = useRouteMatch();
console.log(match);
return (
<Switch>
<Route path={match.path} exact={true}>
<Txs />
</Route>
<Route path={`${match.path}/:txHash`}>
<Tx />
</Route>
</Switch>
);
};
export default TxPage;

View File

@ -0,0 +1,35 @@
export interface Attribute {
key: string;
value: string;
index: boolean;
}
export interface Event {
type: string;
attributes: Attribute[];
}
export interface TxResult {
code: number;
data?: any;
log: string;
info: string;
gas_wanted: string;
gas_used: string;
events: Event[];
codespace: string;
}
export interface Result {
hash: string;
height: string;
index: number;
tx_result: TxResult;
tx: string;
}
export interface TendermintTransactionResponse {
jsonrpc: string;
id: number;
result: Result;
}

View File

@ -0,0 +1,12 @@
export interface Result {
n_txs: string;
total: string;
total_bytes: string;
txs: string[];
}
export interface TendermintUnconfirmedTransactionsResponse {
jsonrpc: string;
id: number;
result: Result;
}

View File

@ -0,0 +1,8 @@
export interface ChainExplorerTxResponse {
Type: string;
Command: string;
Sig: string;
PubKey: string;
Nonce: number;
TxHash: string;
}

View File

@ -0,0 +1,5 @@
export interface TendermintErrorResponse {
id: number;
jsonrpc: string;
error: string;
}

View File

@ -0,0 +1,83 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { NodeStatus } from "./../../../__generated__/globalTypes";
// ====================================================
// GraphQL query operation: NodesQuery
// ====================================================
export interface NodesQuery_nodes_epochData {
__typename: "EpochData";
/**
* Total number of epochs since node was created
*/
total: number;
/**
* Total number of offline epochs since node was created
*/
offline: number;
/**
* Total number of online epochs since node was created
*/
online: number;
}
export interface NodesQuery_nodes {
__typename: "Node";
/**
* The node url eg n01.vega.xyz
*/
id: string;
name: string;
/**
* URL where I can find out more info on the node. Will this be possible?
*/
infoUrl: string;
avatarUrl: string | null;
/**
* Pubkey of the node operator
*/
pubkey: string;
/**
* Public key of Tendermint
*/
tmPubkey: string;
/**
* Ethereum public key of the node
*/
ethereumAdddress: string;
/**
* Country code for the location of the node
*/
location: string;
/**
* The amount the node has put up themselves
*/
stakedByOperator: string;
/**
* The amount of stake that has been delegated by token holders
*/
stakedByDelegates: string;
/**
* Total amount staked on node
*/
stakedTotal: string;
/**
* Amount of stake on the next epoch
*/
pendingStake: string;
epochData: NodesQuery_nodes_epochData | null;
status: NodeStatus;
score: string;
normalisedScore: string;
}
export interface NodesQuery {
/**
* all known network nodes
*/
nodes: NodesQuery_nodes[] | null;
}

View File

@ -0,0 +1,53 @@
import { gql, useQuery } from "@apollo/client";
import React from "react";
import { DATA_SOURCES } from "../../config";
import useFetch from "../../hooks/use-fetch";
import { TendermintValidatorsResponse } from "./tendermint-validator-response";
import { NodesQuery } from "./__generated__/NodesQuery";
const NODES_QUERY = gql`
query NodesQuery {
nodes {
id
name
infoUrl
avatarUrl
pubkey
tmPubkey
ethereumAdddress
location
stakedByOperator
stakedByDelegates
stakedTotal
pendingStake
epochData {
total
offline
online
}
status
score
normalisedScore
name
}
}
`;
const Validators = () => {
const { data: validators } = useFetch<TendermintValidatorsResponse>(
`${DATA_SOURCES.tendermintUrl}/validators`
);
const { data } = useQuery<NodesQuery>(NODES_QUERY);
return (
<section>
<h1>Validators</h1>
<h2>Tendermint data</h2>
<pre>{JSON.stringify(validators, null, " ")}</pre>
<h2>Vega data</h2>
<pre>{JSON.stringify(data, null, " ")}</pre>
</section>
);
};
export default Validators;

View File

@ -0,0 +1,24 @@
export interface PubKey {
type: string;
value: string;
}
export interface Validator {
address: string;
pub_key: PubKey;
voting_power: string;
proposer_priority: string;
}
export interface Result {
block_height: string;
validators: Validator[];
count: string;
total: string;
}
export interface TendermintValidatorsResponse {
jsonrpc: string;
id: number;
result: Result;
}

View File

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

Binary file not shown.

View File

@ -0,0 +1,99 @@
/* === BLUEPRINT COLOR OVERRIDES === */
$black: #000;
$white: #fff;
$dark-gray1: #1f1f1f;
$dark-gray2: #2a2a2a;
$dark-gray3: #363636;
$dark-gray4: #3f3f3f;
$dark-gray5: #494949;
$gray1: #6e6e6e;
$gray2: #848484;
$gray3: #999;
$gray4: #b5b5b5;
$gray5: #cbcbcb;
$light-gray1: #d7d7d7;
$light-gray2: #e0e0e0;
$light-gray3: #e7e7e7;
$light-gray4: #f0f0f0;
$light-gray5: #f8f8f8;
/* === VEGA COLORS === */
/*
Note: We follow blueprints color naming scheme. https://blueprintjs.com/docs/#core/colors EG:
$color1 = Darkest
$color2
$color3 = Base color
$color4
$color5 = Lightest
*/
$vega-pink: #ff2d5e;
$vega-green: #00f780;
$vega-green3: #26ff8a;
$vega-red3: #ff261a;
$vega-blue3: #48aff0;
$vega-yellow3: #daff0d;
$vega-orange3: #ff7a1a;
$vega-yellow4: #edff22;
$vega-red1: darken($vega-red3, 38%);
$vega-green1: darken($vega-green3, 38%);
$vega-yellow1: darken($vega-yellow3, 38%);
$vega-orange1: darken($vega-orange3, 38%);
/* === TEXT COLORS === */
$text-color: #c7c7c7;
$text-color-inverse: #1a1821;
$text-color-deemphasise: #8a9ba8;
$text-color-emphasise: #f5f8fa;
$text-color-error: $vega-red3;
/* === BUY/SELL BUTTONS === */
$button-sell-hover: #893939;
$button-sell-active: #ff5e5e;
$button-buy-hover: #0a4023;
$button-buy-active: #00ffb2;
/* === MISC BLUEPRINT COLOR OVERRIDES === */
$pt-intent-danger: $vega-red3;
$input-background: #3f3f3f;
// App background
$pt-dark-app-background-color: $dark-gray2;
// Card
$dark-card-background-color: $dark-gray2;
// Menu
$dark-menu-background-color: $dark-gray2;
// Navbar
$dark-navbar-background-color: $black;
// Popover
$dark-popover-background-color: $dark-gray2;
//overlay-backdrop
.bp3-overlay-backdrop {
background-color: rgba(73, 73, 73, 0.7);
}
// Text helpers
.text-deemphasise {
color: $text-color-deemphasise;
}
.text-error {
color: $text-color-error;
}
// hover row
$row-hover-background-color: $dark-gray5;
// backdrop
$backdrop-black: rgba(0, 0, 0, 0.6);

View File

@ -0,0 +1,17 @@
$font-main: "Helvetica neue", "Helvetica", arial, sans-serif;
$font-mono: "Roboto Mono", monospace;
$font-alpa-lyrae: AlphaLyrae, "Helvetica neue", "Helvetica", arial,
sans-serif;
.font-main {
font-family: $font-main;
}
.font-mono {
font-family: $font-mono;
}
@font-face {
font-family: AlphaLyrae;
src: url(./AlphaLyrae-Medium.woff);
}

View File

@ -0,0 +1,6 @@
fieldset {
border: 0;
padding: 0;
margin: 0;
min-width: 0;
}

View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}

10761
apps/block-explorer/yarn.lock Normal file

File diff suppressed because it is too large Load Diff