diff --git a/apps/liquidity-provision-dashboard/src/app/app.tsx b/apps/liquidity-provision-dashboard/src/app/app.tsx
index 757b88c0f..357d05186 100644
--- a/apps/liquidity-provision-dashboard/src/app/app.tsx
+++ b/apps/liquidity-provision-dashboard/src/app/app.tsx
@@ -1,3 +1,5 @@
+import type { InMemoryCacheConfig } from '@apollo/client';
+import { NetworkLoader, useInitializeEnv } from '@vegaprotocol/environment';
import { useRoutes } from 'react-router-dom';
import '../styles.scss';
@@ -5,14 +7,40 @@ import { Navbar } from './components/navbar';
import { routerConfig } from './routes/router-config';
+const cache: InMemoryCacheConfig = {
+ typePolicies: {
+ Market: {
+ merge: true,
+ },
+ Party: {
+ merge: true,
+ },
+ Query: {},
+ Account: {
+ keyFields: false,
+ fields: {
+ balanceFormatted: {},
+ },
+ },
+ Node: {
+ keyFields: false,
+ },
+ Instrument: {
+ keyFields: false,
+ },
+ },
+};
const AppRouter = () => useRoutes(routerConfig);
export function App() {
+ useInitializeEnv();
return (
-
+
+
+
);
}
diff --git a/apps/liquidity-provision-dashboard/src/main.tsx b/apps/liquidity-provision-dashboard/src/main.tsx
index addcc9921..7615e405e 100644
--- a/apps/liquidity-provision-dashboard/src/main.tsx
+++ b/apps/liquidity-provision-dashboard/src/main.tsx
@@ -1,44 +1,15 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
-import { EnvironmentProvider, NetworkLoader } from '@vegaprotocol/environment';
import App from './app/app';
-import type { InMemoryCacheConfig } from '@apollo/client';
const rootElement = document.getElementById('root');
const root = rootElement && createRoot(rootElement);
-const cache: InMemoryCacheConfig = {
- typePolicies: {
- Market: {
- merge: true,
- },
- Party: {
- merge: true,
- },
- Query: {},
- Account: {
- keyFields: false,
- fields: {
- balanceFormatted: {},
- },
- },
- Node: {
- keyFields: false,
- },
- Instrument: {
- keyFields: false,
- },
- },
-};
root?.render(
-
-
-
-
-
+
);
diff --git a/apps/multisig-signer/.env b/apps/multisig-signer/.env
index 02483fefb..a8ba60771 100644
--- a/apps/multisig-signer/.env
+++ b/apps/multisig-signer/.env
@@ -2,3 +2,6 @@ NX_VEGA_URL=https://api.stagnet3.vega.xyz/graphql
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
NX_VEGA_ENV=STAGNET3
+NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
+NX_ETHERSCAN_URL=https://sepolia.etherscan.io
+
diff --git a/apps/multisig-signer/.env.devnet b/apps/multisig-signer/.env.devnet
index 4155154bf..9e8692993 100644
--- a/apps/multisig-signer/.env.devnet
+++ b/apps/multisig-signer/.env.devnet
@@ -3,3 +3,7 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet-network.json
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
NX_VEGA_ENV=DEVNET
+NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
+NX_ETHERSCAN_URL=https://sepolia.etherscan.io
+
+
diff --git a/apps/multisig-signer/.env.mainnet b/apps/multisig-signer/.env.mainnet
index 59c595f17..551b12bdc 100644
--- a/apps/multisig-signer/.env.mainnet
+++ b/apps/multisig-signer/.env.mainnet
@@ -3,3 +3,5 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mainnet-network.json
NX_VEGA_URL=https://api.vega.xyz/query
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
NX_VEGA_ENV=MAINNET
+NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
+NX_ETHERSCAN_URL=https://etherscan.io
\ No newline at end of file
diff --git a/apps/multisig-signer/.env.sandbox b/apps/multisig-signer/.env.sandbox
index baa7a9415..58dfe9405 100644
--- a/apps/multisig-signer/.env.sandbox
+++ b/apps/multisig-signer/.env.sandbox
@@ -3,3 +3,5 @@ NX_VEGA_URL=https://api.sandbox.vega.xyz/graphql
NX_VEGA_ENV=SANDBOX
NX_VEGA_NETWORKS={\"DEVNET\":\"https://dev.token.vega.xyz\",\"STAGNET3\":\"https://stagnet3.token.vega.xyz\",\"STAGNET1\":\"https://stagnet1.token.vega.xyz\",\"TESTNET\":\"https://token.fairground.wtf\",\"MAINNET\":\"https://token.vega.xyz\"}
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/sandbox-network.json
+NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
+NX_ETHERSCAN_URL=https://sepolia.etherscan.io
diff --git a/apps/multisig-signer/.env.stagnet3 b/apps/multisig-signer/.env.stagnet3
index 93045d219..593e9c1bc 100644
--- a/apps/multisig-signer/.env.stagnet3
+++ b/apps/multisig-signer/.env.stagnet3
@@ -3,3 +3,5 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
NX_VEGA_URL=https://api.stagnet3.vega.xyz/graphql
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
NX_VEGA_ENV=STAGNET3
+NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
+NX_ETHERSCAN_URL=https://sepolia.etherscan.io
\ No newline at end of file
diff --git a/apps/multisig-signer/.env.testnet b/apps/multisig-signer/.env.testnet
index ae63ae5af..1720077f4 100644
--- a/apps/multisig-signer/.env.testnet
+++ b/apps/multisig-signer/.env.testnet
@@ -3,3 +3,5 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json
NX_VEGA_URL=https://api.n09.testnet.vega.xyz/graphql
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
NX_VEGA_ENV=TESTNET
+NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
+NX_ETHERSCAN_URL=https://sepolia.etherscan.io
\ No newline at end of file
diff --git a/apps/multisig-signer/src/app/app.tsx b/apps/multisig-signer/src/app/app.tsx
index f20c6abbe..4707f6f50 100644
--- a/apps/multisig-signer/src/app/app.tsx
+++ b/apps/multisig-signer/src/app/app.tsx
@@ -3,9 +3,9 @@ import classnames from 'classnames';
import { useEffect, useMemo, useState } from 'react';
import { BrowserTracing } from '@sentry/tracing';
import {
- EnvironmentProvider,
NetworkLoader,
useEnvironment,
+ useInitializeEnv,
} from '@vegaprotocol/environment';
import { AsyncRenderer, Button, Lozenge } from '@vegaprotocol/ui-toolkit';
import type { EthereumConfig } from '@vegaprotocol/web3';
@@ -64,6 +64,7 @@ function App() {
environment: VEGA_ENV,
});
}, [VEGA_ENV]);
+
const Connectors = useMemo(() => {
if (config?.chain_id) {
return createConnectors(ETHEREUM_PROVIDER_URL, Number(config.chain_id));
@@ -107,12 +108,11 @@ const Wrapper = () => {
},
},
};
+ useInitializeEnv();
return (
-
-
-
-
-
+
+
+
);
};
diff --git a/libs/environment/src/hooks/use-environment.ts b/libs/environment/src/hooks/use-environment.ts
index 8a56e0cfd..418466f81 100644
--- a/libs/environment/src/hooks/use-environment.ts
+++ b/libs/environment/src/hooks/use-environment.ts
@@ -44,6 +44,7 @@ export const useEnvironment = create((set, get) => ({
initialize: async () => {
set({ status: 'pending' });
+ // validate env vars
try {
const rawVars = compileEnvVars();
const safeVars = envSchema.parse(rawVars);
@@ -70,9 +71,7 @@ export const useEnvironment = create((set, get) => ({
console.warn(`Could not fetch node config from ${state.VEGA_CONFIG_URL}`);
}
- // user has previously loaded the app and found
- // a successful node, or chosen one manually - reconnect
- // to same node
+ // Node url found in localStorage, if its valid attempt to connect
if (storedUrl) {
if (isValidUrl(storedUrl)) {
set({ VEGA_URL: storedUrl, status: 'success' });
@@ -82,12 +81,14 @@ export const useEnvironment = create((set, get) => ({
}
}
- // VEGA_URL env var is set no need to find suitable node
+ // VEGA_URL env var is set and is a valid url no need to proceed
if (state.VEGA_URL) {
set({ status: 'success' });
return;
}
+ // No url found in env vars or localStorage, AND no nodes were found in
+ // the config fetched from VEGA_CONFIG_URL, app initialization has failed
if (!nodes || !nodes.length) {
set({
status: 'failed',
@@ -96,7 +97,7 @@ export const useEnvironment = create((set, get) => ({
return;
}
- // create client and store instances
+ // Create a map of node urls to client instances
const clients: ClientCollection = {};
nodes.forEach((url) => {
clients[url] = createClient({
@@ -107,7 +108,8 @@ export const useEnvironment = create((set, get) => ({
});
});
- // find a suitable node to connected, first one to respond is chosen
+ // Find a suitable node to connect to by attempting a query and a
+ // subscription, first to fulfill both will be the resulting url.
const url = await findNode(clients);
if (url !== null) {
@@ -116,7 +118,10 @@ export const useEnvironment = create((set, get) => ({
VEGA_URL: url,
});
LocalStorage.setItem(STORAGE_KEY, url);
- } else {
+ }
+ // Every node failed either to make a query or retrieve data from
+ // a subscription
+ else {
set({
status: 'failed',
error: t('No node found'),
@@ -126,8 +131,13 @@ export const useEnvironment = create((set, get) => ({
},
}));
-// Use this to fetch node config and find suitable node
-// if no NX_VEGA_URL is not provided
+/**
+ * Initialize Vega app to dynamically select a node from the
+ * VEGA_CONFIG_URL
+ *
+ * This can be ommitted if you intend to only use a single node,
+ * in those cases be sure to set NX_VEGA_URL
+ */
export const useInitializeEnv = () => {
const { initialize, status } = useEnvironment((store) => ({
status: store.status,
@@ -141,6 +151,9 @@ export const useInitializeEnv = () => {
}, [status, initialize]);
};
+/**
+ * Fetch and validate a vega node configuration
+ */
const fetchConfig = async (url?: string) => {
if (!url) return [];
const res = await fetch(url);
@@ -149,11 +162,18 @@ const fetchConfig = async (url?: string) => {
return result.hosts;
};
+/**
+ * Find a suitable node by running a test query and test
+ * subscription, against a list of clients, first to resolve wins
+ */
const findNode = (clients: ClientCollection): Promise => {
const tests = Object.entries(clients).map((args) => testNode(...args));
return Promise.race(tests);
};
+/**
+ * Test a node for suitability for connection
+ */
const testNode = async (
url: string,
client: Client
@@ -173,6 +193,9 @@ const testNode = async (
}
};
+/**
+ * Run a test query on a client
+ */
const testQuery = async (client: Client) => {
try {
const result = await client.query({
@@ -187,6 +210,11 @@ const testQuery = async (client: Client) => {
}
};
+/**
+ * Run a test subscription on a client. A subscription
+ * that takes longer than SUBSCRIPTION_TIMEOUT ms to respond
+ * is deemed a failure
+ */
const testSubscription = (client: Client) => {
return new Promise((resolve) => {
const sub = client
@@ -212,6 +240,10 @@ const testSubscription = (client: Client) => {
});
};
+/**
+ * Retrieve env vars, parsing where needed some type casting is needed
+ * here to appease the environment store interface
+ */
function compileEnvVars() {
const env = {
VEGA_URL: process.env['NX_VEGA_URL'],